From db71f70a4c08d12b795b80f923d7f444e715372e Mon Sep 17 00:00:00 2001 From: prostgles Date: Thu, 12 Dec 2024 11:52:00 +0200 Subject: [PATCH] add documentation --- .npmignore | 3 +- .vscode/settings.json | 11 +- documentation/CLIENT.md | 542 ++ documentation/SERVER.md | 104 + documentation/utils/clientTypes.ts | 8436 +++++++++++++++++ documentation/utils/generateClientDocs.ts | 142 + documentation/utils/generateDocs.ts | 4 + documentation/utils/generateServerDocs.ts | 82 + documentation/utils/getResolvedTypes.ts | 45 + documentation/utils/getSerializableType.ts | 479 + documentation/utils/loadTsFile.ts | 45 + documentation/utils/moduleResolver.ts | 31 + documentation/utils/package-lock.json | 75 + documentation/utils/package.json | 14 + documentation/utils/serverTypes.ts | 345 + documentation/utils/tsconfig.json | 28 + .../full-example-typescript/DBoGenerated.d.ts | 4 +- .../full-example-typescript/DBoGenerated.ts | 4 +- examples/full-example-typescript/index.ts | 84 +- examples/full-example-vanilla/index.js | 104 +- examples/server/typescript/index.d.ts | 2 - examples/server/typescript/index.d.ts.map | 1 - examples/server/typescript/index.js | 54 - examples/server/typescript/index.js.map | 1 - examples/server/typescript/index.ts | 54 +- lib/DboBuilder/TableHandler/DataValidator.ts | 10 +- lib/DboBuilder/TableHandler/TableHandler.ts | 2 +- lib/DboBuilder/TableHandler/insert.ts | 6 +- lib/DboBuilder/TableHandler/update.ts | 154 +- lib/DboBuilder/ViewHandler/ViewHandler.ts | 4 +- lib/DboBuilder/parseUpdateRules.ts | 2 +- lib/Prostgles.ts | 352 +- lib/ProstglesTypes.ts | 181 +- lib/SyncReplication.ts | 4 +- lib/initProstgles.ts | 256 +- lib/utils.ts | 9 +- package-lock.json | 41 +- package.json | 5 +- tests/client/package-lock.json | 16 +- tests/client/package.json | 2 +- ...oGenerated.d.ts => DBSchemaGenerated.d.ts} | 0 tests/server/index.ts | 481 +- tests/server/package-lock.json | 8 +- tests/server/server.ts | 24 +- 44 files changed, 11407 insertions(+), 844 deletions(-) create mode 100644 documentation/CLIENT.md create mode 100644 documentation/SERVER.md create mode 100644 documentation/utils/clientTypes.ts create mode 100644 documentation/utils/generateClientDocs.ts create mode 100644 documentation/utils/generateDocs.ts create mode 100644 documentation/utils/generateServerDocs.ts create mode 100644 documentation/utils/getResolvedTypes.ts create mode 100644 documentation/utils/getSerializableType.ts create mode 100644 documentation/utils/loadTsFile.ts create mode 100644 documentation/utils/moduleResolver.ts create mode 100644 documentation/utils/package-lock.json create mode 100644 documentation/utils/package.json create mode 100644 documentation/utils/serverTypes.ts create mode 100644 documentation/utils/tsconfig.json delete mode 100644 examples/server/typescript/index.d.ts delete mode 100644 examples/server/typescript/index.d.ts.map delete mode 100644 examples/server/typescript/index.js delete mode 100644 examples/server/typescript/index.js.map rename tests/server/{DBoGenerated.d.ts => DBSchemaGenerated.d.ts} (100%) diff --git a/.npmignore b/.npmignore index 3598c300..d308756d 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1,2 @@ -tests \ No newline at end of file +tests +documenation \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 3662b370..185f8d79 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,12 @@ { - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "editor.formatOnSave": true, + "prettier.experimentalTernaries": true, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "prettier.printWidth": 100 } \ No newline at end of file diff --git a/documentation/CLIENT.md b/documentation/CLIENT.md new file mode 100644 index 00000000..90ec55fb --- /dev/null +++ b/documentation/CLIENT.md @@ -0,0 +1,542 @@ +## getInfo() +Retrieves the table/view info +```typescript + getInfo: (lang?: string): Promise + ``` +#### Arguments + + - **lang**: `string` - Language code for i18n data. ```typescript. "en". ```. +#### Return type +`TableInfo` + - **oid**: `number` - OID from the postgres database. Useful in handling renamed tables. + - **comment**: `string` - Comment from the postgres database. + - **isFileTable**: `{ allowedNestedInserts?: { table: string; column: string; }[] | undefined; }` - Defined if this is the fileTable. + - **allowedNestedInserts**: `{ table: string; column: string; }` + - **table**: `string` + - **column**: `string` + - **hasFiles**: `false` - True if fileTable is enabled and this table references the fileTable. + - **isView**: `false` + - **fileTableName**: `string` - Name of the fileTable (if enabled). + - **dynamicRules**: `{ update?: boolean | undefined; }` - See dynamicFields from Update rules. + - **update**: `false` + - **info**: `{ label?: string | undefined; }` - Additional table info provided through TableConfig. + - **label**: `string` + - **uniqueColumnGroups**: `string[][] | undefined` - + +## getColumns() +Retrieves columns metadata of the table/view +```typescript + getColumns: (lang?: string, params?: GetColumnsParams): Promise + ``` +#### Arguments + + - **lang**: `string` + - **params**: `GetColumnsParams` - Dynamic/filter based rules allow limit what columns can be updated based on the request data/filter. This allows parameter allows identifying the columns that can be updated based on the request data. + - **rule**: `"update"` + - **data**: `AnyObject` + + - **filter**: `AnyObject` + +#### Return type +`ValidatedColumnInfo` + - **name**: `string` + - **label**: `string` - Column display name. Will be first non empty value from i18n data, comment, name. + - **comment**: `string` - Column description (if provided). + - **ordinal_position**: `number` - Ordinal position of the column within the table (count starts at 1). + - **is_nullable**: `boolean` - + - **is_updatable**: `boolean` + - **is_generated**: `boolean` - If the column is a generated column (converted to boolean from ALWAYS and NEVER). + - **data_type**: `string` - Simplified data type. + - **udt_name**: `PG_COLUMN_UDT_DATA_TYPE` - Postgres raw data types. values starting with underscore means it's an array of that data type. + - **element_type**: `string` - Element data type. + - **element_udt_name**: `string` - Element raw data type. + - **is_pkey**: `boolean` - PRIMARY KEY constraint on column. A table can have more then one PK. + - **references**: `{ ftable: string; fcols: string[]; cols: string[]; }` + - **ftable**: `string` + - **fcols**: `string` + - **cols**: `string` + - **has_default**: `boolean` - true if column has a default value. Used for excluding pkey from insert. + - **column_default**: `any` - Column default value. + - **min**: `string | number | undefined` - Extracted from tableConfig. Used in SmartForm. + - **max**: `string | number | undefined` + - **hint**: `string` + - **jsonbSchema**: `JSONBSchema` + - **nullable**: `any` - False by default. + - **description**: `any` + - **title**: `any` + - **type**: `any` + - **allowedValues**: `any` + - **oneOf**: `any` + - **oneOfType**: `any` + - **arrayOf**: `any` + - **arrayOfType**: `any` + - **enum**: `any` + - **record**: `any` + - **lookup**: `any` + - **defaultValue**: `any` + - **file**: `FileColumnConfig | undefined` - If degined then this column is referencing the file table. Extracted from FileTable config. Used in SmartForm. + - **tsDataType**: `"string" | "number" | "boolean" | "any" | "number[]" | "boolean[]" | "string[]" | "any[]"` - TypeScript data type. + - **select**: `boolean` - Can be viewed/selected. + - **orderBy**: `boolean` - Can be ordered by. + - **filter**: `boolean` - Can be filtered by. + - **insert**: `boolean` - Can be inserted. + - **update**: `boolean` - Can be updated. + - **delete**: `boolean` - Can be used in the delete filter. + +## find() +Retrieves a list of matching records from the view/table +```typescript + find: (filter?: FullFilter | undefined, selectParams?: SelectParams): Promise> + ``` +#### Arguments + + - **filter**: `FullFilter | undefined` + - **selectParams**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`GetSelectReturnType` + +## findOne() +Retrieves a record from the view/table +```typescript + findOne: (filter?: FullFilter | undefined, selectParams?: SelectParams): Promise | undefined> + ``` +#### Arguments + + - **filter**: `FullFilter | undefined` + - **selectParams**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`GetSelectReturnType | undefined` + +## subscribe() +Retrieves a list of matching records from the view/table and subscribes to changes +```typescript + subscribe: (filter: FullFilter, params: SelectParams, onData: (items: GetSelectReturnType) => any, onError?: OnError): Promise + ``` +#### Arguments + + - **filter**: `FullFilter` - Group or simple filter. + - **params**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). + - **onData**: `(items: GetSelectReturnType) => any` + - **onError**: `OnError` +#### Return type +`SubscriptionHandler` + - **unsubscribe**: `() => Promise` + - **filter**: `{} | FullFilter` + +## subscribeOne() +Retrieves first matching record from the view/table and subscribes to changes +```typescript + subscribeOne: (filter: FullFilter, params: SelectParams, onData: (item: GetSelectReturnType | undefined) => any, onError?: OnError): Promise + ``` +#### Arguments + + - **filter**: `FullFilter` - Group or simple filter. + - **params**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). + - **onData**: `(item: GetSelectReturnType | undefined) => any` + - **onError**: `OnError` +#### Return type +`SubscriptionHandler` + - **unsubscribe**: `() => Promise` + - **filter**: `{} | FullFilter` + +## count() +Returns the number of rows that match the filter +```typescript + count: (filter?: FullFilter | undefined, selectParams?: SelectParams): Promise + ``` +#### Arguments + + - **filter**: `FullFilter | undefined` + - **selectParams**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`number` + +## size() +Returns result size in bits +```typescript + size: (filter?: FullFilter | undefined, selectParams?: SelectParams): Promise + ``` +#### Arguments + + - **filter**: `FullFilter | undefined` + - **selectParams**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`string` + +## getJoinedTables() + +```typescript + getJoinedTables: (): string[] + ``` +#### Arguments + +#### Return type +`string` + + + + + +## sync() + +```typescript + sync: (basicFilter: EqualityFilter, options: SyncOptions, onChange: (data: SyncDataItem, false>[], delta?: Partial[] | undefined) => any, onError?: (error: any) => void): Promise<{ $unsync: () => void; $upsert: (newData: T[]) => any; getItems: () => T[]; }> + ``` +#### Arguments + + - **basicFilter**: `EqualityFilter` - Equality filter used for sync. Multiple columns are combined with AND. + + - **options**: `SyncOptions` + - **onChange**: `(data: SyncDataItem, false>[], delta?: Partial[] | undefined) => any` + - **onError**: `(error: any) => void` +#### Return type +`{ $unsync: () => void; $upsert: (newData: T[]) => any; getItems: () => T[]; }` + - **$unsync**: `() => void` + - **$upsert**: `(newData: T[]) => any` + - **getItems**: `() => T[]` + +## useSync() +Retrieves rows matching the filter and keeps them in sync +- use { handlesOnData: true } to get optimistic updates method: $update +- any changes to the row using the $update method will be reflected instantly + to all sync subscribers that were initiated with the same syncOptions +```typescript + useSync: (basicFilter: EqualityFilter, syncOptions: SyncOptions): { data: SyncDataItem>[] | undefined; isLoading: boolean; error?: any; } + ``` +#### Arguments + + - **basicFilter**: `EqualityFilter` - Equality filter used for sync. Multiple columns are combined with AND. + + - **syncOptions**: `SyncOptions` +#### Return type +`{ data: SyncDataItem>[] | undefined; isLoading: boolean; error?: any; }` + - **data**: `SyncDataItem>[] | undefined` + - **isLoading**: `boolean` + - **error**: `any` + +## syncOne() + +```typescript + syncOne: (basicFilter: Partial, options: SyncOneOptions, onChange: (data: SyncDataItem, false>, delta?: Partial | undefined) => any, onError?: (error: any) => void): Promise> + ``` +#### Arguments + + - **basicFilter**: `Partial` - Make all properties in T optional. + + - **options**: `SyncOneOptions` + - **onChange**: `(data: SyncDataItem, false>, delta?: Partial | undefined) => any` + - **onError**: `(error: any) => void` +#### Return type +`SingleSyncHandles` - CRUD handles added if initialised with handlesOnData = true. + - **$get**: `() => T | undefined` + - **$find**: `(idObj: Partial) => T | undefined` + - **$unsync**: `() => any` + - **$delete**: `() => void` + - **$update**: `(newData: OPTS extends { deepMerge: true; } ? DeepPartial : Partial, opts?: OPTS | undefined) => any` + - **$cloneSync**: `CloneSync` + - **$cloneMultiSync**: `CloneMultiSync` + +## useSyncOne() +Retrieves the first row matching the filter and keeps it in sync +- use { handlesOnData: true } to get optimistic updates method: $update +- any changes to the row using the $update method will be reflected instantly + to all sync subscribers that were initiated with the same syncOptions +```typescript + useSyncOne: (basicFilter: EqualityFilter, syncOptions: SyncOneOptions): { data: SyncDataItem> | undefined; isLoading: boolean; error?: any; } + ``` +#### Arguments + + - **basicFilter**: `EqualityFilter` - Equality filter used for sync. Multiple columns are combined with AND. + + - **syncOptions**: `SyncOneOptions` +#### Return type +`{ data: SyncDataItem> | undefined; isLoading: boolean; error?: any; }` + - **data**: `SyncDataItem> | undefined` + - **isLoading**: `boolean` + - **error**: `any` + + + +## useSubscribe() +Retrieves a list of matching records from the view/table and subscribes to changes +```typescript + useSubscribe: (filter?: FullFilter | undefined, options?: SubscribeParams): { data: GetSelectReturnType | undefined; error?: any; isLoading: boolean; } + ``` +#### Arguments + + - **filter**: `FullFilter | undefined` + - **options**: `SubscribeParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). + - **throttle**: `number` - If true then the subscription will be throttled to the provided number of milliseconds. + - **throttleOpts**: `{ skipFirst?: boolean | undefined; }` + - **skipFirst**: `false` - If true then the first value will be emitted at the end of the interval. Instant otherwise. +#### Return type +`{ data: GetSelectReturnType | undefined; error?: any; isLoading: boolean; }` + - **data**: `GetSelectReturnType | undefined` + - **error**: `any` + - **isLoading**: `boolean` + +## useSubscribeOne() +Retrieves a matching record from the view/table and subscribes to changes +```typescript + useSubscribeOne: (filter?: FullFilter | undefined, options?: SubscribeParams): { data: GetSelectReturnType | undefined; error?: any; isLoading: boolean; } + ``` +#### Arguments + + - **filter**: `FullFilter | undefined` + - **options**: `SubscribeParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). + - **throttle**: `number` - If true then the subscription will be throttled to the provided number of milliseconds. + - **throttleOpts**: `{ skipFirst?: boolean | undefined; }` + - **skipFirst**: `false` - If true then the first value will be emitted at the end of the interval. Instant otherwise. +#### Return type +`{ data: GetSelectReturnType | undefined; error?: any; isLoading: boolean; }` + - **data**: `GetSelectReturnType | undefined` + - **error**: `any` + - **isLoading**: `boolean` + +## useFind() +Retrieves a list of matching records from the view/table +```typescript + useFind: (filter?: FullFilter | undefined, selectParams?: SelectParams): { data: GetSelectReturnType | undefined; isLoading: boolean; error?: any; } + ``` +#### Arguments + + - **filter**: `FullFilter | undefined` + - **selectParams**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`{ data: GetSelectReturnType | undefined; isLoading: boolean; error?: any; }` + - **data**: `GetSelectReturnType | undefined` + - **isLoading**: `boolean` + - **error**: `any` + +## useFindOne() +Retrieves first matching record from the view/table +```typescript + useFindOne: (filter?: FullFilter | undefined, selectParams?: SelectParams): { data: GetSelectReturnType | undefined; isLoading: boolean; error?: any; } + ``` +#### Arguments + + - **filter**: `FullFilter | undefined` + - **selectParams**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`{ data: GetSelectReturnType | undefined; isLoading: boolean; error?: any; }` + - **data**: `GetSelectReturnType | undefined` + - **isLoading**: `boolean` + - **error**: `any` + +## useCount() +Returns the total number of rows matching the filter +```typescript + useCount: (filter?: FullFilter | undefined, selectParams?: SelectParams): { data: number | undefined; isLoading: boolean; error?: any; } + ``` +#### Arguments + + - **filter**: `FullFilter | undefined` + - **selectParams**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`{ data: number | undefined; isLoading: boolean; error?: any; }` + - **data**: `number | undefined` + - **isLoading**: `boolean` + - **error**: `any` + +## useSize() +Returns result size in bits matching the filter and selectParams +```typescript + useSize: (filter?: FullFilter | undefined, selectParams?: SelectParams): { data: string | undefined; isLoading: boolean; error?: any; } + ``` +#### Arguments + + - **filter**: `FullFilter | undefined` + - **selectParams**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`{ data: string | undefined; isLoading: boolean; error?: any; }` + - **data**: `string | undefined` + - **isLoading**: `boolean` + - **error**: `any` + +## update() +Updates a record in the table based on the specified filter criteria +- Use { multi: false } to ensure no more than one row is updated +```typescript + update: (filter: FullFilter, newData: Partial, params?: SelectParams): Promise | undefined> + ``` +#### Arguments + + - **filter**: `FullFilter` - Group or simple filter. + - **newData**: `Partial` - Make all properties in T optional. + + - **params**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`GetUpdateReturnType | undefined` + +## updateBatch() +Updates multiple records in the table in a batch operation. +- Each item in the `data` array contains a filter and the corresponding data to update. +```typescript + updateBatch: (data: [FullFilter, Partial>][], params?: SelectParams): Promise> + ``` +#### Arguments + + - **data**: `[FullFilter, Partial>][]` + - **params**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`void | GetUpdateReturnType` + +## insert() +Inserts a new record into the table. +```typescript + insert: (data: UpsertDataToPGCast | UpsertDataToPGCast[], params?: SelectParams): Promise> + ``` +#### Arguments + + - **data**: `InsertData` + - **params**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`GetInsertReturnType` + +## upsert() +Inserts or updates a record in the table. +- If a record matching the `filter` exists, it updates the record. +- If no matching record exists, it inserts a new record. +```typescript + upsert: (filter: FullFilter, newData: Partial, params?: SelectParams): Promise | undefined> + ``` +#### Arguments + + - **filter**: `FullFilter` - Group or simple filter. + - **newData**: `Partial` - Make all properties in T optional. + + - **params**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`GetUpdateReturnType | undefined` + +## delete() +Deletes records from the table based on the specified filter criteria. +- If no filter is provided, all records may be deleted (use with caution). +```typescript + delete: (filter?: FullFilter | undefined, params?: SelectParams): Promise | undefined> + ``` +#### Arguments + + - **filter**: `FullFilter | undefined` + - **params**: `SelectParams` + - **limit**: `number | null | undefined` - Max number of rows to return. - If undefined then 1000 will be applied as the default. - On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present). + - **offset**: `number` - Number of rows to skip. + - **groupBy**: `false` - Will group by all non aggregated fields specified in select (or all fields by default). + - **returnType**: `"row" | "value" | "values" | "statement" | "statement-no-rls" | "statement-where" | undefined` - Result data structure/type:. - row: the first row as an object. - value: the first value from of first field. - values: array of values from the selected field. - statement: sql statement. - statement-no-rls: sql statement without row level security. - statement-where: sql statement where condition. + - **select**: `Select` - Fields/expressions/linked data to select. - If empty then all fields will be selected. - If "*" then all fields will be selected. - If { field: 0 } then all fields except the specified field will be selected. - If { field: 1 } then only the specified field will be selected. - If { field: { funcName: [args] } } then the field will be selected with the specified function applied. - If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields. + - **orderBy**: `OrderBy` - Order by options. - If array then the order will be maintained. + - **having**: `FullFilter | undefined` - Filter applied after any aggregations (group by). +#### Return type +`GetUpdateReturnType | undefined` \ No newline at end of file diff --git a/documentation/SERVER.md b/documentation/SERVER.md new file mode 100644 index 00000000..7cf16244 --- /dev/null +++ b/documentation/SERVER.md @@ -0,0 +1,104 @@ +# Overview +Our Isomorphic Typescript API allows connecting to a PostgreSQL database to get a realtime view of the data and schema. Interact with the data with full end-to-end type safety. +### Installation +To install the package, run: +```bash +npm install prostgles-server +``` +### Configuration +To get started, you need to provide a configuration object to the server. + +Basic example: +```typescript +import prostgles from "prostgles-server"; +import { DBSchemaGenerated } from "./DBSchemaGenerated"; +prostgles({ + dbConnection: { + host: "localhost", + port: 5432, + database: "postgres" + user: process.env.PRGL_USER, + password: process.env.PRGL_PWD + }, + tsGeneratedTypesDir: __dirname, + onReady: async ({ dbo }) => { + try { + await dbo.items.insert({ name: "a" }); + console.log(await dbo.items.find()); + } catch(err) { + console.error(err) + } + }, +}); +``` +### Configuration options + - dbConnection `DbConnection` + Database connection details + - onReady `OnReadyCallback` + Called when the prostgles server is ready to accept connections. +It waits for auth, tableConfig and other async configurations to complete before executing + - dbOptions `IDefaults` + - tsGeneratedTypesDir `string | undefined` + If defined then a `DBSchemaGenerated.d.ts` file will be created in the provided directory. +This file exports a `DBSchemaGenerated` type which contains types for the database tables and +can be used as a generic type input for the prostgles instances to ensure type safety + - disableRealtime `boolean | undefined` + If true then schema watch, subscriptions and syncs will be disabled. +No `prostgles` schema will be created which is needed for the realtime features. +This is useful when you want to connect to a database and prevent any changes to the schema + - io `Server` + Socket.IO server instance object + - publish `Publish | undefined` + Data access rules applied to clients. +By default, nothing is allowed. + - testRulesOnConnect `boolean | undefined` + If true then will test all table methods on each socket connect. +Not recommended for production + - publishMethods `PublishMethods` + Custom methods that can be called from the client + - publishRawSQL `(params: PublishParams) => boolean | "*" | Promise` + If defined and resolves to true then the connected client can run SQL queries + - joins `Joins | undefined` + Allows defining joins between tables: + - `infered` - uses the foreign keys to infer the joins + - `Join[]` - specifies the joins manually + - schema `Record | Record | undefined` + If defined then the specified schemas are included/excluded from the prostgles schema. +By default the `public` schema is included. + - sqlFilePath `string | undefined` + Path to a SQL file that will be executed on startup (but before onReady) + - transactions `string | boolean | undefined` + - wsChannelNamePrefix `string | undefined` + - onSocketConnect `(args: AuthRequestParams & { socket: PRGLIOSocket; }) => void | Promise` + Called when a socket connects +Use for connection verification. Will disconnect socket on any errors + - onSocketDisconnect `(args: AuthRequestParams & { socket: PRGLIOSocket; }) => void | Promise` + Called when a socket disconnects + - auth `Auth` + Auth configuration. +Supports email and OAuth strategies + - DEBUG_MODE `boolean | undefined` + - onQuery `(error: any, ctx: IEventContext) => void` + Callback called when a query is executed. +Useful for logging or debugging + - watchSchemaType `"DDL_trigger" | "prostgles_queries" | undefined` + - watchSchema `boolean | EventTriggerTagFilter | "hotReloadMode" | OnSchemaChangeCallback | undefined` + If truthy then DBSchemaGenerated.d.ts will be updated +and "onReady" will be called with new schema on both client and server + - keywords `Keywords` + - onNotice `(notice: AnyObject, message?: string | undefined) => void` + - fileTable `FileTableConfig` + Enables file storage and serving. +Currently supports saving files locally or to AWS S3 + - restApi `RestApiConfig` + Rest API configuration. +The REST API allows interacting with the database similarly to the socket connection +with the exception of subscriptions and realtime features + - tableConfig `TableConfig` + A simple way of defining tables through a JSON-schema like object. +Allowes adding runtime JSONB validation and type safety. +Should be used with caution because it tends to revert any changes +made to the database schema through SQL queries + - tableConfigMigrations `{ silentFail?: boolean | undefined; version: number; versionTableName?: string | undefined; onMigrate: (args: { db: DB; oldVersion: number | undefined; getConstraints: (table: string, column?: string | undefined, types?: ("c" | ... 2 more ... | "f")[] | undefined) => Promise<...>; }) => void; }` + - onLog `(evt: EventInfo) => Promise` + Usefull for logging or debugging \ No newline at end of file diff --git a/documentation/utils/clientTypes.ts b/documentation/utils/clientTypes.ts new file mode 100644 index 00000000..8bf3da06 --- /dev/null +++ b/documentation/utils/clientTypes.ts @@ -0,0 +1,8436 @@ +import type { TS_Type } from "./getSerializableType"; +export const definitions = [ + { + "type": "object", + "alias": "TableHandlerClient", + "aliasSymbolescapedName": "TableHandlerClient", + "properties": { + "getInfo": { + "type": "function", + "alias": "(lang?: string | undefined) => Promise", + "arguments": [ + { + "name": "lang", + "optional": true, + "type": "primitive", + "alias": "string", + "subType": "string", + "comments": "Language code for i18n data\n```typescript\n \"en\"\n```" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "object", + "alias": "TableInfo", + "aliasSymbolescapedName": "TableInfo", + "comments": "", + "properties": { + "oid": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": false, + "comments": "OID from the postgres database\nUseful in handling renamed tables" + }, + "comment": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": true, + "comments": "Comment from the postgres database" + }, + "isFileTable": { + "type": "object", + "alias": "{ allowedNestedInserts?: { table: string; column: string; }[] | undefined; }", + "comments": "Defined if this is the fileTable", + "properties": { + "allowedNestedInserts": { + "type": "array", + "alias": "{ table: string; column: string; }[]", + "itemType": { + "type": "object", + "alias": "{ table: string; column: string; }", + "properties": { + "table": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": false + }, + "column": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": false + } + } + }, + "optional": true, + "comments": "Defined if direct inserts are disabled.\nOnly nested inserts through the specified tables/columns are allowed" + } + }, + "optional": true + }, + "hasFiles": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "True if fileTable is enabled and this table references the fileTable" + }, + "isView": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true + }, + "fileTableName": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": true, + "comments": "Name of the fileTable (if enabled)" + }, + "dynamicRules": { + "type": "object", + "alias": "{ update?: boolean | undefined; }", + "comments": "Used for getColumns in cases where the columns are dynamic based on the request.\nSee dynamicFields from Update rules", + "properties": { + "update": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true + } + }, + "optional": true + }, + "info": { + "type": "object", + "alias": "{ label?: string | undefined; }", + "comments": "Additional table info provided through TableConfig", + "properties": { + "label": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": true + } + }, + "optional": true + }, + "uniqueColumnGroups": { + "type": "union", + "alias": "string[][] | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "array", + "alias": "string[][]", + "itemType": { + "type": "array", + "alias": "string[]", + "itemType": { + "type": "primitive", + "alias": "string", + "subType": "string" + } + } + } + ], + "optional": false, + "comments": "List of unique column indexes/constraints.\nColumn groups where at least a column is not allowed to be viewed (selected) are omitted." + } + } + } + }, + "optional": false, + "comments": "Retrieves the table/view info" + }, + "getColumns": { + "type": "function", + "alias": "GetColumns", + "aliasSymbolescapedName": "GetColumns", + "arguments": [ + { + "name": "lang", + "optional": true, + "type": "primitive", + "alias": "string", + "subType": "string", + "comments": "" + }, + { + "name": "params", + "optional": true, + "type": "object", + "alias": "GetColumnsParams", + "aliasSymbolescapedName": "GetColumnsParams", + "comments": "Dynamic/filter based rules allow limit what columns can be updated based on the request data/filter\nThis allows parameter allows identifying the columns that can be updated based on the request data", + "properties": { + "rule": { + "type": "literal", + "alias": "\"update\"", + "value": "update", + "optional": false + }, + "data": { + "type": "object", + "alias": "AnyObject", + "aliasSymbolescapedName": "AnyObject", + "properties": {}, + "optional": false + }, + "filter": { + "type": "object", + "alias": "AnyObject", + "aliasSymbolescapedName": "AnyObject", + "properties": {}, + "optional": false + } + } + } + ], + "returnType": { + "type": "promise", + "alias": "Promise", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "array", + "alias": "ValidatedColumnInfo[]", + "itemType": { + "type": "object", + "alias": "ValidatedColumnInfo", + "aliasSymbolescapedName": "ValidatedColumnInfo", + "properties": { + "name": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": false + }, + "label": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": false, + "comments": "Column display name. Will be first non empty value from i18n data, comment, name" + }, + "comment": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": false, + "comments": "Column description (if provided)" + }, + "ordinal_position": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": false, + "comments": "Ordinal position of the column within the table (count starts at 1)" + }, + "is_nullable": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false, + "comments": "True if column is nullable. A not-null constraint is one way a column can be known not nullable, but there may be others." + }, + "is_updatable": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false + }, + "is_generated": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false, + "comments": "If the column is a generated column (converted to boolean from ALWAYS and NEVER)" + }, + "data_type": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": false, + "comments": "Simplified data type" + }, + "udt_name": { + "type": "reference", + "alias": "PG_COLUMN_UDT_DATA_TYPE", + "aliasSymbolescapedName": "PG_COLUMN_UDT_DATA_TYPE", + "comments": "Postgres raw data types. values starting with underscore means it's an array of that data type", + "optional": false + }, + "element_type": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": false, + "comments": "Element data type" + }, + "element_udt_name": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": false, + "comments": "Element raw data type" + }, + "is_pkey": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false, + "comments": "PRIMARY KEY constraint on column. A table can have more then one PK" + }, + "references": { + "type": "array", + "alias": "{ ftable: string; fcols: string[]; cols: string[]; }[]", + "itemType": { + "type": "object", + "alias": "{ ftable: string; fcols: string[]; cols: string[]; }", + "properties": { + "ftable": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": false + }, + "fcols": { + "type": "array", + "alias": "string[]", + "itemType": { + "type": "primitive", + "alias": "string", + "subType": "string" + }, + "optional": false + }, + "cols": { + "type": "array", + "alias": "string[]", + "itemType": { + "type": "primitive", + "alias": "string", + "subType": "string" + }, + "optional": false + } + } + }, + "optional": true, + "comments": "Foreign key constraint\nA column can reference multiple tables" + }, + "has_default": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false, + "comments": "true if column has a default value\nUsed for excluding pkey from insert" + }, + "column_default": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true, + "comments": "Column default value" + }, + "min": { + "type": "union", + "alias": "string | number | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "string", + "subType": "string" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Extracted from tableConfig\nUsed in SmartForm" + }, + "max": { + "type": "union", + "alias": "string | number | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "string", + "subType": "string" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true + }, + "hint": { + "type": "primitive", + "alias": "string", + "subType": "string", + "optional": true + }, + "jsonbSchema": { + "type": "object", + "alias": "JSONBSchema", + "aliasSymbolescapedName": "JSONBSchema", + "properties": { + "nullable": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true, + "comments": "False by default" + }, + "description": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "title": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "type": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "allowedValues": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "oneOf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "oneOfType": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "arrayOf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "arrayOfType": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "enum": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "record": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "lookup": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "defaultValue": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + }, + "optional": true + }, + "file": { + "type": "union", + "alias": "FileColumnConfig | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "object", + "alias": "{ acceptedContent: FieldFilter<{ image: 1; audio: 1; video: 1; text: 1; application: 1; }>; } & { maxFileSizeMB?: number | undefined; }", + "properties": { + "acceptedContent": { + "type": "union", + "alias": "FieldFilter<{ image: 1; audio: 1; video: 1; text: 1; application: 1; }>", + "aliasSymbolescapedName": "FieldFilter", + "comments": "List of fields to include or exclude", + "types": [ + { + "type": "literal", + "alias": "\"\"", + "value": "" + }, + { + "type": "literal", + "alias": "\"*\"", + "value": "*" + }, + { + "type": "object", + "alias": "{ \"*\": 1; }", + "properties": { + "*": { + "type": "primitive", + "alias": "1", + "subType": "number", + "optional": false + } + } + }, + { + "type": "object", + "alias": "{ image?: true | 1 | undefined; audio?: true | 1 | undefined; video?: true | 1 | undefined; text?: true | 1 | undefined; application?: true | 1 | undefined; }", + "properties": { + "image": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "audio": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + { + "type": "object", + "alias": "{ image?: false | 0 | undefined; audio?: false | 0 | undefined; video?: false | 0 | undefined; text?: false | 0 | undefined; application?: false | 0 | undefined; }", + "properties": { + "image": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "audio": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + { + "type": "array", + "alias": "(\"text\" | \"image\" | \"audio\" | \"video\" | \"application\")[]", + "itemType": { + "type": "union", + "alias": "\"text\" | \"image\" | \"audio\" | \"video\" | \"application\"", + "types": [ + { + "type": "literal", + "alias": "\"text\"", + "value": "text" + }, + { + "type": "literal", + "alias": "\"image\"", + "value": "image" + }, + { + "type": "literal", + "alias": "\"audio\"", + "value": "audio" + }, + { + "type": "literal", + "alias": "\"video\"", + "value": "video" + }, + { + "type": "literal", + "alias": "\"application\"", + "value": "application" + } + ] + } + } + ], + "optional": false + }, + "maxFileSizeMB": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true + } + } + }, + { + "type": "object", + "alias": "{ acceptedContentType: FieldFilter<{ readonly \"text/html\": readonly [\"html\", \"htm\", \"shtml\"]; readonly \"text/css\": readonly [\"css\"]; readonly \"text/csv\": readonly [\"csv\"]; readonly \"text/tsv\": readonly [\"tsv\"]; readonly \"text/xml\": readonly [\"xml\"]; ... 61 more ...; readonly \"video/webm\": readonly [...]; }>; } & { ....", + "properties": { + "acceptedContentType": { + "type": "union", + "alias": "FieldFilter<{ readonly \"text/html\": readonly [\"html\", \"htm\", \"shtml\"]; readonly \"text/css\": readonly [\"css\"]; readonly \"text/csv\": readonly [\"csv\"]; readonly \"text/tsv\": readonly [\"tsv\"]; readonly \"text/xml\": readonly [\"xml\"]; readonly \"text/mathml\": readonly [\"mml\"]; ... 60 more ...; readonly \"video/webm\": readonly...", + "aliasSymbolescapedName": "FieldFilter", + "comments": "List of fields to include or exclude", + "types": [ + { + "type": "literal", + "alias": "\"\"", + "value": "" + }, + { + "type": "literal", + "alias": "\"*\"", + "value": "*" + }, + { + "type": "object", + "alias": "{ \"*\": 1; }", + "properties": { + "*": { + "type": "primitive", + "alias": "1", + "subType": "number", + "optional": false + } + } + }, + { + "type": "object", + "alias": "{ readonly \"text/html\"?: true | 1 | undefined; readonly \"text/css\"?: true | 1 | undefined; readonly \"text/csv\"?: true | 1 | undefined; readonly \"text/tsv\"?: true | 1 | undefined; readonly \"text/xml\"?: true | ... 1 more ... | undefined; ... 61 more ...; readonly \"video/webm\"?: true | ... 1 more ... | undefined; }", + "properties": { + "text/html": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/css": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/csv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/tsv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/mathml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/plain": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/vnd.sun.j2me.app-descriptor": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/vnd.wap.wml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/x-component": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/gif": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/jpeg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/png": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/tiff": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/vnd.wap.wbmp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/x-icon": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/x-jng": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/x-ms-bmp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/svg+xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/webp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/sql": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-javascript": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/atom+xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/rss+xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/java-archive": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/mac-binhex40": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/msword": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/pdf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/postscript": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/rtf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/vnd.ms-excel": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/vnd.ms-powerpoint": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/vnd.wap.wmlc": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/vnd.google-earth.kml+xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/vnd.google-earth.kmz": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-7z-compressed": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-cocoa": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-java-archive-diff": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-java-jnlp-file": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-makeself": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-perl": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-pilot": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-rar-compressed": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-redhat-package-manager": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-sea": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-shockwave-flash": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-stuffit": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-tcl": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-x509-ca-cert": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-xpinstall": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/xhtml+xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/zip": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/octet-stream": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "audio/midi": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "audio/mpeg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "audio/ogg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "audio/x-realaudio": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/3gpp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/mpeg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/quicktime": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/x-flv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/x-mng": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/x-ms-asf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/x-ms-wmv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/x-msvideo": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/mp4": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/webm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + { + "type": "object", + "alias": "{ readonly \"text/html\"?: false | 0 | undefined; readonly \"text/css\"?: false | 0 | undefined; readonly \"text/csv\"?: false | 0 | undefined; readonly \"text/tsv\"?: false | 0 | undefined; readonly \"text/xml\"?: false | ... 1 more ... | undefined; ... 61 more ...; readonly \"video/webm\"?: false | ... 1 more ... | undefined; }", + "properties": { + "text/html": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/css": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/csv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/tsv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/mathml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/plain": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/vnd.sun.j2me.app-descriptor": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/vnd.wap.wml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "text/x-component": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/gif": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/jpeg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/png": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/tiff": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/vnd.wap.wbmp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/x-icon": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/x-jng": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/x-ms-bmp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/svg+xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "image/webp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/sql": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-javascript": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/atom+xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/rss+xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/java-archive": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/mac-binhex40": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/msword": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/pdf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/postscript": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/rtf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/vnd.ms-excel": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/vnd.ms-powerpoint": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/vnd.wap.wmlc": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/vnd.google-earth.kml+xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/vnd.google-earth.kmz": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-7z-compressed": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-cocoa": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-java-archive-diff": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-java-jnlp-file": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-makeself": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-perl": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-pilot": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-rar-compressed": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-redhat-package-manager": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-sea": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-shockwave-flash": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-stuffit": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-tcl": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-x509-ca-cert": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/x-xpinstall": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/xhtml+xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/zip": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "application/octet-stream": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "audio/midi": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "audio/mpeg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "audio/ogg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "audio/x-realaudio": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/3gpp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/mpeg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/quicktime": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/x-flv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/x-mng": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/x-ms-asf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/x-ms-wmv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/x-msvideo": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/mp4": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "video/webm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + { + "type": "array", + "alias": "(\"text/html\" | \"text/css\" | \"text/csv\" | \"text/tsv\" | \"text/xml\" | \"text/mathml\" | \"text/plain\" | \"text/vnd.sun.j2me.app-descriptor\" | \"text/vnd.wap.wml\" | \"text/x-component\" | ... 56 more ... | \"video/webm\")[]", + "itemType": { + "type": "union", + "alias": "\"text/html\" | \"text/css\" | \"text/csv\" | \"text/tsv\" | \"text/xml\" | \"text/mathml\" | \"text/plain\" | \"text/vnd.sun.j2me.app-descriptor\" | \"text/vnd.wap.wml\" | \"text/x-component\" | ... 56 more ... | \"video/webm\"", + "types": [ + { + "type": "literal", + "alias": "\"text/html\"", + "value": "text/html" + }, + { + "type": "literal", + "alias": "\"text/css\"", + "value": "text/css" + }, + { + "type": "literal", + "alias": "\"text/csv\"", + "value": "text/csv" + }, + { + "type": "literal", + "alias": "\"text/tsv\"", + "value": "text/tsv" + }, + { + "type": "literal", + "alias": "\"text/xml\"", + "value": "text/xml" + }, + { + "type": "literal", + "alias": "\"text/mathml\"", + "value": "text/mathml" + }, + { + "type": "literal", + "alias": "\"text/plain\"", + "value": "text/plain" + }, + { + "type": "literal", + "alias": "\"text/vnd.sun.j2me.app-descriptor\"", + "value": "text/vnd.sun.j2me.app-descriptor" + }, + { + "type": "literal", + "alias": "\"text/vnd.wap.wml\"", + "value": "text/vnd.wap.wml" + }, + { + "type": "literal", + "alias": "\"text/x-component\"", + "value": "text/x-component" + }, + { + "type": "literal", + "alias": "\"image/gif\"", + "value": "image/gif" + }, + { + "type": "literal", + "alias": "\"image/jpeg\"", + "value": "image/jpeg" + }, + { + "type": "literal", + "alias": "\"image/png\"", + "value": "image/png" + }, + { + "type": "literal", + "alias": "\"image/tiff\"", + "value": "image/tiff" + }, + { + "type": "literal", + "alias": "\"image/vnd.wap.wbmp\"", + "value": "image/vnd.wap.wbmp" + }, + { + "type": "literal", + "alias": "\"image/x-icon\"", + "value": "image/x-icon" + }, + { + "type": "literal", + "alias": "\"image/x-jng\"", + "value": "image/x-jng" + }, + { + "type": "literal", + "alias": "\"image/x-ms-bmp\"", + "value": "image/x-ms-bmp" + }, + { + "type": "literal", + "alias": "\"image/svg+xml\"", + "value": "image/svg+xml" + }, + { + "type": "literal", + "alias": "\"image/webp\"", + "value": "image/webp" + }, + { + "type": "literal", + "alias": "\"application/sql\"", + "value": "application/sql" + }, + { + "type": "literal", + "alias": "\"application/x-javascript\"", + "value": "application/x-javascript" + }, + { + "type": "literal", + "alias": "\"application/atom+xml\"", + "value": "application/atom+xml" + }, + { + "type": "literal", + "alias": "\"application/rss+xml\"", + "value": "application/rss+xml" + }, + { + "type": "literal", + "alias": "\"application/java-archive\"", + "value": "application/java-archive" + }, + { + "type": "literal", + "alias": "\"application/mac-binhex40\"", + "value": "application/mac-binhex40" + }, + { + "type": "literal", + "alias": "\"application/msword\"", + "value": "application/msword" + }, + { + "type": "literal", + "alias": "\"application/pdf\"", + "value": "application/pdf" + }, + { + "type": "literal", + "alias": "\"application/postscript\"", + "value": "application/postscript" + }, + { + "type": "literal", + "alias": "\"application/rtf\"", + "value": "application/rtf" + }, + { + "type": "literal", + "alias": "\"application/vnd.ms-excel\"", + "value": "application/vnd.ms-excel" + }, + { + "type": "literal", + "alias": "\"application/vnd.ms-powerpoint\"", + "value": "application/vnd.ms-powerpoint" + }, + { + "type": "literal", + "alias": "\"application/vnd.wap.wmlc\"", + "value": "application/vnd.wap.wmlc" + }, + { + "type": "literal", + "alias": "\"application/vnd.google-earth.kml+xml\"", + "value": "application/vnd.google-earth.kml+xml" + }, + { + "type": "literal", + "alias": "\"application/vnd.google-earth.kmz\"", + "value": "application/vnd.google-earth.kmz" + }, + { + "type": "literal", + "alias": "\"application/x-7z-compressed\"", + "value": "application/x-7z-compressed" + }, + { + "type": "literal", + "alias": "\"application/x-cocoa\"", + "value": "application/x-cocoa" + }, + { + "type": "literal", + "alias": "\"application/x-java-archive-diff\"", + "value": "application/x-java-archive-diff" + }, + { + "type": "literal", + "alias": "\"application/x-java-jnlp-file\"", + "value": "application/x-java-jnlp-file" + }, + { + "type": "literal", + "alias": "\"application/x-makeself\"", + "value": "application/x-makeself" + }, + { + "type": "literal", + "alias": "\"application/x-perl\"", + "value": "application/x-perl" + }, + { + "type": "literal", + "alias": "\"application/x-pilot\"", + "value": "application/x-pilot" + }, + { + "type": "literal", + "alias": "\"application/x-rar-compressed\"", + "value": "application/x-rar-compressed" + }, + { + "type": "literal", + "alias": "\"application/x-redhat-package-manager\"", + "value": "application/x-redhat-package-manager" + }, + { + "type": "literal", + "alias": "\"application/x-sea\"", + "value": "application/x-sea" + }, + { + "type": "literal", + "alias": "\"application/x-shockwave-flash\"", + "value": "application/x-shockwave-flash" + }, + { + "type": "literal", + "alias": "\"application/x-stuffit\"", + "value": "application/x-stuffit" + }, + { + "type": "literal", + "alias": "\"application/x-tcl\"", + "value": "application/x-tcl" + }, + { + "type": "literal", + "alias": "\"application/x-x509-ca-cert\"", + "value": "application/x-x509-ca-cert" + }, + { + "type": "literal", + "alias": "\"application/x-xpinstall\"", + "value": "application/x-xpinstall" + }, + { + "type": "literal", + "alias": "\"application/xhtml+xml\"", + "value": "application/xhtml+xml" + }, + { + "type": "literal", + "alias": "\"application/zip\"", + "value": "application/zip" + }, + { + "type": "literal", + "alias": "\"application/octet-stream\"", + "value": "application/octet-stream" + }, + { + "type": "literal", + "alias": "\"audio/midi\"", + "value": "audio/midi" + }, + { + "type": "literal", + "alias": "\"audio/mpeg\"", + "value": "audio/mpeg" + }, + { + "type": "literal", + "alias": "\"audio/ogg\"", + "value": "audio/ogg" + }, + { + "type": "literal", + "alias": "\"audio/x-realaudio\"", + "value": "audio/x-realaudio" + }, + { + "type": "literal", + "alias": "\"video/3gpp\"", + "value": "video/3gpp" + }, + { + "type": "literal", + "alias": "\"video/mpeg\"", + "value": "video/mpeg" + }, + { + "type": "literal", + "alias": "\"video/quicktime\"", + "value": "video/quicktime" + }, + { + "type": "literal", + "alias": "\"video/x-flv\"", + "value": "video/x-flv" + }, + { + "type": "literal", + "alias": "\"video/x-mng\"", + "value": "video/x-mng" + }, + { + "type": "literal", + "alias": "\"video/x-ms-asf\"", + "value": "video/x-ms-asf" + }, + { + "type": "literal", + "alias": "\"video/x-ms-wmv\"", + "value": "video/x-ms-wmv" + }, + { + "type": "literal", + "alias": "\"video/x-msvideo\"", + "value": "video/x-msvideo" + }, + { + "type": "literal", + "alias": "\"video/mp4\"", + "value": "video/mp4" + }, + { + "type": "literal", + "alias": "\"video/webm\"", + "value": "video/webm" + } + ] + } + } + ], + "optional": false + }, + "maxFileSizeMB": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true + } + } + }, + { + "type": "object", + "alias": "{ acceptedFileTypes: FieldFilter>; } & { ...; }", + "properties": { + "acceptedFileTypes": { + "type": "union", + "alias": "FieldFilter>", + "aliasSymbolescapedName": "FieldFilter", + "comments": "List of fields to include or exclude", + "types": [ + { + "type": "literal", + "alias": "\"\"", + "value": "" + }, + { + "type": "literal", + "alias": "\"*\"", + "value": "*" + }, + { + "type": "object", + "alias": "{ \"*\": 1; }", + "properties": { + "*": { + "type": "primitive", + "alias": "1", + "subType": "number", + "optional": false + } + } + }, + { + "type": "object", + "alias": "{ html?: true | 1 | undefined; htm?: true | 1 | undefined; shtml?: true | 1 | undefined; css?: true | 1 | undefined; csv?: true | 1 | undefined; tsv?: true | 1 | undefined; xml?: true | 1 | undefined; ... 91 more ...; mp4?: true | ... 1 more ... | undefined; }", + "properties": { + "html": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "htm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "shtml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "css": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "csv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "tsv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "webm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "txt": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jad": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "wml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "htc": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "gif": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jpeg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jpg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "png": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "tif": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "tiff": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "wbmp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ico": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jng": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "bmp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "svg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "webp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "sql": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "js": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "atom": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "rss": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jar": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "war": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ear": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "hqx": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "doc": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "docx": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pdf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ps": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "eps": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ai": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "rtf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "xls": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "xlsx": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ppt": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pptx": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "wmlc": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "kml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "kmz": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "7z": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "cco": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jardiff": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jnlp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "run": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pl": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "prc": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pdb": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "rar": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "rpm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "sea": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "swf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "sit": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "tcl": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "tk": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "der": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pem": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "crt": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "xpi": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "xhtml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "zip": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "bin": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "exe": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "dll": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "deb": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "dmg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "eot": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "iso": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "img": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "msi": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "msp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "msm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mid": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "midi": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "kar": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mp3": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ogg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ra": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "3gpp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "3gp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mpeg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mpg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mov": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "flv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mng": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "asx": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "asf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "wmv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "avi": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "m4v": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mp4": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + { + "type": "object", + "alias": "{ html?: false | 0 | undefined; htm?: false | 0 | undefined; shtml?: false | 0 | undefined; css?: false | 0 | undefined; csv?: false | 0 | undefined; tsv?: false | 0 | undefined; xml?: false | 0 | undefined; ... 91 more ...; mp4?: false | ... 1 more ... | undefined; }", + "properties": { + "html": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "htm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "shtml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "css": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "csv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "tsv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "xml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "webm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "txt": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jad": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "wml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "htc": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "gif": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jpeg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jpg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "png": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "tif": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "tiff": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "wbmp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ico": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jng": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "bmp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "svg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "webp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "sql": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "js": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "atom": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "rss": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jar": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "war": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ear": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "hqx": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "doc": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "docx": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pdf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ps": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "eps": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ai": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "rtf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "xls": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "xlsx": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ppt": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pptx": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "wmlc": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "kml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "kmz": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "7z": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "cco": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jardiff": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "jnlp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "run": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pl": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "prc": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pdb": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "rar": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "rpm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "sea": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "swf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "sit": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "tcl": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "tk": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "der": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pem": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "crt": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "xpi": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "xhtml": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "zip": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "bin": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "exe": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "dll": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "deb": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "dmg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "eot": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "iso": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "img": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "msi": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "msp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "msm": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mid": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "midi": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "kar": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mp3": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ogg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "ra": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "3gpp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "3gp": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mpeg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mpg": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mov": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "flv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mng": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "asx": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "asf": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "wmv": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "avi": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "m4v": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "mp4": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + { + "type": "array", + "alias": "(\"html\" | \"htm\" | \"shtml\" | \"css\" | \"csv\" | \"tsv\" | \"xml\" | \"webm\" | \"mml\" | \"txt\" | \"jad\" | \"wml\" | \"htc\" | \"gif\" | \"jpeg\" | \"jpg\" | \"png\" | \"tif\" | \"tiff\" | \"wbmp\" | \"ico\" | \"jng\" | ... 76 more ... | \"mp4\")[]", + "itemType": { + "type": "union", + "alias": "\"html\" | \"htm\" | \"shtml\" | \"css\" | \"csv\" | \"tsv\" | \"xml\" | \"webm\" | \"mml\" | \"txt\" | \"jad\" | \"wml\" | \"htc\" | \"gif\" | \"jpeg\" | \"jpg\" | \"png\" | \"tif\" | \"tiff\" | \"wbmp\" | \"ico\" | \"jng\" | ... 76 more ... | \"mp4\"", + "types": [ + { + "type": "literal", + "alias": "\"html\"", + "value": "html" + }, + { + "type": "literal", + "alias": "\"htm\"", + "value": "htm" + }, + { + "type": "literal", + "alias": "\"shtml\"", + "value": "shtml" + }, + { + "type": "literal", + "alias": "\"css\"", + "value": "css" + }, + { + "type": "literal", + "alias": "\"csv\"", + "value": "csv" + }, + { + "type": "literal", + "alias": "\"tsv\"", + "value": "tsv" + }, + { + "type": "literal", + "alias": "\"xml\"", + "value": "xml" + }, + { + "type": "literal", + "alias": "\"webm\"", + "value": "webm" + }, + { + "type": "literal", + "alias": "\"mml\"", + "value": "mml" + }, + { + "type": "literal", + "alias": "\"txt\"", + "value": "txt" + }, + { + "type": "literal", + "alias": "\"jad\"", + "value": "jad" + }, + { + "type": "literal", + "alias": "\"wml\"", + "value": "wml" + }, + { + "type": "literal", + "alias": "\"htc\"", + "value": "htc" + }, + { + "type": "literal", + "alias": "\"gif\"", + "value": "gif" + }, + { + "type": "literal", + "alias": "\"jpeg\"", + "value": "jpeg" + }, + { + "type": "literal", + "alias": "\"jpg\"", + "value": "jpg" + }, + { + "type": "literal", + "alias": "\"png\"", + "value": "png" + }, + { + "type": "literal", + "alias": "\"tif\"", + "value": "tif" + }, + { + "type": "literal", + "alias": "\"tiff\"", + "value": "tiff" + }, + { + "type": "literal", + "alias": "\"wbmp\"", + "value": "wbmp" + }, + { + "type": "literal", + "alias": "\"ico\"", + "value": "ico" + }, + { + "type": "literal", + "alias": "\"jng\"", + "value": "jng" + }, + { + "type": "literal", + "alias": "\"bmp\"", + "value": "bmp" + }, + { + "type": "literal", + "alias": "\"svg\"", + "value": "svg" + }, + { + "type": "literal", + "alias": "\"webp\"", + "value": "webp" + }, + { + "type": "literal", + "alias": "\"sql\"", + "value": "sql" + }, + { + "type": "literal", + "alias": "\"js\"", + "value": "js" + }, + { + "type": "literal", + "alias": "\"atom\"", + "value": "atom" + }, + { + "type": "literal", + "alias": "\"rss\"", + "value": "rss" + }, + { + "type": "literal", + "alias": "\"jar\"", + "value": "jar" + }, + { + "type": "literal", + "alias": "\"war\"", + "value": "war" + }, + { + "type": "literal", + "alias": "\"ear\"", + "value": "ear" + }, + { + "type": "literal", + "alias": "\"hqx\"", + "value": "hqx" + }, + { + "type": "literal", + "alias": "\"doc\"", + "value": "doc" + }, + { + "type": "literal", + "alias": "\"docx\"", + "value": "docx" + }, + { + "type": "literal", + "alias": "\"pdf\"", + "value": "pdf" + }, + { + "type": "literal", + "alias": "\"ps\"", + "value": "ps" + }, + { + "type": "literal", + "alias": "\"eps\"", + "value": "eps" + }, + { + "type": "literal", + "alias": "\"ai\"", + "value": "ai" + }, + { + "type": "literal", + "alias": "\"rtf\"", + "value": "rtf" + }, + { + "type": "literal", + "alias": "\"xls\"", + "value": "xls" + }, + { + "type": "literal", + "alias": "\"xlsx\"", + "value": "xlsx" + }, + { + "type": "literal", + "alias": "\"ppt\"", + "value": "ppt" + }, + { + "type": "literal", + "alias": "\"pptx\"", + "value": "pptx" + }, + { + "type": "literal", + "alias": "\"wmlc\"", + "value": "wmlc" + }, + { + "type": "literal", + "alias": "\"kml\"", + "value": "kml" + }, + { + "type": "literal", + "alias": "\"kmz\"", + "value": "kmz" + }, + { + "type": "literal", + "alias": "\"7z\"", + "value": "7z" + }, + { + "type": "literal", + "alias": "\"cco\"", + "value": "cco" + }, + { + "type": "literal", + "alias": "\"jardiff\"", + "value": "jardiff" + }, + { + "type": "literal", + "alias": "\"jnlp\"", + "value": "jnlp" + }, + { + "type": "literal", + "alias": "\"run\"", + "value": "run" + }, + { + "type": "literal", + "alias": "\"pl\"", + "value": "pl" + }, + { + "type": "literal", + "alias": "\"pm\"", + "value": "pm" + }, + { + "type": "literal", + "alias": "\"prc\"", + "value": "prc" + }, + { + "type": "literal", + "alias": "\"pdb\"", + "value": "pdb" + }, + { + "type": "literal", + "alias": "\"rar\"", + "value": "rar" + }, + { + "type": "literal", + "alias": "\"rpm\"", + "value": "rpm" + }, + { + "type": "literal", + "alias": "\"sea\"", + "value": "sea" + }, + { + "type": "literal", + "alias": "\"swf\"", + "value": "swf" + }, + { + "type": "literal", + "alias": "\"sit\"", + "value": "sit" + }, + { + "type": "literal", + "alias": "\"tcl\"", + "value": "tcl" + }, + { + "type": "literal", + "alias": "\"tk\"", + "value": "tk" + }, + { + "type": "literal", + "alias": "\"der\"", + "value": "der" + }, + { + "type": "literal", + "alias": "\"pem\"", + "value": "pem" + }, + { + "type": "literal", + "alias": "\"crt\"", + "value": "crt" + }, + { + "type": "literal", + "alias": "\"xpi\"", + "value": "xpi" + }, + { + "type": "literal", + "alias": "\"xhtml\"", + "value": "xhtml" + }, + { + "type": "literal", + "alias": "\"zip\"", + "value": "zip" + }, + { + "type": "literal", + "alias": "\"bin\"", + "value": "bin" + }, + { + "type": "literal", + "alias": "\"exe\"", + "value": "exe" + }, + { + "type": "literal", + "alias": "\"dll\"", + "value": "dll" + }, + { + "type": "literal", + "alias": "\"deb\"", + "value": "deb" + }, + { + "type": "literal", + "alias": "\"dmg\"", + "value": "dmg" + }, + { + "type": "literal", + "alias": "\"eot\"", + "value": "eot" + }, + { + "type": "literal", + "alias": "\"iso\"", + "value": "iso" + }, + { + "type": "literal", + "alias": "\"img\"", + "value": "img" + }, + { + "type": "literal", + "alias": "\"msi\"", + "value": "msi" + }, + { + "type": "literal", + "alias": "\"msp\"", + "value": "msp" + }, + { + "type": "literal", + "alias": "\"msm\"", + "value": "msm" + }, + { + "type": "literal", + "alias": "\"mid\"", + "value": "mid" + }, + { + "type": "literal", + "alias": "\"midi\"", + "value": "midi" + }, + { + "type": "literal", + "alias": "\"kar\"", + "value": "kar" + }, + { + "type": "literal", + "alias": "\"mp3\"", + "value": "mp3" + }, + { + "type": "literal", + "alias": "\"ogg\"", + "value": "ogg" + }, + { + "type": "literal", + "alias": "\"ra\"", + "value": "ra" + }, + { + "type": "literal", + "alias": "\"3gpp\"", + "value": "3gpp" + }, + { + "type": "literal", + "alias": "\"3gp\"", + "value": "3gp" + }, + { + "type": "literal", + "alias": "\"mpeg\"", + "value": "mpeg" + }, + { + "type": "literal", + "alias": "\"mpg\"", + "value": "mpg" + }, + { + "type": "literal", + "alias": "\"mov\"", + "value": "mov" + }, + { + "type": "literal", + "alias": "\"flv\"", + "value": "flv" + }, + { + "type": "literal", + "alias": "\"mng\"", + "value": "mng" + }, + { + "type": "literal", + "alias": "\"asx\"", + "value": "asx" + }, + { + "type": "literal", + "alias": "\"asf\"", + "value": "asf" + }, + { + "type": "literal", + "alias": "\"wmv\"", + "value": "wmv" + }, + { + "type": "literal", + "alias": "\"avi\"", + "value": "avi" + }, + { + "type": "literal", + "alias": "\"m4v\"", + "value": "m4v" + }, + { + "type": "literal", + "alias": "\"mp4\"", + "value": "mp4" + } + ] + } + } + ], + "optional": false + }, + "maxFileSizeMB": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true + } + } + } + ], + "optional": true, + "comments": "If degined then this column is referencing the file table\nExtracted from FileTable config\nUsed in SmartForm" + }, + "tsDataType": { + "type": "union", + "alias": "\"string\" | \"number\" | \"boolean\" | \"any\" | \"number[]\" | \"boolean[]\" | \"string[]\" | \"any[]\"", + "types": [ + { + "type": "literal", + "alias": "\"string\"", + "value": "string" + }, + { + "type": "literal", + "alias": "\"number\"", + "value": "number" + }, + { + "type": "literal", + "alias": "\"boolean\"", + "value": "boolean" + }, + { + "type": "literal", + "alias": "\"any\"", + "value": "any" + }, + { + "type": "literal", + "alias": "\"number[]\"", + "value": "number[]" + }, + { + "type": "literal", + "alias": "\"boolean[]\"", + "value": "boolean[]" + }, + { + "type": "literal", + "alias": "\"string[]\"", + "value": "string[]" + }, + { + "type": "literal", + "alias": "\"any[]\"", + "value": "any[]" + } + ], + "optional": false, + "comments": "TypeScript data type" + }, + "select": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false, + "comments": "Can be viewed/selected" + }, + "orderBy": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false, + "comments": "Can be ordered by" + }, + "filter": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false, + "comments": "Can be filtered by" + }, + "insert": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false, + "comments": "Can be inserted" + }, + "update": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false, + "comments": "Can be updated" + }, + "delete": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false, + "comments": "Can be used in the delete filter" + } + } + } + } + }, + "optional": false, + "comments": "Retrieves columns metadata of the table/view" + }, + "find": { + "type": "function", + "alias": "

>(filter?: FullFilter | undefined, selectParams?: P | undefined) => Promise>", + "arguments": [ + { + "name": "filter", + "optional": true, + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "" + }, + { + "name": "selectParams", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise>", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "primitive", + "alias": "GetSelectReturnType", + "aliasSymbolescapedName": "GetSelectReturnType", + "subType": "any" + } + }, + "optional": false, + "comments": "Retrieves a list of matching records from the view/table" + }, + "findOne": { + "type": "function", + "alias": "

>(filter?: FullFilter | undefined, selectParams?: P | undefined) => Promise | undefined>", + "arguments": [ + { + "name": "filter", + "optional": true, + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "" + }, + { + "name": "selectParams", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise | undefined>", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "union", + "alias": "GetSelectReturnType | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "GetSelectReturnType", + "aliasSymbolescapedName": "GetSelectReturnType", + "subType": "any" + } + ] + } + }, + "optional": false, + "comments": "Retrieves a record from the view/table" + }, + "subscribe": { + "type": "function", + "alias": "

>(filter: FullFilter, params: P, onData: (items: GetSelectReturnType) => any, onError?: OnError | undefined) => Promise<...>", + "arguments": [ + { + "name": "filter", + "optional": false, + "type": "reference", + "alias": "FullFilter", + "aliasSymbolescapedName": "FullFilter", + "comments": "Group or simple filter" + }, + { + "name": "params", + "optional": false, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + }, + { + "name": "onData", + "optional": false, + "type": "function", + "alias": "(items: GetSelectReturnType) => any", + "arguments": [ + { + "name": "items", + "optional": false, + "type": "primitive", + "alias": "GetSelectReturnType", + "aliasSymbolescapedName": "GetSelectReturnType", + "subType": "any", + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "any", + "subType": "any" + }, + "comments": "" + }, + { + "name": "onError", + "optional": true, + "type": "function", + "alias": "OnError", + "aliasSymbolescapedName": "OnError", + "arguments": [ + { + "name": "err", + "optional": false, + "type": "primitive", + "alias": "any", + "subType": "any", + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "void", + "subType": "any" + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "object", + "alias": "SubscriptionHandler", + "aliasSymbolescapedName": "SubscriptionHandler", + "comments": "", + "properties": { + "unsubscribe": { + "type": "function", + "alias": "() => Promise", + "arguments": [], + "returnType": { + "type": "promise", + "alias": "Promise", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "primitive", + "alias": "any", + "subType": "any" + } + }, + "optional": false + }, + "filter": { + "type": "union", + "alias": "{} | FullFilter", + "types": [ + { + "type": "object", + "alias": "{}", + "properties": {} + }, + { + "type": "object", + "alias": "ComplexFilter", + "aliasSymbolescapedName": "ComplexFilter", + "comments": "Complex filter that allows applying functions to columns", + "properties": { + "$filter": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": false + } + } + }, + { + "type": "object", + "alias": "{ $and: FullFilter[]; }", + "properties": { + "$and": { + "type": "array", + "alias": "FullFilter[]", + "itemType": { + "type": "reference", + "alias": "FullFilter", + "aliasSymbolescapedName": "FullFilter", + "comments": "Group or simple filter" + }, + "optional": false + } + } + }, + { + "type": "object", + "alias": "{ $or: FullFilter[]; }", + "properties": { + "$or": { + "type": "reference", + "alias": "FullFilter[]", + "optional": false + } + } + }, + { + "type": "object", + "alias": "NormalFilter", + "aliasSymbolescapedName": "NormalFilter", + "comments": "Column filter with operators\nMultiple columns are combined with AND", + "properties": { + "$filter": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.=`]: any; [x: `${string}.$eq`]: any; [x: `${string}.<>`]: any; [x: `${string}.>`]: any; [x: `${string}.<`]: any; [x: `${string}.>=`]: any; [x: `${string}.<=`]: any; [x: `${string}.$ne`]: any; [x: `${string}.$gt`]: any; [x: `${string}.$gte`]: any; [x: `${string}.$lt`]: any; [x: `${string}.$lt...", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.$in`]: any[]; [x: `${string}.$nin`]: any[]; }>", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {}, + "intersectionParent": "Partial<{ [x: `${string}.$in`]: any[]; [x: `${string}.$nin`]: any[]; }> & Partial<{ [x: `${string}.@@`]: any; [x: `${string}.@>`]: any; [x: `${string}.<@`]: any; [x: `${string}.$contains`]: any; [x: `${string}.$containedBy`]: any; }>" + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.=`]: any; [x: `${string}.$eq`]: any; [x: `${string}.<>`]: any; [x: `${string}.>`]: any; [x: `${string}.<`]: any; [x: `${string}.>=`]: any; [x: `${string}.<=`]: any; [x: `${string}.$ne`]: any; [x: `${string}.$gt`]: any; [x: `${string}.$gte`]: any; [x: `${string}.$lt`]: any; [x: `${string}.$lt...", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.=`]: any; [x: `${string}.$eq`]: any; [x: `${string}.<>`]: any; [x: `${string}.>`]: any; [x: `${string}.<`]: any; [x: `${string}.>=`]: any; [x: `${string}.<=`]: any; [x: `${string}.$ne`]: any; [x: `${string}.$gt`]: any; [x: `${string}.$gte`]: any; [x: `${string}.$lt`]: any; [x: `${string}.$lt...", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.$in`]: any[]; [x: `${string}.$nin`]: any[]; }> & Partial<{ [x: `${string}.$ilike`]: any; [x: `${string}.$like`]: any; [x: `${string}.$nilike`]: any; [x: `${string}.$nlike`]: any; }>", + "properties": {} + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.$in`]: any[]; [x: `${string}.$nin`]: any[]; }> & Partial<{ [x: `${string}.@@`]: any; [x: `${string}.@>`]: any; [x: `${string}.<@`]: any; [x: `${string}.$contains`]: any; [x: `${string}.$containedBy`]: any; }>", + "properties": {} + }, + { + "type": "object", + "alias": "Partial<{ $exists: any; $notExists: any; $existsJoined: any; $notExistsJoined: any; }>", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": { + "$exists": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$notExists": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$existsJoined": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$notExistsJoined": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + } + ], + "optional": false + } + } + } + }, + "optional": false, + "comments": "Retrieves a list of matching records from the view/table and subscribes to changes" + }, + "subscribeOne": { + "type": "function", + "alias": "

>(filter: FullFilter, params: P, onData: (item: GetSelectReturnType | undefined) => any, onError?: OnError | undefined) => Promise<...>", + "arguments": [ + { + "name": "filter", + "optional": false, + "type": "reference", + "alias": "FullFilter", + "aliasSymbolescapedName": "FullFilter", + "comments": "Group or simple filter" + }, + { + "name": "params", + "optional": false, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + }, + { + "name": "onData", + "optional": false, + "type": "function", + "alias": "(item: GetSelectReturnType | undefined) => any", + "arguments": [ + { + "name": "item", + "optional": false, + "type": "union", + "alias": "GetSelectReturnType | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "GetSelectReturnType", + "aliasSymbolescapedName": "GetSelectReturnType", + "subType": "any" + } + ], + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "any", + "subType": "any" + }, + "comments": "" + }, + { + "name": "onError", + "optional": true, + "type": "function", + "alias": "OnError", + "aliasSymbolescapedName": "OnError", + "arguments": [ + { + "name": "err", + "optional": false, + "type": "primitive", + "alias": "any", + "subType": "any", + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "void", + "subType": "any" + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "object", + "alias": "SubscriptionHandler", + "aliasSymbolescapedName": "SubscriptionHandler", + "comments": "", + "properties": { + "unsubscribe": { + "type": "function", + "alias": "() => Promise", + "arguments": [], + "returnType": { + "type": "promise", + "alias": "Promise", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "primitive", + "alias": "any", + "subType": "any" + } + }, + "optional": false + }, + "filter": { + "type": "union", + "alias": "{} | FullFilter", + "types": [ + { + "type": "object", + "alias": "{}", + "properties": {} + }, + { + "type": "object", + "alias": "ComplexFilter", + "aliasSymbolescapedName": "ComplexFilter", + "comments": "Complex filter that allows applying functions to columns", + "properties": { + "$filter": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": false + } + } + }, + { + "type": "object", + "alias": "{ $and: FullFilter[]; }", + "properties": { + "$and": { + "type": "array", + "alias": "FullFilter[]", + "itemType": { + "type": "reference", + "alias": "FullFilter", + "aliasSymbolescapedName": "FullFilter", + "comments": "Group or simple filter" + }, + "optional": false + } + } + }, + { + "type": "object", + "alias": "{ $or: FullFilter[]; }", + "properties": { + "$or": { + "type": "reference", + "alias": "FullFilter[]", + "optional": false + } + } + }, + { + "type": "object", + "alias": "NormalFilter", + "aliasSymbolescapedName": "NormalFilter", + "comments": "Column filter with operators\nMultiple columns are combined with AND", + "properties": { + "$filter": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.=`]: any; [x: `${string}.$eq`]: any; [x: `${string}.<>`]: any; [x: `${string}.>`]: any; [x: `${string}.<`]: any; [x: `${string}.>=`]: any; [x: `${string}.<=`]: any; [x: `${string}.$ne`]: any; [x: `${string}.$gt`]: any; [x: `${string}.$gte`]: any; [x: `${string}.$lt`]: any; [x: `${string}.$lt...", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.$in`]: any[]; [x: `${string}.$nin`]: any[]; }>", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {}, + "intersectionParent": "Partial<{ [x: `${string}.$in`]: any[]; [x: `${string}.$nin`]: any[]; }> & Partial<{ [x: `${string}.@@`]: any; [x: `${string}.@>`]: any; [x: `${string}.<@`]: any; [x: `${string}.$contains`]: any; [x: `${string}.$containedBy`]: any; }>" + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.=`]: any; [x: `${string}.$eq`]: any; [x: `${string}.<>`]: any; [x: `${string}.>`]: any; [x: `${string}.<`]: any; [x: `${string}.>=`]: any; [x: `${string}.<=`]: any; [x: `${string}.$ne`]: any; [x: `${string}.$gt`]: any; [x: `${string}.$gte`]: any; [x: `${string}.$lt`]: any; [x: `${string}.$lt...", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.=`]: any; [x: `${string}.$eq`]: any; [x: `${string}.<>`]: any; [x: `${string}.>`]: any; [x: `${string}.<`]: any; [x: `${string}.>=`]: any; [x: `${string}.<=`]: any; [x: `${string}.$ne`]: any; [x: `${string}.$gt`]: any; [x: `${string}.$gte`]: any; [x: `${string}.$lt`]: any; [x: `${string}.$lt...", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.$in`]: any[]; [x: `${string}.$nin`]: any[]; }> & Partial<{ [x: `${string}.$ilike`]: any; [x: `${string}.$like`]: any; [x: `${string}.$nilike`]: any; [x: `${string}.$nlike`]: any; }>", + "properties": {} + }, + { + "type": "object", + "alias": "Partial<{ [x: `${string}.$in`]: any[]; [x: `${string}.$nin`]: any[]; }> & Partial<{ [x: `${string}.@@`]: any; [x: `${string}.@>`]: any; [x: `${string}.<@`]: any; [x: `${string}.$contains`]: any; [x: `${string}.$containedBy`]: any; }>", + "properties": {} + }, + { + "type": "object", + "alias": "Partial<{ $exists: any; $notExists: any; $existsJoined: any; $notExistsJoined: any; }>", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": { + "$exists": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$notExists": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$existsJoined": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$notExistsJoined": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + } + ], + "optional": false + } + } + } + }, + "optional": false, + "comments": "Retrieves first matching record from the view/table and subscribes to changes" + }, + "count": { + "type": "function", + "alias": "

>(filter?: FullFilter | undefined, selectParams?: P | undefined) => Promise", + "arguments": [ + { + "name": "filter", + "optional": true, + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "" + }, + { + "name": "selectParams", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "primitive", + "alias": "number", + "subType": "number" + } + }, + "optional": false, + "comments": "Returns the number of rows that match the filter" + }, + "size": { + "type": "function", + "alias": "

>(filter?: FullFilter | undefined, selectParams?: P | undefined) => Promise", + "arguments": [ + { + "name": "filter", + "optional": true, + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "" + }, + { + "name": "selectParams", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "primitive", + "alias": "string", + "subType": "string" + } + }, + "optional": false, + "comments": "Returns result size in bits" + }, + "getJoinedTables": { + "type": "function", + "alias": "() => string[]", + "arguments": [], + "returnType": { + "type": "array", + "alias": "string[]", + "itemType": { + "type": "primitive", + "alias": "string", + "subType": "string" + } + }, + "optional": false + }, + "_syncInfo": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "getSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "sync": { + "type": "function", + "alias": "Sync", + "aliasSymbolescapedName": "Sync", + "arguments": [ + { + "name": "basicFilter", + "optional": false, + "type": "object", + "alias": "EqualityFilter", + "aliasSymbolescapedName": "EqualityFilter", + "comments": "Equality filter used for sync\nMultiple columns are combined with AND", + "properties": {} + }, + { + "name": "options", + "optional": false, + "type": "reference", + "alias": "SyncOptions", + "aliasSymbolescapedName": "SyncOptions", + "comments": "" + }, + { + "name": "onChange", + "optional": false, + "type": "function", + "alias": "(data: SyncDataItem, false>[], delta?: Partial[] | undefined) => any", + "arguments": [ + { + "name": "data", + "optional": false, + "type": "array", + "alias": "SyncDataItem, false>[]", + "itemType": { + "type": "object", + "alias": "SyncDataItem, false>", + "aliasSymbolescapedName": "SyncDataItem", + "properties": { + "$get": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$find": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$unsync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$delete": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$update": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneMultiSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + "comments": "" + }, + { + "name": "delta", + "optional": true, + "type": "array", + "alias": "Partial[]", + "itemType": { + "type": "object", + "alias": "Partial", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + }, + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "any", + "subType": "any" + }, + "comments": "" + }, + { + "name": "onError", + "optional": true, + "type": "function", + "alias": "(error: any) => void", + "arguments": [ + { + "name": "error", + "optional": false, + "type": "primitive", + "alias": "any", + "subType": "any", + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "void", + "subType": "any" + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise<{ $unsync: () => void; $upsert: (newData: T[]) => any; getItems: () => T[]; }>", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "object", + "alias": "{ $unsync: () => void; $upsert: (newData: T[]) => any; getItems: () => T[]; }", + "properties": { + "$unsync": { + "type": "function", + "alias": "() => void", + "arguments": [], + "returnType": { + "type": "primitive", + "alias": "void", + "subType": "any" + }, + "optional": false + }, + "$upsert": { + "type": "function", + "alias": "(newData: T[]) => any", + "arguments": [ + { + "name": "newData", + "optional": false, + "type": "array", + "alias": "T[]", + "itemType": { + "type": "object", + "alias": "AnyObject", + "aliasSymbolescapedName": "AnyObject", + "comments": "", + "properties": {}, + "intersectionParent": "SyncDataItem" + }, + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "any", + "subType": "any" + }, + "optional": false + }, + "getItems": { + "type": "function", + "alias": "() => T[]", + "arguments": [], + "returnType": { + "type": "array", + "alias": "T[]", + "itemType": { + "type": "object", + "alias": "AnyObject", + "aliasSymbolescapedName": "AnyObject", + "comments": "", + "properties": {}, + "intersectionParent": "SyncDataItem" + } + }, + "optional": false + } + } + } + }, + "optional": true + }, + "useSync": { + "type": "function", + "alias": "(basicFilter: EqualityFilter, syncOptions: SyncOptions) => { data: SyncDataItem>[] | undefined; isLoading: boolean; error?: any; }", + "arguments": [ + { + "name": "basicFilter", + "optional": false, + "type": "object", + "alias": "EqualityFilter", + "aliasSymbolescapedName": "EqualityFilter", + "comments": "Equality filter used for sync\nMultiple columns are combined with AND", + "properties": {} + }, + { + "name": "syncOptions", + "optional": false, + "type": "reference", + "alias": "SyncOptions", + "aliasSymbolescapedName": "SyncOptions", + "comments": "" + } + ], + "returnType": { + "type": "object", + "alias": "{ data: SyncDataItem>[] | undefined; isLoading: boolean; error?: any; }", + "properties": { + "data": { + "type": "union", + "alias": "SyncDataItem>[] | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "array", + "alias": "SyncDataItem>[]", + "itemType": { + "type": "object", + "alias": "SyncDataItem>", + "aliasSymbolescapedName": "SyncDataItem", + "properties": { + "$get": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$find": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$unsync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$delete": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$update": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneMultiSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + } + } + ], + "optional": false + }, + "isLoading": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false + }, + "error": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + "optional": true, + "comments": "Retrieves rows matching the filter and keeps them in sync\n- use { handlesOnData: true } to get optimistic updates method: $update\n- any changes to the row using the $update method will be reflected instantly\n to all sync subscribers that were initiated with the same syncOptions" + }, + "syncOne": { + "type": "function", + "alias": "SyncOne", + "aliasSymbolescapedName": "SyncOne", + "arguments": [ + { + "name": "basicFilter", + "optional": false, + "type": "object", + "alias": "Partial", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + }, + { + "name": "options", + "optional": false, + "type": "reference", + "alias": "SyncOneOptions", + "aliasSymbolescapedName": "SyncOneOptions", + "comments": "" + }, + { + "name": "onChange", + "optional": false, + "type": "function", + "alias": "(data: SyncDataItem, false>, delta?: Partial | undefined) => any", + "arguments": [ + { + "name": "data", + "optional": false, + "type": "object", + "alias": "SyncDataItem, false>", + "aliasSymbolescapedName": "SyncDataItem", + "properties": { + "$get": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$find": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$unsync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$delete": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$update": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneMultiSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + }, + "comments": "" + }, + { + "name": "delta", + "optional": true, + "type": "object", + "alias": "Partial", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + } + ], + "returnType": { + "type": "primitive", + "alias": "any", + "subType": "any" + }, + "comments": "" + }, + { + "name": "onError", + "optional": true, + "type": "function", + "alias": "(error: any) => void", + "arguments": [ + { + "name": "error", + "optional": false, + "type": "primitive", + "alias": "any", + "subType": "any", + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "void", + "subType": "any" + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise>", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "object", + "alias": "SingleSyncHandles", + "aliasSymbolescapedName": "SingleSyncHandles", + "comments": "CRUD handles added if initialised with handlesOnData = true", + "properties": { + "$get": { + "type": "function", + "alias": "() => T | undefined", + "arguments": [], + "returnType": { + "type": "union", + "alias": "T | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "object", + "alias": "AnyObject", + "aliasSymbolescapedName": "AnyObject", + "comments": "", + "properties": {}, + "intersectionParent": "SyncDataItem" + } + ] + }, + "optional": false + }, + "$find": { + "type": "function", + "alias": "(idObj: Partial) => T | undefined", + "arguments": [ + { + "name": "idObj", + "optional": false, + "type": "object", + "alias": "Partial", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + } + ], + "returnType": { + "type": "union", + "alias": "T | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "object", + "alias": "AnyObject", + "aliasSymbolescapedName": "AnyObject", + "comments": "", + "properties": {}, + "intersectionParent": "SyncDataItem" + } + ] + }, + "optional": false + }, + "$unsync": { + "type": "function", + "alias": "() => any", + "arguments": [], + "returnType": { + "type": "primitive", + "alias": "any", + "subType": "any" + }, + "optional": false + }, + "$delete": { + "type": "function", + "alias": "() => void", + "arguments": [], + "returnType": { + "type": "primitive", + "alias": "void", + "subType": "any" + }, + "optional": false + }, + "$update": { + "type": "function", + "alias": "(newData: OPTS extends { deepMerge: true; } ? DeepPartial : Partial, opts?: OPTS | undefined) => any", + "arguments": [ + { + "name": "newData", + "optional": false, + "type": "primitive", + "alias": "OPTS extends { deepMerge: true; } ? DeepPartial : Partial", + "subType": "any", + "comments": "" + }, + { + "name": "opts", + "optional": true, + "type": "object", + "alias": "$UpdateOpts", + "aliasSymbolescapedName": "$UpdateOpts", + "comments": "", + "properties": { + "deepMerge": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false + } + } + } + ], + "returnType": { + "type": "primitive", + "alias": "any", + "subType": "any" + }, + "optional": false + }, + "$cloneSync": { + "type": "function", + "alias": "CloneSync", + "aliasSymbolescapedName": "CloneSync", + "arguments": [ + { + "name": "onChange", + "optional": false, + "type": "function", + "alias": "SingleChangeListener", + "aliasSymbolescapedName": "SingleChangeListener", + "arguments": [ + { + "name": "item", + "optional": false, + "type": "object", + "alias": "SyncDataItem", + "aliasSymbolescapedName": "SyncDataItem", + "properties": { + "$get": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$find": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$unsync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$delete": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$update": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneMultiSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + }, + "comments": "" + }, + { + "name": "delta", + "optional": true, + "type": "primitive", + "alias": "DeepPartial", + "aliasSymbolescapedName": "DeepPartial", + "subType": "any", + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "any", + "subType": "any" + }, + "comments": "" + }, + { + "name": "onError", + "optional": true, + "type": "function", + "alias": "(error: any) => void", + "arguments": [ + { + "name": "error", + "optional": false, + "type": "primitive", + "alias": "any", + "subType": "any", + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "void", + "subType": "any" + }, + "comments": "" + } + ], + "returnType": { + "type": "reference", + "alias": "SingleSyncHandles", + "aliasSymbolescapedName": "SingleSyncHandles", + "comments": "CRUD handles added if initialised with handlesOnData = true" + }, + "optional": false + }, + "$cloneMultiSync": { + "type": "function", + "alias": "CloneMultiSync", + "aliasSymbolescapedName": "CloneMultiSync", + "arguments": [ + { + "name": "onChange", + "optional": false, + "type": "function", + "alias": "MultiChangeListener", + "aliasSymbolescapedName": "MultiChangeListener", + "arguments": [ + { + "name": "items", + "optional": false, + "type": "array", + "alias": "SyncDataItem[]", + "itemType": { + "type": "object", + "alias": "SyncDataItem", + "aliasSymbolescapedName": "SyncDataItem", + "properties": { + "$get": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$find": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$unsync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$delete": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$update": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneMultiSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + "comments": "" + }, + { + "name": "delta", + "optional": false, + "type": "array", + "alias": "DeepPartial[]", + "itemType": { + "type": "primitive", + "alias": "DeepPartial", + "aliasSymbolescapedName": "DeepPartial", + "subType": "any" + }, + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "any", + "subType": "any" + }, + "comments": "" + }, + { + "name": "onError", + "optional": true, + "type": "function", + "alias": "(error: any) => void", + "arguments": [ + { + "name": "error", + "optional": false, + "type": "primitive", + "alias": "any", + "subType": "any", + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "void", + "subType": "any" + }, + "comments": "" + } + ], + "returnType": { + "type": "object", + "alias": "MultiSyncHandles", + "aliasSymbolescapedName": "MultiSyncHandles", + "comments": "", + "properties": { + "$unsync": { + "type": "function", + "alias": "() => void", + "arguments": [], + "returnType": { + "type": "primitive", + "alias": "void", + "subType": "any" + }, + "optional": false + }, + "$upsert": { + "type": "function", + "alias": "(newData: T[]) => any", + "arguments": [ + { + "name": "newData", + "optional": false, + "type": "array", + "alias": "T[]", + "itemType": { + "type": "object", + "alias": "AnyObject", + "aliasSymbolescapedName": "AnyObject", + "comments": "", + "properties": {}, + "intersectionParent": "SyncDataItem" + }, + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "any", + "subType": "any" + }, + "optional": false + }, + "getItems": { + "type": "function", + "alias": "() => AnyObject[]", + "arguments": [], + "returnType": { + "type": "array", + "alias": "AnyObject[]", + "itemType": { + "type": "object", + "alias": "AnyObject", + "aliasSymbolescapedName": "AnyObject", + "comments": "", + "properties": {} + } + }, + "optional": false + } + } + }, + "optional": false + } + } + } + }, + "optional": true + }, + "useSyncOne": { + "type": "function", + "alias": "(basicFilter: EqualityFilter, syncOptions: SyncOneOptions) => { data: SyncDataItem> | undefined; isLoading: boolean; error?: any; }", + "arguments": [ + { + "name": "basicFilter", + "optional": false, + "type": "object", + "alias": "EqualityFilter", + "aliasSymbolescapedName": "EqualityFilter", + "comments": "Equality filter used for sync\nMultiple columns are combined with AND", + "properties": {} + }, + { + "name": "syncOptions", + "optional": false, + "type": "reference", + "alias": "SyncOneOptions", + "aliasSymbolescapedName": "SyncOneOptions", + "comments": "" + } + ], + "returnType": { + "type": "object", + "alias": "{ data: SyncDataItem> | undefined; isLoading: boolean; error?: any; }", + "properties": { + "data": { + "type": "union", + "alias": "SyncDataItem> | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "object", + "alias": "SyncDataItem>", + "aliasSymbolescapedName": "SyncDataItem", + "properties": { + "$get": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$find": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$unsync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$delete": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$update": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$cloneMultiSync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + } + ], + "optional": false + }, + "isLoading": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false + }, + "error": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + "optional": true, + "comments": "Retrieves the first row matching the filter and keeps it in sync\n- use { handlesOnData: true } to get optimistic updates method: $update\n- any changes to the row using the $update method will be reflected instantly\n to all sync subscribers that were initiated with the same syncOptions" + }, + "_sync": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "useSubscribe": { + "type": "function", + "alias": ">(filter?: FullFilter | undefined, options?: SubParams | undefined) => { data: GetSelectReturnType | undefined; error?: any; isLoading: boolean; }", + "arguments": [ + { + "name": "filter", + "optional": true, + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "" + }, + { + "name": "options", + "optional": true, + "type": "object", + "alias": "SubscribeParams", + "aliasSymbolescapedName": "SubscribeParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + }, + "throttle": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "If true then the subscription will be throttled to the provided number of milliseconds" + }, + "throttleOpts": { + "type": "object", + "alias": "{ skipFirst?: boolean | undefined; }", + "properties": { + "skipFirst": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "False by default.\nIf true then the first value will be emitted at the end of the interval. Instant otherwise" + } + }, + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "object", + "alias": "{ data: GetSelectReturnType | undefined; error?: any; isLoading: boolean; }", + "properties": { + "data": { + "type": "union", + "alias": "GetSelectReturnType | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "GetSelectReturnType", + "aliasSymbolescapedName": "GetSelectReturnType", + "subType": "any" + } + ], + "optional": false + }, + "error": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "isLoading": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false + } + } + }, + "optional": false, + "comments": "Retrieves a list of matching records from the view/table and subscribes to changes" + }, + "useSubscribeOne": { + "type": "function", + "alias": ">(filter?: FullFilter | undefined, options?: SubParams | undefined) => { data: GetSelectReturnType | undefined; error?: any; isLoading: boolean; }", + "arguments": [ + { + "name": "filter", + "optional": true, + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "" + }, + { + "name": "options", + "optional": true, + "type": "object", + "alias": "SubscribeParams", + "aliasSymbolescapedName": "SubscribeParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + }, + "throttle": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "If true then the subscription will be throttled to the provided number of milliseconds" + }, + "throttleOpts": { + "type": "object", + "alias": "{ skipFirst?: boolean | undefined; }", + "properties": { + "skipFirst": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "False by default.\nIf true then the first value will be emitted at the end of the interval. Instant otherwise" + } + }, + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "object", + "alias": "{ data: GetSelectReturnType | undefined; error?: any; isLoading: boolean; }", + "properties": { + "data": { + "type": "union", + "alias": "GetSelectReturnType | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "GetSelectReturnType", + "aliasSymbolescapedName": "GetSelectReturnType", + "subType": "any" + } + ], + "optional": false + }, + "error": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "isLoading": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false + } + } + }, + "optional": false, + "comments": "Retrieves a matching record from the view/table and subscribes to changes" + }, + "useFind": { + "type": "function", + "alias": "

>(filter?: FullFilter | undefined, selectParams?: P | undefined) => { data: GetSelectReturnType | undefined; isLoading: boolean; error?: any; }", + "arguments": [ + { + "name": "filter", + "optional": true, + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "" + }, + { + "name": "selectParams", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "object", + "alias": "{ data: GetSelectReturnType | undefined; isLoading: boolean; error?: any; }", + "properties": { + "data": { + "type": "union", + "alias": "GetSelectReturnType | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "GetSelectReturnType", + "aliasSymbolescapedName": "GetSelectReturnType", + "subType": "any" + } + ], + "optional": false + }, + "isLoading": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false + }, + "error": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + "optional": false, + "comments": "Retrieves a list of matching records from the view/table" + }, + "useFindOne": { + "type": "function", + "alias": "

>(filter?: FullFilter | undefined, selectParams?: P | undefined) => { data: GetSelectReturnType | undefined; isLoading: boolean; error?: any; }", + "arguments": [ + { + "name": "filter", + "optional": true, + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "" + }, + { + "name": "selectParams", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "object", + "alias": "{ data: GetSelectReturnType | undefined; isLoading: boolean; error?: any; }", + "properties": { + "data": { + "type": "union", + "alias": "GetSelectReturnType | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "GetSelectReturnType", + "aliasSymbolescapedName": "GetSelectReturnType", + "subType": "any" + } + ], + "optional": false + }, + "isLoading": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false + }, + "error": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + "optional": false, + "comments": "Retrieves first matching record from the view/table" + }, + "useCount": { + "type": "function", + "alias": "

>(filter?: FullFilter | undefined, selectParams?: P | undefined) => { data: number | undefined; isLoading: boolean; error?: any; }", + "arguments": [ + { + "name": "filter", + "optional": true, + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "" + }, + { + "name": "selectParams", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "object", + "alias": "{ data: number | undefined; isLoading: boolean; error?: any; }", + "properties": { + "data": { + "type": "union", + "alias": "number | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": false + }, + "isLoading": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false + }, + "error": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + "optional": false, + "comments": "Returns the total number of rows matching the filter" + }, + "useSize": { + "type": "function", + "alias": "

>(filter?: FullFilter | undefined, selectParams?: P | undefined) => { data: string | undefined; isLoading: boolean; error?: any; }", + "arguments": [ + { + "name": "filter", + "optional": true, + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "" + }, + { + "name": "selectParams", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "object", + "alias": "{ data: string | undefined; isLoading: boolean; error?: any; }", + "properties": { + "data": { + "type": "union", + "alias": "string | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "string", + "subType": "string" + } + ], + "optional": false + }, + "isLoading": { + "type": "union", + "alias": "boolean", + "types": [ + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": false + }, + "error": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + "optional": false, + "comments": "Returns result size in bits matching the filter and selectParams" + }, + "update": { + "type": "function", + "alias": "

>(filter: FullFilter, newData: Partial>, params?: P | undefined) => Promise<...>", + "arguments": [ + { + "name": "filter", + "optional": false, + "type": "reference", + "alias": "FullFilter", + "aliasSymbolescapedName": "FullFilter", + "comments": "Group or simple filter" + }, + { + "name": "newData", + "optional": false, + "type": "object", + "alias": "Partial>", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + }, + { + "name": "params", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise | undefined>", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "union", + "alias": "GetUpdateReturnType | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "GetUpdateReturnType", + "aliasSymbolescapedName": "GetUpdateReturnType", + "subType": "any" + } + ] + } + }, + "optional": false, + "comments": "Updates a record in the table based on the specified filter criteria\n- Use { multi: false } to ensure no more than one row is updated" + }, + "updateBatch": { + "type": "function", + "alias": "

>(data: [FullFilter, Partial>][], params?: P | undefined) => Promise<...>", + "arguments": [ + { + "name": "data", + "optional": false, + "type": "tuple", + "alias": "[FullFilter, Partial>][]", + "itemTypes": [ + { + "type": "reference", + "alias": "FullFilter", + "aliasSymbolescapedName": "FullFilter", + "comments": "Group or simple filter" + }, + { + "type": "object", + "alias": "Partial>", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + } + ], + "comments": "" + }, + { + "name": "params", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise>", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "union", + "alias": "void | GetUpdateReturnType", + "types": [ + { + "type": "primitive", + "alias": "void", + "subType": "any" + }, + { + "type": "primitive", + "alias": "GetUpdateReturnType", + "aliasSymbolescapedName": "GetUpdateReturnType", + "subType": "any" + } + ] + } + }, + "optional": false, + "comments": "Updates multiple records in the table in a batch operation.\n- Each item in the `data` array contains a filter and the corresponding data to update." + }, + "insert": { + "type": "function", + "alias": "

, D extends InsertData>(data: D, params?: P | undefined) => Promise>", + "arguments": [ + { + "name": "data", + "optional": false, + "type": "union", + "alias": "InsertData", + "aliasSymbolescapedName": "InsertData", + "types": [ + { + "type": "object", + "alias": "UpsertDataToPGCast", + "aliasSymbolescapedName": "UpsertDataToPGCast", + "comments": "", + "properties": {} + }, + { + "type": "array", + "alias": "UpsertDataToPGCast[]", + "itemType": { + "type": "object", + "alias": "UpsertDataToPGCast", + "aliasSymbolescapedName": "UpsertDataToPGCast", + "comments": "", + "properties": {} + } + } + ], + "comments": "" + }, + { + "name": "params", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise>", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "primitive", + "alias": "GetInsertReturnType", + "aliasSymbolescapedName": "GetInsertReturnType", + "subType": "any" + } + }, + "optional": false, + "comments": "Inserts a new record into the table." + }, + "upsert": { + "type": "function", + "alias": "

>(filter: FullFilter, newData: Partial>, params?: P | undefined) => Promise<...>", + "arguments": [ + { + "name": "filter", + "optional": false, + "type": "reference", + "alias": "FullFilter", + "aliasSymbolescapedName": "FullFilter", + "comments": "Group or simple filter" + }, + { + "name": "newData", + "optional": false, + "type": "object", + "alias": "Partial>", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": {} + }, + { + "name": "params", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise | undefined>", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "union", + "alias": "GetUpdateReturnType | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "GetUpdateReturnType", + "aliasSymbolescapedName": "GetUpdateReturnType", + "subType": "any" + } + ] + } + }, + "optional": false, + "comments": "Inserts or updates a record in the table.\n- If a record matching the `filter` exists, it updates the record.\n- If no matching record exists, it inserts a new record." + }, + "delete": { + "type": "function", + "alias": "

>(filter?: FullFilter | undefined, params?: P | undefined) => Promise | undefined>", + "arguments": [ + { + "name": "filter", + "optional": true, + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "" + }, + { + "name": "params", + "optional": true, + "type": "object", + "alias": "SelectParams", + "aliasSymbolescapedName": "SelectParams", + "properties": { + "limit": { + "type": "union", + "alias": "number | null | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "primitive", + "alias": "number", + "subType": "number" + } + ], + "optional": true, + "comments": "Max number of rows to return\n- If undefined then 1000 will be applied as the default\n- On client publish rules can affect this behaviour: cannot request more than the maxLimit (if present)" + }, + "offset": { + "type": "primitive", + "alias": "number", + "subType": "number", + "optional": true, + "comments": "Number of rows to skip" + }, + "groupBy": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true, + "comments": "Will group by all non aggregated fields specified in select (or all fields by default)" + }, + "returnType": { + "type": "union", + "alias": "\"row\" | \"value\" | \"values\" | \"statement\" | \"statement-no-rls\" | \"statement-where\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"row\"", + "value": "row" + }, + { + "type": "literal", + "alias": "\"value\"", + "value": "value" + }, + { + "type": "literal", + "alias": "\"values\"", + "value": "values" + }, + { + "type": "literal", + "alias": "\"statement\"", + "value": "statement" + }, + { + "type": "literal", + "alias": "\"statement-no-rls\"", + "value": "statement-no-rls" + }, + { + "type": "literal", + "alias": "\"statement-where\"", + "value": "statement-where" + } + ], + "optional": true, + "comments": "Result data structure/type:\n- row: the first row as an object\n- value: the first value from of first field\n- values: array of values from the selected field\n- statement: sql statement\n- statement-no-rls: sql statement without row level security\n- statement-where: sql statement where condition" + }, + "select": { + "type": "primitive", + "alias": "Select", + "aliasSymbolescapedName": "Select", + "comments": "Fields/expressions/linked data to select\n- If empty then all fields will be selected\n- If \"*\" then all fields will be selected\n- If { field: 0 } then all fields except the specified field will be selected\n- If { field: 1 } then only the specified field will be selected\n- If { field: { funcName: [args] } } then the field will be selected with the specified function applied\n- If { field: { nestedTable: { field: 1 } } } then the field will be selected with the nested table fields", + "subType": "any", + "optional": true + }, + "orderBy": { + "type": "primitive", + "alias": "OrderBy", + "aliasSymbolescapedName": "OrderBy", + "subType": "any", + "optional": true, + "comments": "Order by options\n- If array then the order will be maintained" + }, + "having": { + "type": "reference", + "alias": "FullFilter | undefined", + "comments": "Filter applied after any aggregations (group by)", + "optional": true + } + }, + "comments": "" + } + ], + "returnType": { + "type": "promise", + "alias": "Promise | undefined>", + "comments": "Represents the completion of an asynchronous operation", + "innerType": { + "type": "union", + "alias": "GetUpdateReturnType | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "GetUpdateReturnType", + "aliasSymbolescapedName": "GetUpdateReturnType", + "subType": "any" + } + ] + } + }, + "optional": false, + "comments": "Deletes records from the table based on the specified filter criteria.\n- If no filter is provided, all records may be deleted (use with caution)." + } + } + }, + { + "type": "union", + "alias": "PG_COLUMN_UDT_DATA_TYPE", + "aliasSymbolescapedName": "PG_COLUMN_UDT_DATA_TYPE", + "types": [ + { + "type": "literal", + "alias": "\"bpchar\"", + "value": "bpchar" + }, + { + "type": "literal", + "alias": "\"char\"", + "value": "char" + }, + { + "type": "literal", + "alias": "\"varchar\"", + "value": "varchar" + }, + { + "type": "literal", + "alias": "\"text\"", + "value": "text" + }, + { + "type": "literal", + "alias": "\"citext\"", + "value": "citext" + }, + { + "type": "literal", + "alias": "\"uuid\"", + "value": "uuid" + }, + { + "type": "literal", + "alias": "\"bytea\"", + "value": "bytea" + }, + { + "type": "literal", + "alias": "\"time\"", + "value": "time" + }, + { + "type": "literal", + "alias": "\"timetz\"", + "value": "timetz" + }, + { + "type": "literal", + "alias": "\"interval\"", + "value": "interval" + }, + { + "type": "literal", + "alias": "\"name\"", + "value": "name" + }, + { + "type": "literal", + "alias": "\"cidr\"", + "value": "cidr" + }, + { + "type": "literal", + "alias": "\"inet\"", + "value": "inet" + }, + { + "type": "literal", + "alias": "\"macaddr\"", + "value": "macaddr" + }, + { + "type": "literal", + "alias": "\"macaddr8\"", + "value": "macaddr8" + }, + { + "type": "literal", + "alias": "\"int4range\"", + "value": "int4range" + }, + { + "type": "literal", + "alias": "\"int8range\"", + "value": "int8range" + }, + { + "type": "literal", + "alias": "\"numrange\"", + "value": "numrange" + }, + { + "type": "literal", + "alias": "\"tsvector\"", + "value": "tsvector" + }, + { + "type": "literal", + "alias": "\"int2\"", + "value": "int2" + }, + { + "type": "literal", + "alias": "\"int4\"", + "value": "int4" + }, + { + "type": "literal", + "alias": "\"float4\"", + "value": "float4" + }, + { + "type": "literal", + "alias": "\"float8\"", + "value": "float8" + }, + { + "type": "literal", + "alias": "\"oid\"", + "value": "oid" + }, + { + "type": "literal", + "alias": "\"int8\"", + "value": "int8" + }, + { + "type": "literal", + "alias": "\"numeric\"", + "value": "numeric" + }, + { + "type": "literal", + "alias": "\"money\"", + "value": "money" + }, + { + "type": "literal", + "alias": "\"point\"", + "value": "point" + }, + { + "type": "literal", + "alias": "\"line\"", + "value": "line" + }, + { + "type": "literal", + "alias": "\"lseg\"", + "value": "lseg" + }, + { + "type": "literal", + "alias": "\"box\"", + "value": "box" + }, + { + "type": "literal", + "alias": "\"path\"", + "value": "path" + }, + { + "type": "literal", + "alias": "\"polygon\"", + "value": "polygon" + }, + { + "type": "literal", + "alias": "\"circle\"", + "value": "circle" + }, + { + "type": "literal", + "alias": "\"json\"", + "value": "json" + }, + { + "type": "literal", + "alias": "\"jsonb\"", + "value": "jsonb" + }, + { + "type": "literal", + "alias": "\"bool\"", + "value": "bool" + }, + { + "type": "literal", + "alias": "\"date\"", + "value": "date" + }, + { + "type": "literal", + "alias": "\"timestamp\"", + "value": "timestamp" + }, + { + "type": "literal", + "alias": "\"timestamptz\"", + "value": "timestamptz" + }, + { + "type": "literal", + "alias": "\"geometry\"", + "value": "geometry" + }, + { + "type": "literal", + "alias": "\"geography\"", + "value": "geography" + } + ] + }, + { + "type": "union", + "alias": "FullFilter | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "object", + "alias": "ComplexFilter", + "aliasSymbolescapedName": "ComplexFilter", + "comments": "Complex filter that allows applying functions to columns", + "properties": { + "$filter": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": false + } + } + }, + { + "type": "object", + "alias": "{ $and: FullFilter[]; }", + "properties": { + "$and": { + "type": "array", + "alias": "FullFilter[]", + "itemType": { + "type": "reference", + "alias": "FullFilter", + "aliasSymbolescapedName": "FullFilter", + "comments": "Group or simple filter" + }, + "optional": false + } + } + }, + { + "type": "object", + "alias": "{ $or: FullFilter[]; }", + "properties": { + "$or": { + "type": "reference", + "alias": "FullFilter[]", + "optional": false + } + } + }, + { + "type": "object", + "alias": "NormalFilter>", + "aliasSymbolescapedName": "NormalFilter", + "comments": "Column filter with operators\nMultiple columns are combined with AND", + "properties": { + "$filter": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + }, + { + "type": "primitive", + "alias": "ShorthandFilter>", + "aliasSymbolescapedName": "ShorthandFilter", + "comments": "Filters with shorthand notation for autocomplete convenience\nOperator is inside the key: ` \"{columnName}.{operator}\": value`", + "subType": "any" + }, + { + "type": "object", + "alias": "Partial<{ $exists: S extends DBSchema ? ExactlyOne<{ [tname in KeyofString]: FullFilter | { path: RawJoinPath[]; filter: FullFilter<...>; }; }> : any; $notExists: S extends DBSchema ? ExactlyOne<...> : any; $existsJoined: S extends DBSchema ? ExactlyOne<...> : any; $notExistsJoined: S exte...", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional", + "properties": { + "$exists": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$notExists": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$existsJoined": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "$notExistsJoined": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + } + } + } + ] + }, + { + "type": "union", + "alias": "FieldFilter | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "literal", + "alias": "\"\"", + "value": "" + }, + { + "type": "array", + "alias": "string[]", + "itemType": { + "type": "primitive", + "alias": "string", + "subType": "string" + } + }, + { + "type": "literal", + "alias": "\"*\"", + "value": "*" + }, + { + "type": "object", + "alias": "{ \"*\": 1; }", + "properties": { + "*": { + "type": "primitive", + "alias": "1", + "subType": "number", + "optional": false + } + } + }, + { + "type": "object", + "alias": "{ [x: string]: true | 1; }", + "properties": {} + }, + { + "type": "object", + "alias": "{ [x: string]: false | 0; }", + "properties": {} + } + ] + }, + { + "type": "object", + "alias": "SyncOptions", + "aliasSymbolescapedName": "SyncOptions", + "properties": { + "name": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "filter": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "onChange": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "onError": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "db": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pushDebounce": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "skipFirstTrigger": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "select": { + "type": "reference", + "alias": "FieldFilter | undefined", + "optional": true + }, + "storageType": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "patchText": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "patchJSON": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "onReady": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "skipIncomingDeltaCheck": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "onDebug": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "handlesOnData": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true + } + } + }, + { + "type": "object", + "alias": "SyncOneOptions", + "aliasSymbolescapedName": "SyncOneOptions", + "properties": { + "name": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "filter": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "onChange": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "onError": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "db": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "pushDebounce": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "skipFirstTrigger": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "select": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "storageType": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "patchText": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "patchJSON": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "onReady": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "skipIncomingDeltaCheck": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "onDebug": { + "type": "primitive", + "alias": "any", + "subType": "any", + "optional": true + }, + "handlesOnData": { + "type": "primitive", + "alias": "false", + "subType": "boolean", + "optional": true + } + } + } +] as const satisfies TS_Type[]; \ No newline at end of file diff --git a/documentation/utils/generateClientDocs.ts b/documentation/utils/generateClientDocs.ts new file mode 100644 index 00000000..408f73ab --- /dev/null +++ b/documentation/utils/generateClientDocs.ts @@ -0,0 +1,142 @@ +import * as fs from "fs"; +import * as path from "path"; +import { isDefined } from "prostgles-types"; +import { getResolvedTypes } from "./getResolvedTypes"; +import { getObjectEntries } from "prostgles-types"; +import { definitions } from "./clientTypes"; +import { TS_Function, TS_Type } from "./getSerializableType"; + +const testFolderPath = `${__dirname}/../../../tests/`; +const docsFolder = `${__dirname}/../../`; + +export const generateClientDocs = () => { + const clientFilePath = path.resolve( + `${testFolderPath}/client/node_modules/prostgles-client/dist/prostgles.d.ts` + ); + const excludedTypes = [ + "FullFilter", + "FullFilter | undefined", + "FieldFilter | undefined", + "SyncOptions", + "SyncOneOptions", + "PG_COLUMN_UDT_DATA_TYPE", + ]; + const { resolvedTypes, visitedMaps } = getResolvedTypes({ + filePath: clientFilePath, + filter: { + nodeNames: [ + // "ViewHandlerClient", + "TableHandlerClient", + ], + excludedTypes, + }, + }); + + const jsonTypes = JSON.stringify( + [ + ...resolvedTypes, + ...visitedMaps + .flatMap((m) => + Array.from(m.values()).map((v) => + excludedTypes.includes(v.resolvedType.alias ?? "") ? v.resolvedType : undefined + ) + ) + .filter(isDefined), + ] satisfies TS_Type[], + null, + 2 + ); + fs.writeFileSync( + `${__dirname}/../clientTypes.ts`, + [ + `import type { TS_Type } from "./getSerializableType";`, + `export const definitions = ${jsonTypes} as const satisfies TS_Type[];`, + ].join("\n"), + { + encoding: "utf-8", + } + ); + + const docPath = `${docsFolder}CLIENT.md`; + generateMDX(docPath); +}; + +const getAliasWithoutGenerics = (type: TS_Type) => { + if (type.type === "union") return type.types.map(getAliasWithoutGenerics).join(" | "); + return type.aliasSymbolescapedName || type.alias; +}; + +export const generateMDX = (filePath: string) => { + const tableHandler = definitions[0]; + const mdxContent = getObjectEntries(tableHandler.properties).map(([methodName, _methodInfo]) => { + const methodInfo = ( + _methodInfo.type === "function" ? + (_methodInfo as TS_Function) + // : _methodInfo.type === "union" ? _methodInfo.types.find((t) => t.type === "function") + : undefined) as TS_Function | undefined; + if (!methodInfo) return ""; + return [ + `## ${methodName}()`, + methodInfo.comments ?? "", + `\`\`\`typescript + ${methodName}: (${methodInfo.arguments + .map((arg) => `${arg.name}${arg.optional ? "?" : ""}: ${getAliasWithoutGenerics(arg)}`) + .join(", ")}): ${methodInfo.returnType.aliasSymbolescapedName || methodInfo.returnType.alias} + \`\`\``, + `#### Arguments`, + ``, + ...methodInfo.arguments.map((arg) => { + return renderType(arg, 2, { name: arg.name, optional: arg.optional }); + }), + `#### Return type`, + renderType(methodInfo.returnType, 0, undefined), + ].join("\n"); + }); + const result = mdxContent.join("\n\n"); + fs.writeFileSync(filePath, result, { encoding: "utf-8" }); +}; + +const renderType = ( + type: TS_Type, + indent = 2, + argOrProp: { name: string; optional: boolean } | undefined +): string => { + const indentText = " ".repeat(indent); + const title = `${indentText}${argOrProp?.name ? `- **${argOrProp.name}**: ` : ""}\`${ + type.aliasSymbolescapedName || type.alias + }\` ${type.comments ? ` - ${removeLineBreaks(type.comments)}` : ""}`; + if (type.type === "primitive" || type.type === "literal") { + return title; + } + if (type.type === "object") { + return ( + title + + `\n` + + getObjectEntries(type.properties) + .map(([name, p]) => renderType(p, indent + 2, { name, optional: p.optional })) + .join("\n") + ); + } + + if (type.type === "promise") { + const innerType = renderType(type.innerType, indent, argOrProp); + // if (argOrProp?.name) { + // return [title, innerType].join("\n"); + // } + return innerType; + } + + if (type.type === "array") { + return renderType(type.itemType, indent, argOrProp); + } + + return title; +}; + +const removeLineBreaks = (str = "") => + str + .split("\n") + .map((line) => { + if (!line.trim().endsWith(".")) return `${line}.`; + }) + .join(" "); diff --git a/documentation/utils/generateDocs.ts b/documentation/utils/generateDocs.ts new file mode 100644 index 00000000..29760a65 --- /dev/null +++ b/documentation/utils/generateDocs.ts @@ -0,0 +1,4 @@ +import { generateServerDocs } from "./generateServerDocs"; +import { generateClientDocs } from "./generateClientDocs"; +generateServerDocs(); +generateClientDocs(); diff --git a/documentation/utils/generateServerDocs.ts b/documentation/utils/generateServerDocs.ts new file mode 100644 index 00000000..41cf7a35 --- /dev/null +++ b/documentation/utils/generateServerDocs.ts @@ -0,0 +1,82 @@ +import * as fs from "fs"; +import * as path from "path"; +import { getResolvedTypes } from "./getResolvedTypes"; +import { definitions } from "./serverTypes"; +import { getObjectEntries } from "prostgles-types"; +import { TS_Type } from "./getSerializableType"; + +const testFolderPath = `${__dirname}/../../../tests/`; +const docsFolder = `${__dirname}/../../`; + +export const generateServerDocs = () => { + const serverFilePath = path.resolve( + // `${testFolderPath}/server/node_modules/prostgles-server/dist/DBSchemaBuilder.d.ts` // "DBOFullyTyped", + `${testFolderPath}/server/node_modules/prostgles-server/dist/ProstglesTypes.d.ts` // "ProstglesInitOptions", + ); + const { + resolvedTypes: [ProstglesInitOptions], + visitedMaps, + } = getResolvedTypes({ + filePath: serverFilePath, + filter: { + nodeNames: [ + "ProstglesInitOptions", + // "DBOFullyTyped", + ], + excludedTypes: [], + maxDepth: 2, + }, + }); + + if (!ProstglesInitOptions) throw new Error("ProstglesInitOptions not found"); + const prostglesInitOpts = definitions[0]; + const docs = [ + `# Overview`, + `Our Isomorphic Typescript API allows connecting to a PostgreSQL database to get a realtime view of the data and schema. Interact with the data with full end-to-end type safety.`, + `### Installation`, + `To install the package, run:`, + `\`\`\`bash`, + `npm install prostgles-server`, + `\`\`\``, + `### Configuration`, + `To get started, you need to provide a configuration object to the server.`, + ``, + `Basic example:`, + `\`\`\`typescript`, + `import prostgles from "prostgles-server";`, + `import { DBSchemaGenerated } from "./DBSchemaGenerated";`, + `prostgles({`, + ` dbConnection: {`, + ` host: "localhost",`, + ` port: 5432,`, + ` database: "postgres"`, + ` user: process.env.PRGL_USER,`, + ` password: process.env.PRGL_PWD`, + ` },`, + ` tsGeneratedTypesDir: __dirname,`, + ` onReady: async ({ dbo }) => {`, + ` try {`, + ` await dbo.items.insert({ name: "a" });`, + ` console.log(await dbo.items.find());`, + ` } catch(err) {`, + ` console.error(err)`, + ` }`, + ` },`, + `});`, + `\`\`\``, + `### Configuration options`, + ...getObjectEntries(prostglesInitOpts.properties).map(([propName, prop]) => { + const title = ` - ${propName} \`${(prop as TS_Type).aliasSymbolescapedName || (prop as TS_Type).alias}\``; + const comments = (prop as TS_Type).comments || ""; + if (!comments) return title; + return [title, ` ` + comments + " "].join("\n"); + }), + ].join("\n"); + + const serverTypesStr = [ + `import type { TS_Type } from "./getSerializableType";`, + `export const definitions = ${JSON.stringify([ProstglesInitOptions], null, 2)} as const satisfies TS_Type[];`, + ].join("\n"); + fs.writeFileSync(`${docsFolder}/utils/serverTypes.ts`, serverTypesStr, { encoding: "utf-8" }); + fs.writeFileSync(`${docsFolder}SERVER.md`, docs, { encoding: "utf-8" }); +}; diff --git a/documentation/utils/getResolvedTypes.ts b/documentation/utils/getResolvedTypes.ts new file mode 100644 index 00000000..0419d46e --- /dev/null +++ b/documentation/utils/getResolvedTypes.ts @@ -0,0 +1,45 @@ +import * as ts from "typescript"; +import { getSerializableType, TS_Type, VisitedTypesMap } from "./getSerializableType"; +import { loadTsFile } from "./loadTsFile"; + +type Args = { + filePath: string; + filter?: { + nodeNames: string[]; + excludedTypes: string[]; + maxDepth?: number; + }; +}; +export const getResolvedTypes = ({ filePath, filter }: Args) => { + const { checker, sourceFile } = loadTsFile(filePath); + + const results: TS_Type[] = []; + const visitedMaps: VisitedTypesMap[] = []; + + const visit = (node: ts.Node) => { + // const nodeText = node.getText(); + // if (nodeText.includes("DBOFullyTyped")) { + // console.log("node.name.text", node.getText()); + // } + if (ts.isTypeAliasDeclaration(node)) { + if (!filter || filter?.nodeNames.includes(node.name.text)) { + const type1 = checker.getTypeAtLocation(node.type); + const { resolvedType, visited } = getSerializableType( + type1, + checker, + undefined, + [], + filter, + 0 + ); + results.push(resolvedType); + visitedMaps.push(visited); + } + } + + ts.forEachChild(node, visit); + }; + + visit(sourceFile); + return { resolvedTypes: results, visitedMaps }; +}; diff --git a/documentation/utils/getSerializableType.ts b/documentation/utils/getSerializableType.ts new file mode 100644 index 00000000..601ec19b --- /dev/null +++ b/documentation/utils/getSerializableType.ts @@ -0,0 +1,479 @@ +import * as ts from "typescript"; +import { AnyObject, isDefined } from "prostgles-types"; + +type TS_Base = { + alias?: string; + aliasSymbolescapedName?: string; + intersectionParent?: string; + comments?: string; +}; + +export type TS_Literal = TS_Base & { + type: "literal"; + value: string; +}; + +export type TS_Primitive = TS_Base & { + type: "primitive"; + subType: "string" | "number" | "boolean" | "any" | "null" | "undefined"; +}; + +export type TS_Object = TS_Base & { + type: "object"; + properties: Record; +}; + +export type TS_Array = TS_Base & { + type: "array"; + itemType: TS_Type; +}; + +export type TS_Tuple = TS_Base & { + type: "tuple"; + itemTypes: TS_Type[]; +}; + +export type TS_Function = TS_Base & { + type: "function"; + arguments: (TS_Type & { name: string; optional: boolean })[]; + returnType: TS_Type; +}; + +export type TS_Promise = TS_Base & { + type: "promise"; + innerType: Exclude; +}; + +export type TS_Union = TS_Base & { + type: "union"; + types: TS_Type[]; +}; + +export type TS_Reference = TS_Base & { + type: "reference"; + alias: string; +}; + +export type TS_Type = + | TS_Primitive + | TS_Literal + | TS_Object + | TS_Array + | TS_Tuple + | TS_Function + | TS_Union + | TS_Promise + | TS_Reference; + +export type VisitedTypesMap = Map< + string, + { resolvedType: TS_Type; type: ts.Type; referenceType?: TS_Type } +>; + +export const getSerializableType = ( + myType: ts.Type, + checker: ts.TypeChecker, + visited: VisitedTypesMap = new Map(), + parentAliases: string[], + opts: ResolveTypeOptions | undefined, + depth: number +): { resolvedType: TS_Type; visited: VisitedTypesMap } => { + let alias = "unknown"; + const { escapedName } = myType.aliasSymbol ?? {}; + const aliasSymbolescapedName = escapedName?.toString(); + const symbol = myType.aliasSymbol ?? myType.symbol; + const comments = symbol ? getSymbolComments(symbol, checker) : undefined; + try { + alias = checker.typeToString(myType); + } catch (e) { + console.log("Error resolving type", myType, e); + } + + /* Circular resolved type */ + const visitedType = visited.get(alias); + if (visitedType) { + return { resolvedType: visitedType.referenceType ?? visitedType.resolvedType, visited }; + } + const withAlias = (_type: TS_Type) => { + const finalComments = _type.comments || comments; + const resolvedType: TS_Type = sortObjectsByKeyOrder( + { + alias: _type.alias ?? alias, + aliasSymbolescapedName, + ..._type, + ...(finalComments && { comments: finalComments }), + }, + ["type", "alias", "aliasSymbolescapedName", "comments"] + ); + + let referenceType: TS_Type | undefined; + if ( + opts?.excludedTypes.includes(alias) || + (escapedName && opts?.excludedTypes.includes(escapedName)) || + (opts?.maxDepth !== undefined && depth >= opts.maxDepth) + ) { + referenceType = + resolvedType.type === "primitive" ? + resolvedType + : { + type: "reference", + alias, + aliasSymbolescapedName, + comments: resolvedType.comments, + }; + } + + visited.set(alias, { resolvedType, referenceType, type: myType }); + + return { resolvedType: referenceType ?? resolvedType, visited }; + }; + + const unresolvedParentAliases = parentAliases?.filter((a) => !visited.get(a)) ?? []; + + /* Circular unresolved type */ + if (unresolvedParentAliases.includes(alias)) { + return withAlias({ + type: "reference", + alias, + }); + } + const nextUnresolvedParentAliases = unresolvedParentAliases.concat(alias); + + // if (alias === "[FullFilter, Partial>][]") { + // debugger; + // } + + if (myType.isUnion()) { + const unionTypes = myType.types.map((t) => { + const resolvedUnionMember = getSerializableType( + t, + checker, + visited, + nextUnresolvedParentAliases, + opts, + depth + 1 + ); + return resolvedUnionMember.resolvedType; + }); + + /** + * "boolean | undefined" ends up in types as + * { intrinsicName: "true" } | { intrinsicName: "false" } | { intrinsicName: "undefined" } + * So we need to check for "true" and "false" and merge them into "boolean" + */ + const booleanTypes = unionTypes.filter( + (t) => t.type === "primitive" && t.subType === "boolean" + ); + const dedupedTypes = + booleanTypes.length > 1 ? + unionTypes + .filter((t) => t.type !== "primitive" || t.subType !== "boolean") + .concat(booleanTypes[0]!) + : unionTypes; + + const result = withAlias({ + type: "union", + types: dedupedTypes, + }); + + return result; + } + + if ((myType.flags & ts.TypeFlags.String) !== 0) { + return withAlias({ type: "primitive", subType: "string" }); + } + + if ((myType.flags & ts.TypeFlags.NumberLike) !== 0) { + return withAlias({ type: "primitive", subType: "number" }); + } + + if ((myType.flags & ts.TypeFlags.BooleanLike) !== 0) { + return withAlias({ type: "primitive", subType: "boolean" }); + } + + if ((myType.flags & ts.TypeFlags.Null) !== 0) { + return withAlias({ type: "primitive", subType: "null" }); + } + + if ((myType.flags & ts.TypeFlags.Undefined) !== 0) { + return withAlias({ type: "primitive", subType: "undefined" }); + } + + if (checker.isArrayType(myType)) { + const itemType = checker.getTypeArguments(myType as ts.TypeReference)[0]; + if (itemType && checker.isTupleType(itemType)) { + const tupleTypes = + (itemType as unknown as { resolvedTypeArguments: ts.Type[] }).resolvedTypeArguments.map( + (d: ts.Type) => { + return getSerializableType( + d, + checker, + visited, + nextUnresolvedParentAliases, + opts, + depth + 1 + ).resolvedType; + } + ) ?? []; + return withAlias({ + type: "tuple", + itemTypes: tupleTypes, + }); + } + const resolvedItemType: TS_Type = + itemType ? + getSerializableType( + itemType, + checker, + visited, + nextUnresolvedParentAliases, + opts, + depth + 1 + ).resolvedType + : { type: "primitive", subType: "any" }; + + return withAlias({ + type: "array", + itemType: resolvedItemType, + }); + } + + const [firstSignature] = myType.getCallSignatures(); + if (firstSignature) { + const parameters = firstSignature.parameters + .map((param) => { + const { valueDeclaration } = param; + if (!valueDeclaration) return undefined; + const paramType = checker.getTypeOfSymbolAtLocation(param, valueDeclaration); + const resolvedParamType = getSerializableType( + paramType, + checker, + visited, + nextUnresolvedParentAliases, + opts, + depth + 1 + ).resolvedType; + const paramComments = getSymbolComments(param, checker); + const optional = Boolean( + (ts as any).isParameterDeclaration(param.valueDeclaration) && + checker.isOptionalParameter(valueDeclaration as ts.ParameterDeclaration) + ); + const name = param.escapedName.toString() || param.name; + const resolvedParam = + optional ? simplifyUnionForOptionalType(resolvedParamType) : resolvedParamType; + return { + name, + optional, + ...resolvedParam, + comments: resolvedParam.comments || paramComments, + }; + }) + .filter(isDefined); + const returnType = getSerializableType( + checker.getReturnTypeOfSignature(firstSignature), + checker, + visited, + nextUnresolvedParentAliases, + opts, + depth + 1 + ).resolvedType; + + return withAlias({ + type: "function", + arguments: parameters, + returnType, + }); + } + + if ((myType.flags & ts.TypeFlags.Object) !== 0) { + const objectType = myType as ts.ObjectType; + + if (objectType.objectFlags & ts.ObjectFlags.Reference) { + const typeReference = objectType as ts.TypeReference; + const target = typeReference.target; + + if (target.symbol?.escapedName === "Promise") { + const _innerType = checker.getTypeArguments(typeReference)[0]; + const defaultType: TS_Type = { type: "primitive", subType: "any" }; + const resolvedInnerType = + _innerType && + getSerializableType( + _innerType, + checker, + visited, + nextUnresolvedParentAliases, + opts, + depth + 1 + ).resolvedType; + const innerType: Exclude = + resolvedInnerType?.type === "promise" ? defaultType : (resolvedInnerType ?? defaultType); + return withAlias({ + type: "promise", + innerType, + }); + } + } + + const properties: TS_Object["properties"] = {}; + myType.getProperties().forEach((symbol) => { + const propertyType = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration!); + const resolvedPropertyType = getSerializableType( + propertyType, + checker, + visited, + nextUnresolvedParentAliases, + opts, + depth + 1 + ).resolvedType; + const propertyComments = resolvedPropertyType.comments || getSymbolComments(symbol, checker); + const optional = Boolean(symbol.flags & ts.SymbolFlags.Optional); + properties[symbol.name] = { + ...(optional ? simplifyUnionForOptionalType(resolvedPropertyType) : resolvedPropertyType), + optional, + comments: propertyComments || undefined, + }; + }); + const comments = + myType.aliasSymbol?.declarations && + getNonInternalTSDeclarations(myType.aliasSymbol?.declarations) + //@ts-ignore + ?.flatMap((d) => (!d.jsDoc ? [] : d.jsDoc.map((jd) => jd.comment))) + .join("\n"); + return withAlias({ + type: "object", + properties, + comments, + }); + } + + /** + * A & B + */ + if (myType.isIntersection()) { + const intersectionTypes = myType.types.map((t) => { + const { resolvedType: intersectionType } = getSerializableType( + t, + checker, + visited, + nextUnresolvedParentAliases, + opts, + depth + 1 + ); + intersectionType.intersectionParent = aliasSymbolescapedName || alias; + return intersectionType; + }); + if (intersectionTypes.every((t) => t.type === "object")) { + const properties = (intersectionTypes as TS_Object[]).reduce((acc, t) => { + return { ...acc, ...t.properties }; + }, {}); + return withAlias({ + type: "object", + properties, + }); + } + return withAlias({ + type: "union", + types: intersectionTypes, + }); + } + + /** + * Type parameter. E.g.: + * function example(arg: T){ } + */ + if (myType.isTypeParameter()) { + const extendedType = myType.getConstraint(); + if (extendedType) { + const { resolvedType: resolvedExtendedType } = getSerializableType( + extendedType, + checker, + visited, + nextUnresolvedParentAliases, + opts, + depth + 1 + ); + return withAlias(resolvedExtendedType); + } + } + + if (myType.isStringLiteral()) { + return withAlias({ type: "literal", value: myType.value }); + } + + return withAlias({ type: "primitive", subType: "any" }); +}; + +type ResolveTypeOptions = { + excludedTypes: string[]; + maxDepth?: number; +}; +export const resolveType = ( + myType: ts.Type, + checker: ts.TypeChecker, + opts: ResolveTypeOptions +): { resolvedType: TS_Type; constituentTypes?: TS_Type[] } => { + const { resolvedType } = getSerializableType(myType, checker, undefined, [], opts, 0); + if (resolvedType.type === "reference" && opts.excludedTypes.includes(resolvedType.alias)) { + return { resolvedType: { type: "primitive", subType: "any" } }; + } + return { resolvedType, constituentTypes: [] }; +}; + +const getNonInternalTSDeclarations = (declarations: ts.Declaration[]): ts.Declaration[] => { + return declarations.filter((d) => { + return !d.getSourceFile().fileName.includes("/node_modules/typescript/"); + }); +}; + +const sortObjectsByKeyOrder = ( + obj: T, + keyOrder: K[] +): T => { + const newKeyOrder = arraySort(Object.keys(obj), keyOrder); + + return newKeyOrder.reduce( + (acc, key) => ({ + ...acc, + [key]: obj[key], + }), + {} as T + ); +}; + +const arraySort = (arrayToSort: string[], keyOrder: string[]): string[] => { + const keyOrderMap = new Map(keyOrder.map((value, index) => [value, index])); + + return arrayToSort.sort((a, b) => { + const aIndex = keyOrderMap.get(a) ?? Infinity; + const bIndex = keyOrderMap.get(b) ?? Infinity; + + return aIndex - bIndex; + }); +}; + +const simplifyUnionForOptionalType = (resolvedParamType: TS_Type) => { + if (resolvedParamType.type === "union" && resolvedParamType.types.length === 2) { + const indexOfUndefined = resolvedParamType.types.findIndex( + (t) => t.type === "primitive" && t.subType === "undefined" + ); + if (indexOfUndefined === -1) return resolvedParamType; + + const unionTypes = resolvedParamType.types; + const nonUndefined = unionTypes.find((_, i) => indexOfUndefined !== i); + if ( + nonUndefined && + (nonUndefined.type !== "primitive" || nonUndefined.subType !== "undefined") + ) { + return nonUndefined; + } + } + return resolvedParamType; +}; + +const getSymbolComments = (symbol: ts.Symbol, checker: ts.TypeChecker): string => { + const comments = symbol.getDocumentationComment(checker); + return comments + .map((comment) => comment.text) + .join(" ") + .trim(); +}; diff --git a/documentation/utils/loadTsFile.ts b/documentation/utils/loadTsFile.ts new file mode 100644 index 00000000..ac338562 --- /dev/null +++ b/documentation/utils/loadTsFile.ts @@ -0,0 +1,45 @@ +import * as path from "path"; +import * as ts from "typescript"; + +export const loadTsFile = (filePath: string) => { + // Normalize file path + const absolutePath = path.resolve(filePath); + + // Create compiler host and program + const configPath = ts.findConfigFile( + path.dirname(absolutePath), + ts.sys.fileExists, + "tsconfig.json" + ); + + if (!configPath) { + throw new Error("Could not find a valid 'tsconfig.json'."); + } + + // Parse the config file + const configFile = ts.readConfigFile(configPath, ts.sys.readFile); + const parsedConfig = ts.parseJsonConfigFileContent( + configFile.config, + ts.sys, + path.dirname(configPath) + ); + + // Create program + const program = ts.createProgram({ + rootNames: [absolutePath], + options: parsedConfig.options, + }); + + const checker = program.getTypeChecker(); + const sourceFile = program.getSourceFile(absolutePath); + + if (!sourceFile) { + throw new Error(`Could not find source file: ${absolutePath}`); + } + + return { + program, + checker, + sourceFile, + }; +}; diff --git a/documentation/utils/moduleResolver.ts b/documentation/utils/moduleResolver.ts new file mode 100644 index 00000000..44e81d6d --- /dev/null +++ b/documentation/utils/moduleResolver.ts @@ -0,0 +1,31 @@ +import * as ts from "typescript"; + +/** + * Resolves a moduleSpecifier to its resolved file path using TypeScript's module resolution. + * + * @param moduleSpecifier - The module path (e.g., "./Auth"). + * @param currentFilePath - The path of the current file where the module is imported. + * @returns The resolved file path, or `undefined` if not found. + */ +export const resolveModuleWithTypescript = (moduleSpecifier: string, currentFilePath: string) => { + const compilerOptions: ts.CompilerOptions = { + moduleResolution: ts.ModuleResolutionKind.Node16, + baseUrl: "./", // Adjust as needed + }; + const host: ts.ModuleResolutionHost = { + fileExists: ts.sys.fileExists, + readFile: ts.sys.readFile, + directoryExists: ts.sys.directoryExists, + getCurrentDirectory: ts.sys.getCurrentDirectory, + getDirectories: ts.sys.getDirectories, + }; + + const { resolvedModule } = ts.resolveModuleName( + moduleSpecifier, + currentFilePath, + compilerOptions, + host + ); + + return resolvedModule?.resolvedFileName; +}; diff --git a/documentation/utils/package-lock.json b/documentation/utils/package-lock.json new file mode 100644 index 00000000..521f1cab --- /dev/null +++ b/documentation/utils/package-lock.json @@ -0,0 +1,75 @@ +{ + "name": "generate-docs", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "generate-docs", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@types/node": "^20.10.0" + } + }, + "..": { + "name": "prostgles-server", + "version": "4.2.179", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@aws-sdk/client-ses": "^3.699.0", + "@aws-sdk/credential-provider-node": "^3.699.0", + "@types/passport": "^1.0.17", + "@types/passport-facebook": "^3.0.3", + "@types/passport-github2": "^1.2.9", + "@types/passport-google-oauth20": "^2.0.16", + "@types/passport-microsoft": "^1.0.3", + "body-parser": "^1.20.3", + "check-disk-space": "^3.4.0", + "file-type": "^18.5.0", + "nodemailer": "^6.9.16", + "passport": "^0.7.0", + "passport-facebook": "^3.0.0", + "passport-github2": "^0.1.12", + "passport-google-oauth20": "^2.0.0", + "passport-microsoft": "^2.1.0", + "pg": "^8.11.5", + "pg-cursor": "^2.11.0", + "pg-promise": "^11.9.1", + "prostgles-types": "^4.0.115" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/json-schema": "^7.0.15", + "@types/node": "^22.8.1", + "@types/nodemailer": "^6.4.17", + "@types/pg": "^8.11.5", + "@types/pg-cursor": "^2.7.2", + "@types/sharp": "^0.30.4", + "@typescript-eslint/eslint-plugin": "^8.15.0", + "@typescript-eslint/parser": "^8.15.0", + "eslint": "^8.51.0", + "socket.io": "^4.8.1", + "typescript": "^5.3.3" + } + }, + "node_modules/@types/node": { + "version": "20.17.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.10.tgz", + "integrity": "sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/documentation/utils/package.json b/documentation/utils/package.json new file mode 100644 index 00000000..9baf3d43 --- /dev/null +++ b/documentation/utils/package.json @@ -0,0 +1,14 @@ +{ + "name": "generate-docs", + "version": "1.0.0", + "description": "", + "main": "generateDocs.js", + "scripts": { + "start": "npm i && tsc && node dist/generateDocs.js" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@types/node": "^20.10.0" + } +} diff --git a/documentation/utils/serverTypes.ts b/documentation/utils/serverTypes.ts new file mode 100644 index 00000000..e80a8ef0 --- /dev/null +++ b/documentation/utils/serverTypes.ts @@ -0,0 +1,345 @@ +import type { TS_Type } from "./getSerializableType"; +export const definitions = [ + { + "type": "object", + "alias": "ProstglesInitOptions", + "aliasSymbolescapedName": "ProstglesInitOptions", + "comments": "", + "properties": { + "dbConnection": { + "type": "union", + "alias": "DbConnection", + "aliasSymbolescapedName": "DbConnection", + "comments": "Database connection details", + "types": [ + { + "type": "primitive", + "alias": "string", + "subType": "string" + }, + { + "type": "reference", + "alias": "IConnectionParameters" + } + ], + "optional": false + }, + "onReady": { + "type": "function", + "alias": "OnReadyCallback", + "aliasSymbolescapedName": "OnReadyCallback", + "arguments": [ + { + "name": "params", + "optional": false, + "type": "reference", + "alias": "OnReadyParams", + "aliasSymbolescapedName": "OnReadyParams", + "comments": "" + } + ], + "returnType": { + "type": "primitive", + "alias": "any", + "subType": "any" + }, + "optional": false, + "comments": "Called when the prostgles server is ready to accept connections.\nIt waits for auth, tableConfig and other async configurations to complete before executing" + }, + "dbOptions": { + "type": "reference", + "alias": "IDefaults", + "optional": true + }, + "tsGeneratedTypesDir": { + "type": "reference", + "alias": "string | undefined", + "comments": "If defined then a `DBSchemaGenerated.d.ts` file will be created in the provided directory.\nThis file exports a `DBSchemaGenerated` type which contains types for the database tables and\ncan be used as a generic type input for the prostgles instances to ensure type safety", + "optional": true + }, + "disableRealtime": { + "type": "reference", + "alias": "boolean | undefined", + "comments": "If true then schema watch, subscriptions and syncs will be disabled.\nNo `prostgles` schema will be created which is needed for the realtime features.\nThis is useful when you want to connect to a database and prevent any changes to the schema", + "optional": true + }, + "io": { + "type": "reference", + "alias": "Server", + "comments": "Socket.IO server instance object", + "optional": true + }, + "publish": { + "type": "union", + "alias": "Publish | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "null", + "subType": "null" + }, + { + "type": "reference", + "alias": "\"*\"" + }, + { + "type": "primitive", + "alias": "PublishFullyTyped", + "aliasSymbolescapedName": "PublishFullyTyped", + "subType": "any" + }, + { + "type": "reference", + "alias": "(params: PublishParams) => Awaitable>" + }, + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": true, + "comments": "Data access rules applied to clients.\nBy default, nothing is allowed." + }, + "testRulesOnConnect": { + "type": "reference", + "alias": "boolean | undefined", + "comments": "If true then will test all table methods on each socket connect.\nNot recommended for production", + "optional": true + }, + "publishMethods": { + "type": "reference", + "alias": "PublishMethods", + "aliasSymbolescapedName": "PublishMethods", + "comments": "Custom methods that can be called from the client", + "optional": true + }, + "publishRawSQL": { + "type": "reference", + "alias": "(params: PublishParams) => boolean | \"*\" | Promise", + "comments": "If defined and resolves to true then the connected client can run SQL queries", + "optional": true + }, + "joins": { + "type": "union", + "alias": "Joins | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "reference", + "alias": "Join[]" + }, + { + "type": "reference", + "alias": "\"inferred\"" + } + ], + "optional": true, + "comments": "Allows defining joins between tables:\n - `infered` - uses the foreign keys to infer the joins\n - `Join[]` - specifies the joins manually" + }, + "schema": { + "type": "union", + "alias": "Record | Record | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "reference", + "alias": "Record", + "aliasSymbolescapedName": "Record", + "comments": "Construct a type with a set of properties K of type T" + }, + { + "type": "reference", + "alias": "Record", + "aliasSymbolescapedName": "Record", + "comments": "Construct a type with a set of properties K of type T" + } + ], + "optional": true, + "comments": "If defined then the specified schemas are included/excluded from the prostgles schema.\nBy default the `public` schema is included." + }, + "sqlFilePath": { + "type": "reference", + "alias": "string | undefined", + "comments": "Path to a SQL file that will be executed on startup (but before onReady)", + "optional": true + }, + "transactions": { + "type": "union", + "alias": "string | boolean | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "primitive", + "alias": "string", + "subType": "string" + }, + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": true + }, + "wsChannelNamePrefix": { + "type": "reference", + "alias": "string | undefined", + "optional": true + }, + "onSocketConnect": { + "type": "reference", + "alias": "(args: AuthRequestParams & { socket: PRGLIOSocket; }) => void | Promise", + "comments": "Called when a socket connects\nUse for connection verification. Will disconnect socket on any errors", + "optional": true + }, + "onSocketDisconnect": { + "type": "reference", + "alias": "(args: AuthRequestParams & { socket: PRGLIOSocket; }) => void | Promise", + "comments": "Called when a socket disconnects", + "optional": true + }, + "auth": { + "type": "reference", + "alias": "Auth", + "aliasSymbolescapedName": "Auth", + "comments": "Auth configuration.\nSupports email and OAuth strategies", + "optional": true + }, + "DEBUG_MODE": { + "type": "reference", + "alias": "boolean | undefined", + "optional": true + }, + "onQuery": { + "type": "reference", + "alias": "(error: any, ctx: IEventContext) => void", + "comments": "Callback called when a query is executed.\nUseful for logging or debugging", + "optional": true + }, + "watchSchemaType": { + "type": "union", + "alias": "\"DDL_trigger\" | \"prostgles_queries\" | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "reference", + "alias": "\"DDL_trigger\"" + }, + { + "type": "reference", + "alias": "\"prostgles_queries\"" + } + ], + "optional": true + }, + "watchSchema": { + "type": "union", + "alias": "boolean | EventTriggerTagFilter | \"hotReloadMode\" | OnSchemaChangeCallback | undefined", + "types": [ + { + "type": "primitive", + "alias": "undefined", + "subType": "undefined" + }, + { + "type": "reference", + "alias": "\"*\"" + }, + { + "type": "reference", + "alias": "Partial>", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional" + }, + { + "type": "reference", + "alias": "Partial>", + "aliasSymbolescapedName": "Partial", + "comments": "Make all properties in T optional" + }, + { + "type": "reference", + "alias": "\"hotReloadMode\"" + }, + { + "type": "reference", + "alias": "OnSchemaChangeCallback", + "aliasSymbolescapedName": "OnSchemaChangeCallback" + }, + { + "type": "primitive", + "alias": "false", + "subType": "boolean" + } + ], + "optional": true, + "comments": "If truthy then DBSchemaGenerated.d.ts will be updated\nand \"onReady\" will be called with new schema on both client and server" + }, + "keywords": { + "type": "reference", + "alias": "Keywords", + "aliasSymbolescapedName": "Keywords", + "optional": true + }, + "onNotice": { + "type": "reference", + "alias": "(notice: AnyObject, message?: string | undefined) => void", + "optional": true + }, + "fileTable": { + "type": "reference", + "alias": "FileTableConfig", + "aliasSymbolescapedName": "FileTableConfig", + "comments": "Enables file storage and serving.\nCurrently supports saving files locally or to AWS S3", + "optional": true + }, + "restApi": { + "type": "reference", + "alias": "RestApiConfig", + "aliasSymbolescapedName": "RestApiConfig", + "comments": "Rest API configuration.\nThe REST API allows interacting with the database similarly to the socket connection\nwith the exception of subscriptions and realtime features", + "optional": true + }, + "tableConfig": { + "type": "reference", + "alias": "TableConfig", + "aliasSymbolescapedName": "TableConfig", + "comments": "A simple way of defining tables through a JSON-schema like object.\nAllowes adding runtime JSONB validation and type safety.\nShould be used with caution because it tends to revert any changes\nmade to the database schema through SQL queries", + "optional": true + }, + "tableConfigMigrations": { + "type": "reference", + "alias": "{ silentFail?: boolean | undefined; version: number; versionTableName?: string | undefined; onMigrate: (args: { db: DB; oldVersion: number | undefined; getConstraints: (table: string, column?: string | undefined, types?: (\"c\" | ... 2 more ... | \"f\")[] | undefined) => Promise<...>; }) => void; }", + "optional": true + }, + "onLog": { + "type": "reference", + "alias": "(evt: EventInfo) => Promise", + "comments": "Usefull for logging or debugging", + "optional": true + } + } + } +] as const satisfies TS_Type[]; \ No newline at end of file diff --git a/documentation/utils/tsconfig.json b/documentation/utils/tsconfig.json new file mode 100644 index 00000000..5394fe93 --- /dev/null +++ b/documentation/utils/tsconfig.json @@ -0,0 +1,28 @@ +{ + "files": ["generateDocs.ts"], + "compilerOptions": { + "baseUrl": ".", + "target": "es2019", + "lib": [ + "es6", + "dom", + "es2017", + "es2019", + ], + "strict": true, + "module": "commonjs", + "resolveJsonModule": true, + "rootDir": "./", + "outDir": "dist", + "noImplicitAny": false, + "declaration": true, + "strictNullChecks": true, + "declarationMap": true, + "ignoreDeprecations": "5.0", + "skipLibCheck": true, + "noUncheckedIndexedAccess": true, + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/examples/full-example-typescript/DBoGenerated.d.ts b/examples/full-example-typescript/DBoGenerated.d.ts index 19cd4c81..a9f4f925 100644 --- a/examples/full-example-typescript/DBoGenerated.d.ts +++ b/examples/full-example-typescript/DBoGenerated.d.ts @@ -15,13 +15,13 @@ export declare type SelectParams = { export declare type UpdateParams = { returning?: FieldFilter; onConflictDoNothing?: boolean; - fixIssues?: boolean; + removeDisallowedFields?: boolean; multi?: boolean; }; export declare type InsertParams = { returning?: FieldFilter; onConflictDoNothing?: boolean; - fixIssues?: boolean; + removeDisallowedFields?: boolean; }; export declare type DeleteParams = { returning?: FieldFilter; diff --git a/examples/full-example-typescript/DBoGenerated.ts b/examples/full-example-typescript/DBoGenerated.ts index a924a002..bb96847d 100644 --- a/examples/full-example-typescript/DBoGenerated.ts +++ b/examples/full-example-typescript/DBoGenerated.ts @@ -15,13 +15,13 @@ export type SelectParams = { export type UpdateParams = { returning?: FieldFilter; onConflictDoNothing?: boolean; - fixIssues?: boolean; + removeDisallowedFields?: boolean; multi?: boolean; } export type InsertParams = { returning?: FieldFilter; onConflictDoNothing?: boolean; - fixIssues?: boolean; + removeDisallowedFields?: boolean; } export type DeleteParams = { returning?: FieldFilter; diff --git a/examples/full-example-typescript/index.ts b/examples/full-example-typescript/index.ts index aa3d628f..601f3ec2 100644 --- a/examples/full-example-typescript/index.ts +++ b/examples/full-example-typescript/index.ts @@ -1,54 +1,48 @@ -import express from 'express'; +import express from "express"; +import path from "path"; +import prostgles from "prostgles-server"; const app = express(); -import path from 'path'; -var http = require('http').createServer(app); +var http = require("http").createServer(app); var io = require("socket.io")(http); http.listen(30009); -import prostgles from "prostgles-server" import { DBObj } from "./DBoGenerated"; - prostgles({ - dbConnection: { - host: "localhost", - port: 5432, - database: "example", - user: process.env.PRGL_USER, - password: process.env.PRGL_PWD - }, - - sqlFilePath: path.join(__dirname+'/init.sql'), - io, - tsGeneratedTypesDir: path.join(__dirname + '/'), - publish: () => { + dbConnection: { + host: "localhost", + port: 5432, + database: "example", + user: process.env.PRGL_USER, + password: process.env.PRGL_PWD, + }, + + sqlFilePath: path.join(__dirname + "/init.sql"), + io, + tsGeneratedTypesDir: path.join(__dirname + "/"), + publish: () => { + return { + planes: "*", + }; + }, + publishMethods: ({ dbo }) => { + return { + insertPlanes: async (data) => { + let res = await dbo.planes.insert(data); + return res; + }, + }; + }, + + onReady: async (dbo: DBObj) => { + let plane = await dbo.planes.findOne(); - return { - planes: "*" - } - }, - publishMethods: ({ dbo }) => { - return { - insertPlanes: async (data) => { - // let tl = Date.now(); - let res = await (dbo.planes).insert(data); - // console.log(Date.now() - tl, "ms"); - return res; - } - } - }, - - onReady: async (dbo: DBObj) => { + app.get("/", (req, res) => { + res.sendFile(path.join(__dirname + "/home.html")); + }); - let plane = await dbo.planes.findOne(); - - - app.get('/', (req, res) => { - res.sendFile(path.join(__dirname+'/home.html')); - }); - - app.get('*', function(req, res){ - res.status(404).send('Page not found'); - }); - }, -}); \ No newline at end of file + app.get("*", function (req, res) { + res.status(404).send("Page not found"); + }); + }, +}); diff --git a/examples/full-example-vanilla/index.js b/examples/full-example-vanilla/index.js index 1000098f..0d5823ba 100644 --- a/examples/full-example-vanilla/index.js +++ b/examples/full-example-vanilla/index.js @@ -1,59 +1,57 @@ -const express = require('express'); +const express = require("express"); const app = express(); -const path = require('path'); -var http = require('http').createServer(app); -var io = require('socket.io')(http, { path: "/s" }); +const path = require("path"); +var http = require("http").createServer(app); +var io = require("socket.io")(http, { path: "/s" }); http.listen(3001); -let prostgles = require('prostgles-server'); - +let prostgles = require("prostgles-server"); prostgles({ - dbConnection: { - host: "localhost", - port: "5432", - user: process.env.PRGL_USER, - password: process.env.PRGL_PWD - }, - dbOptions: { - application_name: "prostgles_api", - max: 100, - poolIdleTimeout: 10000 - }, - sqlFilePath: path.join(__dirname+'/init.sql'), - - io, - - onReady: async () => { - app.get('*', function(req, res){ - console.log(req.originalUrl) - res.sendFile(path.join(__dirname+'/home.html')); - }); - }, + dbConnection: { + host: "localhost", + port: "5432", + user: process.env.PRGL_USER, + password: process.env.PRGL_PWD, + }, + dbOptions: { + application_name: "prostgles_api", + max: 100, + poolIdleTimeout: 10000, + }, + sqlFilePath: path.join(__dirname + "/init.sql"), + + io, + + onReady: async () => { + app.get("*", function (req, res) { + console.log(req.originalUrl); + res.sendFile(path.join(__dirname + "/home.html")); + }); + }, - publish: () => { - return { - - Points: { - select: "*", - insert: "*", - update: "*", - delete: "*", - sync: { - synced_field: "Synced", - id_fields: ["id"] - } - }, - lines: { - select: "*", - insert: "*", - update: "*", - delete: "*", - sync: { - synced_field: "Synced", - id_fields: ["id"] - } - } - } - }, -}); \ No newline at end of file + publish: () => { + return { + Points: { + select: "*", + insert: "*", + update: "*", + delete: "*", + sync: { + synced_field: "Synced", + id_fields: ["id"], + }, + }, + lines: { + select: "*", + insert: "*", + update: "*", + delete: "*", + sync: { + synced_field: "Synced", + id_fields: ["id"], + }, + }, + }; + }, +}); diff --git a/examples/server/typescript/index.d.ts b/examples/server/typescript/index.d.ts deleted file mode 100644 index e26a57a8..00000000 --- a/examples/server/typescript/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/examples/server/typescript/index.d.ts.map b/examples/server/typescript/index.d.ts.map deleted file mode 100644 index 82335e72..00000000 --- a/examples/server/typescript/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/examples/server/typescript/index.js b/examples/server/typescript/index.js deleted file mode 100644 index 919a547f..00000000 --- a/examples/server/typescript/index.js +++ /dev/null @@ -1,54 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const path_1 = __importDefault(require("path")); -const express_1 = __importDefault(require("express")); -// const prostgles = require("prostgles-server"); -const index_1 = __importDefault(require("../../../dist/index")); -const app = express_1.default(); -const http = require('http').createServer(app); -const io = require("socket.io")(http); -http.listen(3001); -index_1.default({ - dbConnection: { - host: "localhost", - port: 5432, - database: "postgres", - user: process.env.PRGL_USER, - password: process.env.PRGL_PWD - }, - sqlFilePath: path_1.default.join(__dirname + '/init.sql'), - io, - tsGeneratedTypesDir: path_1.default.join(__dirname + '/'), - transactions: "tt", - publish: (socket, dbo) => { - return "*"; - }, - joins: [ - { - tables: ["items", "items2"], - on: { name: "name" }, - type: "many-many" - }, - { - tables: ["items2", "items3"], - on: { name: "name" }, - type: "many-many" - } - ], - onReady: async (dbo, db) => { - app.get("*", (req, res) => { - console.log(req.originalUrl); - }); - try { - await dbo.items.insert([{ name: "a" }, { name: "a" }]); - console.log(await dbo.items.find()); - } - catch (err) { - console.error(err); - } - }, -}); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/examples/server/typescript/index.js.map b/examples/server/typescript/index.js.map deleted file mode 100644 index fdc38b24..00000000 --- a/examples/server/typescript/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;AAAA,gDAAwB;AACxB,sDAA8B;AAC9B,iDAAiD;AACjD,gEAA4C;AAC5C,MAAM,GAAG,GAAG,iBAAO,EAAE,CAAC;AACtB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC;AACtC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAIlB,eAAS,CAAC;IACT,YAAY,EAAE;QACb,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,IAAI;QACV,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS;QAC3B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ;KAC9B;IACD,WAAW,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,GAAC,WAAW,CAAC;IAC7C,EAAE;IACF,mBAAmB,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;IAC/C,YAAY,EAAE,IAAI;IAClB,OAAO,EAAE,CAAC,MAAM,EAAE,GAAU,EAAE,EAAE;QAE/B,OAAO,GAAG,CAAC;IACZ,CAAC;IACD,KAAK,EAAE;QACN;YACC,MAAM,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC;YAC3B,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;YACpB,IAAI,EAAE,WAAW;SACjB;QACD;YACC,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;YAC5B,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;YACpB,IAAI,EAAE,WAAW;SACjB;KACD;IACD,OAAO,EAAE,KAAK,EAAE,GAAU,EAAE,EAAE,EAAE,EAAE;QAEjC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACzB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QAC7B,CAAC,CAAC,CAAA;QACF,IAAI;YAEH,MAAM,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;SAGpC;QAAC,OAAM,GAAG,EAAE;YACZ,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;SAClB;IACF,CAAC;CACD,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/server/typescript/index.ts b/examples/server/typescript/index.ts index 7c4dd2e5..915da848 100644 --- a/examples/server/typescript/index.ts +++ b/examples/server/typescript/index.ts @@ -1,37 +1,31 @@ -import path from 'path'; -import express from 'express'; +import path from "path"; +import express from "express"; import prostgles from "prostgles-server"; const app = express(); -const http = require('http').createServer(app); +const http = require("http").createServer(app); const io = require("socket.io")(http); http.listen(3001); prostgles({ - dbConnection: { - host: "localhost", - port: 5432, - database: "postgres", - user: process.env.PRGL_USER, - password: process.env.PRGL_PWD - }, - sqlFilePath: path.join(__dirname+'/init.sql'), - io, - tsGeneratedTypesDir: path.join(__dirname + '/'), - transactions: "tt", - publish: () => { - - return "*"; - }, - onReady: async (dbo: DBObj, db) => { - - try { - - await dbo.items.insert([{ name: "a" }, { name: "a" }]); - console.log(await dbo.items.find()); - - - } catch(err) { - console.error(err) - } - }, + dbConnection: { + host: "localhost", + port: 5432, + database: "postgres", + user: process.env.PRGL_USER, + password: process.env.PRGL_PWD, + }, + sqlFilePath: path.join(__dirname + "/init.sql"), + io, + tsGeneratedTypesDir: path.join(__dirname + "/"), + publish: () => { + return "*"; + }, + onReady: async ({ dbo }) => { + try { + await dbo.items.insert([{ name: "a" }, { name: "a" }]); + console.log(await dbo.items.find()); + } catch (err) { + console.error(err); + } + }, }); diff --git a/lib/DboBuilder/TableHandler/DataValidator.ts b/lib/DboBuilder/TableHandler/DataValidator.ts index 1c6270eb..a2598016 100644 --- a/lib/DboBuilder/TableHandler/DataValidator.ts +++ b/lib/DboBuilder/TableHandler/DataValidator.ts @@ -82,7 +82,7 @@ type PrepareFieldValuesArgs = { row: AnyObject | undefined; forcedData: AnyObject | undefined; allowedCols: FieldFilter | undefined; - removeDisallowedColumns?: boolean; + removeDisallowedFields?: boolean; tableHandler: TableHandler; } /** @@ -94,7 +94,7 @@ type PrepareFieldValuesArgs = { * @param {Object} forcedData - set/override property * @param {string[]} allowed_cols - allowed columns (excluding forcedData) from table rules */ -const getValidatedRow = ({ row = {}, forcedData = {}, allowedCols, removeDisallowedColumns = false, tableHandler }: PrepareFieldValuesArgs): AnyObject => { +const getValidatedRow = ({ row = {}, forcedData = {}, allowedCols, removeDisallowedFields = false, tableHandler }: PrepareFieldValuesArgs): AnyObject => { const column_names = tableHandler.column_names.slice(0); if (!column_names.length) { throw "table column_names mising"; @@ -102,7 +102,7 @@ const getValidatedRow = ({ row = {}, forcedData = {}, allowedCols, removeDisallo const validatedAllowedColumns = tableHandler.parseFieldFilter(allowedCols, false); let finalRow = { ...row }; - if (removeDisallowedColumns && !isEmpty(finalRow)) { + if (removeDisallowedFields && !isEmpty(finalRow)) { finalRow = pickKeys(finalRow, validatedAllowedColumns); } @@ -124,7 +124,7 @@ const getValidatedRow = ({ row = {}, forcedData = {}, allowedCols, removeDisallo * prepareFieldValues(): Apply forcedData, remove disallowed columns, validate against allowed columns * tableConfigurator?.checkColVal(): Validate column min/max/isText/lowerCased/trimmed values */ -export const prepareNewData = async ({ row, forcedData, allowedFields, tableRules, fixIssues = false, tableConfigurator, tableHandler }: ValidatedParams) => { +export const prepareNewData = async ({ row, forcedData, allowedFields, tableRules, removeDisallowedFields = false, tableConfigurator, tableHandler }: ValidatedParams) => { const synced_field = (tableRules ?? {})?.sync?.synced_field; /* Update synced_field if sync is on and missing */ @@ -132,7 +132,7 @@ export const prepareNewData = async ({ row, forcedData, allowedFields, tableRule row[synced_field] = Date.now(); } - const data = getValidatedRow({ tableHandler, row, forcedData, allowedCols: allowedFields , removeDisallowedColumns: fixIssues }); + const data = getValidatedRow({ tableHandler, row, forcedData, allowedCols: allowedFields, removeDisallowedFields }); const dataKeys = getKeys(data); dataKeys.forEach(col => { diff --git a/lib/DboBuilder/TableHandler/TableHandler.ts b/lib/DboBuilder/TableHandler/TableHandler.ts index 6a70ccec..f5faa559 100644 --- a/lib/DboBuilder/TableHandler/TableHandler.ts +++ b/lib/DboBuilder/TableHandler/TableHandler.ts @@ -22,7 +22,7 @@ export type ValidatedParams = { forcedData?: AnyObject; allowedFields?: FieldFilter; tableRules?: TableRule; - fixIssues: boolean; + removeDisallowedFields: boolean; tableConfigurator: TableConfigurator | undefined; tableHandler: TableHandler; } diff --git a/lib/DboBuilder/TableHandler/insert.ts b/lib/DboBuilder/TableHandler/insert.ts index 338029bc..7d3f6667 100644 --- a/lib/DboBuilder/TableHandler/insert.ts +++ b/lib/DboBuilder/TableHandler/insert.ts @@ -13,7 +13,7 @@ export async function insert(this: TableHandler, rowOrRows: AnyObject | AnyObjec const start = Date.now(); try { - const { fixIssues = false } = insertParams || {}; + const { removeDisallowedFields = false } = insertParams || {}; const { returnQuery = false, nestedInsert } = localParams || {}; const finalDBtx = this.getFinalDBtx(localParams); @@ -79,7 +79,7 @@ export async function insert(this: TableHandler, rowOrRows: AnyObject | AnyObjec forcedData, allowedFields: fields, tableRules, - fixIssues, + removeDisallowedFields, tableConfigurator: this.dboBuilder.prostgles.tableConfigurator, tableHandler: this, }); @@ -165,7 +165,7 @@ const validateInsertParams = (params: InsertParams | undefined) => { } if (params) { - const good_paramsObj: Record = { returning: 1, returnType: 1, fixIssues: 1, onConflict: 1 }; + const good_paramsObj: Record = { returning: 1, returnType: 1, removeDisallowedFields: 1, onConflict: 1 }; const good_params = Object.keys(good_paramsObj); const bad_params = Object.keys(params).filter(k => !good_params.includes(k)); if (bad_params && bad_params.length) throw "Invalid params: " + bad_params.join(", ") + " \n Expecting: " + good_params.join(", "); diff --git a/lib/DboBuilder/TableHandler/update.ts b/lib/DboBuilder/TableHandler/update.ts index eb3908ca..04523540 100644 --- a/lib/DboBuilder/TableHandler/update.ts +++ b/lib/DboBuilder/TableHandler/update.ts @@ -1,30 +1,46 @@ import { AnyObject, UpdateParams } from "prostgles-types"; import { TableRule } from "../../PublishParser/PublishParser"; -import { Filter, LocalParams, getErrorAsObject, getSerializedClientErrorFromPGError, withUserRLS } from "../DboBuilder"; +import { + Filter, + LocalParams, + getErrorAsObject, + getSerializedClientErrorFromPGError, + withUserRLS, +} from "../DboBuilder"; import { getInsertTableRules, getReferenceColumnInserts } from "../insertNestedRecords"; import { prepareNewData } from "./DataValidator"; import { runInsertUpdateQuery } from "./runInsertUpdateQuery"; import { TableHandler } from "./TableHandler"; import { updateFile } from "./updateFile"; -export async function update(this: TableHandler, filter: Filter, _newData: AnyObject, params?: UpdateParams, tableRules?: TableRule, localParams?: LocalParams): Promise { +export async function update( + this: TableHandler, + filter: Filter, + _newData: AnyObject, + params?: UpdateParams, + tableRules?: TableRule, + localParams?: LocalParams +): Promise { const ACTION = "update"; const start = Date.now(); try { /** postValidate */ const finalDBtx = this.getFinalDBtx(localParams); - const wrapInTx = () => this.dboBuilder.getTX(_dbtx => _dbtx[this.name]?.[ACTION]?.(filter, _newData, params, tableRules, localParams)) - const rule = tableRules?.[ACTION] - if(rule?.postValidate && !finalDBtx){ + const wrapInTx = () => + this.dboBuilder.getTX((_dbtx) => + _dbtx[this.name]?.[ACTION]?.(filter, _newData, params, tableRules, localParams) + ); + const rule = tableRules?.[ACTION]; + if (rule?.postValidate && !finalDBtx) { return wrapInTx(); } let newData = _newData; - if(this.is_media){ + if (this.is_media) { ({ newData } = await updateFile.bind(this)({ newData, filter, localParams, tableRules })); } - const parsedRules = await this.parseUpdateRules(filter, params, tableRules, localParams) + const parsedRules = await this.parseUpdateRules(filter, params, tableRules, localParams); if (localParams?.testRule) { return parsedRules; } @@ -33,25 +49,34 @@ export async function update(this: TableHandler, filter: Filter, _newData: AnyOb throw "no update data provided\nEXPECTING db.table.update(filter, updateData, options)"; } - const { fields, validateRow, forcedData, returningFields, forcedFilter, filterFields } = parsedRules; - const { onConflict, fixIssues = false } = params || {}; + const { fields, validateRow, forcedData, returningFields, forcedFilter, filterFields } = + parsedRules; + const { removeDisallowedFields = false } = params || {}; const { returnQuery = false } = localParams ?? {}; if (params) { - const good_paramsObj: Record = { returning: 1, returnType: 1, fixIssues: 1, onConflict: 1, multi: 1 }; + const good_paramsObj: Record = { + returning: 1, + returnType: 1, + removeDisallowedFields: 1, + multi: 1, + }; const good_params = Object.keys(good_paramsObj); - const bad_params = Object.keys(params).filter(k => !good_params.includes(k)); - if (bad_params && bad_params.length) throw "Invalid params: " + bad_params.join(", ") + " \n Expecting: " + good_params.join(", "); + const bad_params = Object.keys(params).filter((k) => !good_params.includes(k)); + if (bad_params && bad_params.length) + throw ( + "Invalid params: " + bad_params.join(", ") + " \n Expecting: " + good_params.join(", ") + ); } - const { data, allowedCols } = await prepareNewData({ - row: newData, - forcedData, - allowedFields: fields, - tableRules, - fixIssues, + const { data, allowedCols } = await prepareNewData({ + row: newData, + forcedData, + allowedFields: fields, + tableRules, + removeDisallowedFields, tableConfigurator: this.dboBuilder.prostgles.tableConfigurator, - tableHandler: this, + tableHandler: this, }); const updateFilter = await this.prepareWhere({ @@ -60,46 +85,68 @@ export async function update(this: TableHandler, filter: Filter, _newData: AnyOb forcedFilter, filterFields, localParams, - tableRule: tableRules - }) - + tableRule: tableRules, + }); + /** * Nested inserts */ const nData = { ...data }; const nestedInserts = getReferenceColumnInserts(this, nData, true); const nestedInsertsResultsObj: Record = {}; - if(nestedInserts.length){ + if (nestedInserts.length) { const updateCount = await this.count(updateFilter.filter); - if(+updateCount > 1){ + if (+updateCount > 1) { throw "Cannot do a nestedInsert from an update that targets more than 1 row"; } - if(!finalDBtx){ + if (!finalDBtx) { return wrapInTx(); } - await Promise.all(nestedInserts.map(async nestedInsert => { - const nesedTableHandler = finalDBtx[nestedInsert.tableName] as TableHandler | undefined; - if(!nesedTableHandler) throw `nestedInsert Tablehandler not found for ${nestedInsert.tableName}`; - const refTableRules = !localParams? undefined : await getInsertTableRules(this, nestedInsert.tableName, localParams); - const nestedLocalParams: LocalParams = { ...localParams, nestedInsert: { depth: 1, previousData: nData, previousTable: this.name, referencingColumn: nestedInsert.col } } - const nestedInsertResult = await nesedTableHandler.insert(nestedInsert.data, { returning: "*" }, undefined, refTableRules, nestedLocalParams); - nestedInsertsResultsObj[nestedInsert.col] = nestedInsertResult; + await Promise.all( + nestedInserts.map(async (nestedInsert) => { + const nesedTableHandler = finalDBtx[nestedInsert.tableName] as TableHandler | undefined; + if (!nesedTableHandler) + throw `nestedInsert Tablehandler not found for ${nestedInsert.tableName}`; + const refTableRules = !localParams + ? undefined + : await getInsertTableRules(this, nestedInsert.tableName, localParams); + const nestedLocalParams: LocalParams = { + ...localParams, + nestedInsert: { + depth: 1, + previousData: nData, + previousTable: this.name, + referencingColumn: nestedInsert.col, + }, + }; + const nestedInsertResult = await nesedTableHandler.insert( + nestedInsert.data, + { returning: "*" }, + undefined, + refTableRules, + nestedLocalParams + ); + nestedInsertsResultsObj[nestedInsert.col] = nestedInsertResult; - nData[nestedInsert.col] = nestedInsertResult[nestedInsert.fcol]; - return { - ...nestedInsert, - result: nestedInsertResult, - } - })); + nData[nestedInsert.col] = nestedInsertResult[nestedInsert.fcol]; + return { + ...nestedInsert, + result: nestedInsertResult, + }; + }) + ); } - // let query = await this.colSet.getUpdateQuery(nData, allowedCols, this.getFinalDbo(localParams), validateRow, localParams) - let query = (await this.dataValidator.parse({ command: "update", rows: [nData], allowedCols, dbTx: this.getFinalDbo(localParams), validationOptions: { validate: validateRow, localParams }})).getQuery() + let query = ( + await this.dataValidator.parse({ + command: "update", + rows: [nData], + allowedCols, + dbTx: this.getFinalDbo(localParams), + validationOptions: { validate: validateRow, localParams }, + }) + ).getQuery(); query += "\n" + updateFilter.where; - if (onConflict === "DoNothing") query += " ON CONFLICT DO NOTHING "; - if(onConflict === "DoUpdate"){ - throw "onConflict 'DoUpdate' not possible for an update"; - } const queryWithoutUserRLS = query; query = withUserRLS(localParams, query); @@ -115,12 +162,23 @@ export async function update(this: TableHandler, filter: Filter, _newData: AnyOb returningFields, rule, type: "update", - nestedInsertsResultsObj + nestedInsertsResultsObj, + }); + await this._log({ + command: "update", + localParams, + data: { filter, _newData, params }, + duration: Date.now() - start, }); - await this._log({ command: "update", localParams, data: { filter, _newData, params }, duration: Date.now() - start }); return result; } catch (e) { - await this._log({ command: "update", localParams, data: { filter, _newData, params }, duration: Date.now() - start, error: getErrorAsObject(e) }); + await this._log({ + command: "update", + localParams, + data: { filter, _newData, params }, + duration: Date.now() - start, + error: getErrorAsObject(e), + }); throw getSerializedClientErrorFromPGError(e, { type: "tableMethod", localParams, view: this }); } -} \ No newline at end of file +} diff --git a/lib/DboBuilder/ViewHandler/ViewHandler.ts b/lib/DboBuilder/ViewHandler/ViewHandler.ts index 6da7b897..9c43bc00 100644 --- a/lib/DboBuilder/ViewHandler/ViewHandler.ts +++ b/lib/DboBuilder/ViewHandler/ViewHandler.ts @@ -353,7 +353,7 @@ export class ViewHandler { */ prepareWhere = prepareWhere.bind(this); - intersectColumns(allowedFields: FieldFilter, dissallowedFields: FieldFilter, fixIssues = false): string[] { + intersectColumns(allowedFields: FieldFilter, dissallowedFields: FieldFilter, removeDisallowedFields = false): string[] { let result: string[] = []; if (allowedFields) { result = this.parseFieldFilter(allowedFields); @@ -361,7 +361,7 @@ export class ViewHandler { if (dissallowedFields) { const _dissalowed = this.parseFieldFilter(dissallowedFields); - if (!fixIssues) { + if (!removeDisallowedFields) { throw `dissallowed/invalid field found for ${this.name}: ` } diff --git a/lib/DboBuilder/parseUpdateRules.ts b/lib/DboBuilder/parseUpdateRules.ts index 5a5abd67..d546fa4a 100644 --- a/lib/DboBuilder/parseUpdateRules.ts +++ b/lib/DboBuilder/parseUpdateRules.ts @@ -136,7 +136,7 @@ export async function parseUpdateRules( forcedData: undefined, allowedFields: "*", tableRules, - fixIssues: false, + removeDisallowedFields: false, tableConfigurator: this.dboBuilder.prostgles.tableConfigurator, tableHandler: this, }); diff --git a/lib/Prostgles.ts b/lib/Prostgles.ts index e4f5ab3a..16680f9f 100644 --- a/lib/Prostgles.ts +++ b/lib/Prostgles.ts @@ -3,37 +3,37 @@ * Licensed under the MIT License. See LICENSE in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as pgPromise from 'pg-promise'; +import * as pgPromise from "pg-promise"; import { AuthHandler } from "./Auth/AuthHandler"; import { FileManager } from "./FileManager/FileManager"; import { SchemaWatch } from "./SchemaWatch/SchemaWatch"; import { OnInitReason, initProstgles } from "./initProstgles"; import { makeSocketError, onSocketConnected } from "./onSocketConnected"; import { clientCanRunSqlRequest, runClientSqlRequest } from "./runClientRequest"; -import pg = require('pg-promise/typescript/pg-subset'); -const { version } = require('../package.json'); +import pg = require("pg-promise/typescript/pg-subset"); +const { version } = require("../package.json"); import type { ProstglesInitOptions } from "./ProstglesTypes"; import { RestApi } from "./RestApi"; import TableConfigurator from "./TableConfig/TableConfig"; - -import { DBHandlerServer, DboBuilder, LocalParams, PRGLIOSocket, getErrorAsObject } from "./DboBuilder/DboBuilder"; + +import { + DBHandlerServer, + DboBuilder, + LocalParams, + PRGLIOSocket, + getErrorAsObject, +} from "./DboBuilder/DboBuilder"; export { DBHandlerServer }; export type PGP = pgPromise.IMain<{}, pg.IClient>; -import { - CHANNELS, - ClientSchema, - SQLRequest, - isObject, omitKeys, tryCatch -} from "prostgles-types"; +import { CHANNELS, ClientSchema, SQLRequest, isObject, omitKeys, tryCatch } from "prostgles-types"; import { DBEventsManager } from "./DBEventsManager"; import { PublishParser } from "./PublishParser/PublishParser"; export { getOrSetTransporter, sendEmail, verifySMTPConfig } from "./Auth/sendEmail"; export type DB = pgPromise.IDatabase<{}, pg.IClient>; -export type DBorTx = DB | pgPromise.ITask<{}> - +export type DBorTx = DB | pgPromise.ITask<{}>; export const TABLE_METHODS = ["update", "find", "findOne", "insert", "delete", "upsert"] as const; @@ -48,17 +48,17 @@ export const TABLE_METHODS = ["update", "find", "findOne", "insert", "delete", " export type OnReady = { dbo: DBHandlerServer; db: DB; -} +}; const DEFAULT_KEYWORDS = { $filter: "$filter", $and: "$and", $or: "$or", - $not: "$not" + $not: "$not", }; import { randomUUID } from "crypto"; -import * as fs from 'fs'; +import * as fs from "fs"; export class Prostgles { /** @@ -70,22 +70,22 @@ export class Prostgles { dbConnection: { host: "localhost", port: 5432, - application_name: "prostgles_app" + application_name: "prostgles_app", }, - onReady: () => { - //empty + onReady: () => { + //empty }, watchSchema: false, watchSchemaType: "DDL_trigger", }; - + db?: DB; pgp?: PGP; dbo?: DBHandlerServer; _dboBuilder?: DboBuilder; get dboBuilder(): DboBuilder { if (!this._dboBuilder) { - console.trace(1) + console.trace(1); throw "get dboBuilder: it's undefined"; } return this._dboBuilder; @@ -118,14 +118,39 @@ export class Prostgles { if (!params) throw "ProstglesInitOptions missing"; const config: Record = { - transactions: 1, joins: 1, tsGeneratedTypesDir: 1, disableRealtime: 1, - onReady: 1, dbConnection: 1, dbOptions: 1, publishMethods: 1, - io: 1, publish: 1, schema: 1, publishRawSQL: 1, wsChannelNamePrefix: 1, - onSocketConnect: 1, onSocketDisconnect: 1, sqlFilePath: 1, auth: 1, - DEBUG_MODE: 1, watchSchema: 1, watchSchemaType: 1, fileTable: 1, onQuery: 1, - tableConfig: 1, tableConfigMigrations: 1, keywords: 1, onNotice: 1, onLog: 1, restApi: 1, testRulesOnConnect: 1 + transactions: 1, + joins: 1, + tsGeneratedTypesDir: 1, + disableRealtime: 1, + onReady: 1, + dbConnection: 1, + dbOptions: 1, + publishMethods: 1, + io: 1, + publish: 1, + schema: 1, + publishRawSQL: 1, + wsChannelNamePrefix: 1, + onSocketConnect: 1, + onSocketDisconnect: 1, + sqlFilePath: 1, + auth: 1, + DEBUG_MODE: 1, + watchSchema: 1, + watchSchemaType: 1, + fileTable: 1, + onQuery: 1, + tableConfig: 1, + tableConfigMigrations: 1, + keywords: 1, + onNotice: 1, + onLog: 1, + restApi: 1, + testRulesOnConnect: 1, }; - const unknownParams = Object.keys(params).filter((key: string) => !Object.keys(config).includes(key)) + const unknownParams = Object.keys(params).filter( + (key: string) => !Object.keys(config).includes(key) + ); if (unknownParams.length) { console.error(`Unrecognised ProstglesInitOptions params: ${unknownParams.join()}`); } @@ -136,12 +161,12 @@ export class Prostgles { if (this.opts?.fileTable) { this.opts.fileTable.tableName ??= "media"; } - this.opts.schema ??= { "public": 1 }; + this.opts.schema ??= { public: 1 }; this.keywords = { ...DEFAULT_KEYWORDS, ...params.keywords, - } + }; } destroyed = false; @@ -151,45 +176,45 @@ export class Prostgles { } getTSFileName() { - const fileName = "DBoGenerated.d.ts" //`dbo_${this.schema}_types.ts`; - const _dir = (this.opts.tsGeneratedTypesDir || ""); - const dir = _dir.endsWith("/")? _dir : `${_dir}/`; + const fileName = "DBSchemaGenerated.d.ts"; //`dbo_${this.schema}_types.ts`; + const _dir = this.opts.tsGeneratedTypesDir || ""; + const dir = _dir.endsWith("/") ? _dir : `${_dir}/`; const fullPath = dir + fileName; - return { fileName, fullPath } + return { fileName, fullPath }; } private getFileText(fullPath: string, _format = "utf8"): Promise { return new Promise((resolve, reject) => { - fs.readFile(fullPath, 'utf8', function (err, data) { + fs.readFile(fullPath, "utf8", function (err, data) { if (err) reject(err); else resolve(data); }); - }) + }); } getTSFileContent = () => { - const header = `/* This file was generated by Prostgles \n` + - // `* ${(new Date).toUTCString()} \n` - `*/ \n\n `; + const header = + `/* This file was generated by Prostgles \n` + + // `* ${(new Date).toUTCString()} \n` + `*/ \n\n `; return header + this.dboBuilder.tsTypesDefinition; - } + }; /** * Will write the Schema Typescript definitions to file (tsGeneratedTypesDir) */ writeDBSchema(force = false) { - if (this.opts.tsGeneratedTypesDir) { const { fullPath, fileName } = this.getTSFileName(); const fileContent = this.getTSFileContent(); - fs.readFile(fullPath, 'utf8', function (err, data) { - if (err || (force || data !== fileContent)) { + fs.readFile(fullPath, "utf8", function (err, data) { + if (err || force || data !== fileContent) { fs.writeFileSync(fullPath, fileContent); - console.log("Prostgles: Created typescript schema definition file: \n " + fileName) + console.log("Prostgles: Created typescript schema definition file: \n " + fileName); } }); } else if (force) { - console.error("Schema changed. tsGeneratedTypesDir needs to be set to reload server") + console.error("Schema changed. tsGeneratedTypesDir needs to be set to reload server"); } } @@ -197,11 +222,11 @@ export class Prostgles { * Will re-create the dbo object */ refreshDBO = async () => { - await this.opts.onLog?.({ - type: "debug", - command: "refreshDBO.start", - duration: -1, - data: { } + await this.opts.onLog?.({ + type: "debug", + command: "refreshDBO.start", + duration: -1, + data: {}, }); const start = Date.now(); if (this._dboBuilder) { @@ -211,9 +236,13 @@ export class Prostgles { } if (!this.dboBuilder) throw "this.dboBuilder"; this.dbo = this.dboBuilder.dbo; - await this.opts.onLog?.({ type: "debug", command: "refreshDBO.end", duration: Date.now() - start }) + await this.opts.onLog?.({ + type: "debug", + command: "refreshDBO.end", + duration: Date.now() - start, + }); return this.dbo; - } + }; initRestApi = async () => { if (this.opts.restApi) { @@ -222,29 +251,36 @@ export class Prostgles { this.restApi?.destroy(); this.restApi = undefined; } - } + }; initAuthHandler = async () => { this.authHandler?.destroy(); this.authHandler = new AuthHandler(this as any); await this.authHandler.init(); - } + }; initTableConfig = async (reason: OnInitReason) => { const res = await tryCatch(async () => { - - if(this.tableConfigurator?.initialising){ + if (this.tableConfigurator?.initialising) { console.error("TableConfigurator WILL deadlock", { reason }); } await this.tableConfigurator?.destroy(); this.tableConfigurator = new TableConfigurator(this); try { const now = Date.now(); - await this.opts.onLog?.({ type: "debug", command: "tableConfigurator.init.start", duration: -1 }); + await this.opts.onLog?.({ + type: "debug", + command: "tableConfigurator.init.start", + duration: -1, + }); await this.tableConfigurator.init(); - await this.opts.onLog?.({ type: "debug", command: "tableConfigurator.init.end", duration: Date.now() - now }); + await this.opts.onLog?.({ + type: "debug", + command: "tableConfigurator.init.end", + duration: Date.now() - now, + }); } catch (e) { - if(this.opts.tableConfigMigrations?.silentFail === false){ + if (this.opts.tableConfigMigrations?.silentFail === false) { console.error("TableConfigurator silentFail: ", e); } else { throw e; @@ -252,21 +288,21 @@ export class Prostgles { } }); await this.opts.onLog?.({ type: "debug", command: "initTableConfig", ...res }); - if(res.hasError) throw res.error; + if (res.hasError) throw res.error; return res.data; - } + }; /* Create media table if required */ initFileTable = async () => { const res = await tryCatch(async () => { - if (this.opts.fileTable) { const { cloudClient, localConfig, imageOptions } = this.opts.fileTable; await this.refreshDBO(); - if (!cloudClient && !localConfig) throw "fileTable missing param: Must provide awsS3Config OR localConfig"; - + if (!cloudClient && !localConfig) + throw "fileTable missing param: Must provide awsS3Config OR localConfig"; + this.fileManager = new FileManager(cloudClient || localConfig!, imageOptions); - + try { await this.fileManager.init(this); } catch (e) { @@ -278,55 +314,61 @@ export class Prostgles { this.fileManager = undefined; } await this.refreshDBO(); - return { data: {} } + return { data: {} }; }); - await this.opts.onLog?.({ - type: "debug", - command: "initFileTable", + await this.opts.onLog?.({ + type: "debug", + command: "initFileTable", ...res, }); - if(res.error !== undefined) throw res.error; + if (res.error !== undefined) throw res.error; return res.data; - } + }; isSuperUser = false; init = initProstgles.bind(this); async runSQLFile(filePath: string) { - const res = await tryCatch(async () => { - const fileContent = await this.getFileText(filePath);//.then(console.log); - - const result = await this.db?.multi(fileContent) + const fileContent = await this.getFileText(filePath); //.then(console.log); + + const result = await this.db + ?.multi(fileContent) .then((data) => { console.log("Prostgles: SQL file executed successfuly \n -> " + filePath); return data; - }).catch((err) => { + }) + .catch((err) => { const { position, length } = err, lines = fileContent.split("\n"); let errMsg = filePath + " error: "; - + if (position && length && fileContent) { - const startLine = Math.max(0, fileContent.substring(0, position).split("\n").length - 2), + const startLine = Math.max( + 0, + fileContent.substring(0, position).split("\n").length - 2 + ), endLine = startLine + 3; - + errMsg += "\n\n"; - errMsg += lines.slice(startLine, endLine).map((txt, i) => `${startLine + i + 1} ${i === 1 ? "->" : " "} ${txt}`).join("\n"); + errMsg += lines + .slice(startLine, endLine) + .map((txt, i) => `${startLine + i + 1} ${i === 1 ? "->" : " "} ${txt}`) + .join("\n"); errMsg += "\n\n"; } console.error(errMsg, err); return Promise.reject(err); }); - return { success: result?.length } + return { success: result?.length }; }); await this.opts.onLog?.({ type: "debug", command: "runSQLFile", ...res }); - if(res.error !== undefined) throw res.error; + if (res.error !== undefined) throw res.error; return res.success; } - connectedSockets: PRGLIOSocket[] = []; async setSocketEvents() { this.checkDb(); @@ -334,11 +376,11 @@ export class Prostgles { if (!this.dbo) throw "dbo missing"; const publishParser = new PublishParser( - this.opts.publish, - this.opts.publishMethods, - this.opts.publishRawSQL, - this.dbo, - this.db!, + this.opts.publish, + this.opts.publishMethods, + this.opts.publishRawSQL, + this.dbo, + this.db!, this ); this.publishParser = publishParser; @@ -347,7 +389,7 @@ export class Prostgles { /* Already initialised. Only reconnect sockets */ if (this.connectedSockets.length) { - this.connectedSockets.forEach(s => { + this.connectedSockets.forEach((s) => { s.emit(CHANNELS.SCHEMA_CHANGED); this.pushSocketSchema(s); }); @@ -355,22 +397,23 @@ export class Prostgles { } /* Initialise */ - this.opts.io.removeAllListeners('connection'); - this.opts.io.on('connection', this.onSocketConnected); + this.opts.io.removeAllListeners("connection"); + this.opts.io.on("connection", this.onSocketConnected); /** In some cases io will re-init with already connected sockets */ - this.opts.io?.sockets.sockets.forEach(socket => this.onSocketConnected(socket)) + this.opts.io?.sockets.sockets.forEach((socket) => this.onSocketConnected(socket)); } onSocketConnected = onSocketConnected.bind(this); getClientSchema = async (clientReq: Pick) => { - const result = await tryCatch(async () => { - - const clientInfo = clientReq.socket? { type: "socket" as const, socket: clientReq.socket } : clientReq.httpReq? { type: "http" as const, httpReq: clientReq.httpReq } : undefined; - if(!clientInfo) throw "Invalid client"; - if(!this.authHandler) throw "this.authHandler missing"; - const userData = await this.authHandler.getClientInfo(clientInfo); + const clientInfo = + clientReq.socket ? { type: "socket" as const, socket: clientReq.socket } + : clientReq.httpReq ? { type: "http" as const, httpReq: clientReq.httpReq } + : undefined; + if (!clientInfo) throw "Invalid client"; + if (!this.authHandler) throw "this.authHandler missing"; + const userData = await this.authHandler.getClientInfo(clientInfo); const { publishParser } = this; let fullSchema: Awaited> | undefined; let publishValidationError; @@ -388,15 +431,19 @@ export class Prostgles { rawSQL = allowed; } - const { schema, tables, tableSchemaErrors } = fullSchema ?? { schema: {}, tables: [], tableSchemaErrors: {} }; + const { schema, tables, tableSchemaErrors } = fullSchema ?? { + schema: {}, + tables: [], + tableSchemaErrors: {}, + }; const joinTables2: string[][] = []; if (this.opts.joins) { - const _joinTables2 = this.dboBuilder.getAllJoinPaths() - .filter(jp => - ![jp.t1, jp.t2].find(t => !schema[t] || !schema[t]?.findOne) - ).map(jp => [jp.t1, jp.t2].sort()); - _joinTables2.map(jt => { - if (!joinTables2.find(_jt => _jt.join() === jt.join())) { + const _joinTables2 = this.dboBuilder + .getAllJoinPaths() + .filter((jp) => ![jp.t1, jp.t2].find((t) => !schema[t] || !schema[t]?.findOne)) + .map((jp) => [jp.t1, jp.t2].sort()); + _joinTables2.map((jt) => { + if (!joinTables2.find((_jt) => _jt.join() === jt.join())) { joinTables2.push(jt); } }); @@ -404,78 +451,97 @@ export class Prostgles { const methods = await publishParser?.getAllowedMethods(clientInfo, userData); - const methodSchema: ClientSchema["methods"] = !methods? [] : Object.entries(methods).map(([methodName, method]) => { - if(isObject(method) && "run" in method){ - return { - name: methodName, - ...omitKeys(method, ["run"]), - } - } - return methodName; - }).sort((a, b) => { - const aName = isObject(a)? a.name : a; - const bName = isObject(b)? b.name : b; - return aName.localeCompare(bName); - }); + const methodSchema: ClientSchema["methods"] = + !methods ? + [] + : Object.entries(methods) + .map(([methodName, method]) => { + if (isObject(method) && "run" in method) { + return { + name: methodName, + ...omitKeys(method, ["run"]), + }; + } + return methodName; + }) + .sort((a, b) => { + const aName = isObject(a) ? a.name : a; + const bName = isObject(b) ? b.name : b; + return aName.localeCompare(bName); + }); const { auth } = await this.authHandler.getClientAuth(clientReq); - + const clientSchema: ClientSchema = { schema, - methods: methodSchema, + methods: methodSchema, tableSchema: tables, rawSQL, joinTables: joinTables2, tableSchemaErrors, auth, version, - err: publishValidationError? "Server Error: User publish validation failed." : undefined + err: publishValidationError ? "Server Error: User publish validation failed." : undefined, }; - + return { publishValidationError, clientSchema, - userData - } + userData, + }; }); const sid = result.userData?.sid ?? this.authHandler?.getSIDNoError(clientReq); - await this.opts.onLog?.({ + await this.opts.onLog?.({ type: "connect.getClientSchema", duration: result.duration, sid, socketId: clientReq.socket?.id, error: result.error || result.publishValidationError, }); - if(result.hasError) throw result.error; + if (result.hasError) throw result.error; return result.clientSchema; - } + }; pushSocketSchema = async (socket: PRGLIOSocket) => { - try { const clientSchema = await this.getClientSchema({ socket }); socket.prostgles = clientSchema; if (clientSchema.rawSQL) { - socket.removeAllListeners(CHANNELS.SQL) - socket.on(CHANNELS.SQL, async ({ query, params, options }: SQLRequest, cb = (..._callback: any) => { /* Empty */ }) => { - - runClientSqlRequest.bind(this)({ type: "socket", socket, query, args: params, options }).then(res => { - cb(null, res); - }).catch(err => { - makeSocketError(cb, err); - }); - }); + socket.removeAllListeners(CHANNELS.SQL); + socket.on( + CHANNELS.SQL, + async ( + { query, params, options }: SQLRequest, + cb = (..._callback: any) => { + /* Empty */ + } + ) => { + runClientSqlRequest + .bind(this)({ type: "socket", socket, query, args: params, options }) + .then((res) => { + cb(null, res); + }) + .catch((err) => { + makeSocketError(cb, err); + }); + } + ); } - await this.dboBuilder.prostgles.opts.onLog?.({ type: "debug", command: "pushSocketSchema", duration: -1, data: { socketId: socket.id, clientSchema } }); + await this.dboBuilder.prostgles.opts.onLog?.({ + type: "debug", + command: "pushSocketSchema", + duration: -1, + data: { socketId: socket.id, clientSchema }, + }); socket.emit(CHANNELS.SCHEMA, clientSchema); - } catch (err) { socket.emit(CHANNELS.SCHEMA, { err: getErrorAsObject(err) }); } - } + }; } - export async function getIsSuperUser(db: DB): Promise { - return db.oneOrNone("select usesuper from pg_user where usename = CURRENT_USER;").then(r => r.usesuper); -} \ No newline at end of file + return db + .oneOrNone("select usesuper from pg_user where usename = CURRENT_USER;") + .then((r) => r.usesuper); +} diff --git a/lib/ProstglesTypes.ts b/lib/ProstglesTypes.ts index 37a0b0d3..7decc340 100644 --- a/lib/ProstglesTypes.ts +++ b/lib/ProstglesTypes.ts @@ -1,4 +1,3 @@ - import { FileColumnConfig } from "prostgles-types"; import { Auth, AuthRequestParams, SessionUser } from "./Auth/AuthTypes"; import { EventTriggerTagFilter } from "./Event_Trigger_Tags"; @@ -10,12 +9,10 @@ import { ColConstraint } from "./TableConfig/getConstraintDefinitionQueries"; import { DbConnection, DbConnectionOpts, OnReadyCallback } from "./initProstgles"; import { RestApiConfig } from "./RestApi"; import { TableConfig } from "./TableConfig/TableConfig"; - + import { PRGLIOSocket } from "./DboBuilder/DboBuilder"; -import { - AnyObject -} from "prostgles-types"; +import { AnyObject } from "prostgles-types"; import type { Server } from "socket.io"; import { Publish, PublishMethods, PublishParams } from "./PublishParser/PublishParser"; import { DB } from "./Prostgles"; @@ -25,21 +22,21 @@ import pg from "pg-promise/typescript/pg-subset"; /** * Allows uploading and downloading files. * Currently supports only S3. - * + * * @description * Will create a media table that contains file metadata and urls * Inserting a file into this table through prostgles will upload it to S3 and insert the relevant metadata into the media table * Requesting a file from HTTP GET {fileUrlPath}/{fileId} will: - * 1. check auth (if provided) + * 1. check auth (if provided) * 2. check the permissions in publish (if provided) * 3. redirect the request to the signed url (if allowed) - * + * * Specifying referencedTables will: * 1. create a column in that table called media - * 2. create a lookup table lookup_media_{referencedTable} that joins referencedTable to the media table + * 2. create a lookup table lookup_media_{referencedTable} that joins referencedTable to the media table */ export type FileTableConfig = { - tableName?: string; /* defaults to 'media' */ + tableName?: string /* defaults to 'media' */; /** * GET path used in serving media. defaults to /${tableName} @@ -54,31 +51,27 @@ export type FileTableConfig = { * Instead, the "deleted" field will be updated to the current timestamp and after the day interval provided in "deleteAfterNDays" the files will be deleted * "checkIntervalMinutes" is the frequency in hours at which the files ready for deletion are deleted */ - delayedDelete?: { + delayedDelete?: { /** * Minimum amount of time measured in days for which the files will not be deleted after requesting delete */ - deleteAfterNDays: number; + deleteAfterNDays: number; /** * How freuquently the files will be checked for deletion delay */ - checkIntervalHours?: number; - } + checkIntervalHours?: number; + }; expressApp: ExpressApp; referencedTables?: { - [tableName: string]: - - /** - * If defined then will try to create (if necessary) these columns which will reference files_table(id) - * Prostgles UI will use these hints (obtained through tableHandler.getInfo()) - * */ - | { type: "column", referenceColumns: Record } - }, - imageOptions?: ImageOptions + [tableName: string]: /** + * If defined then will try to create (if necessary) these columns which will reference files_table(id) + * Prostgles UI will use these hints (obtained through tableHandler.getInfo()) + * */ + { type: "column"; referenceColumns: Record }; + }; + imageOptions?: ImageOptions; }; - - type Keywords = { $and: string; $or: string; @@ -89,59 +82,127 @@ export const JOIN_TYPES = ["one-many", "many-one", "one-one", "many-many"] as co export type Join = { tables: [string, string]; on: { [key: string]: string }[]; // Allow multi references to table - type: typeof JOIN_TYPES[number]; + type: (typeof JOIN_TYPES)[number]; }; type Joins = Join[] | "inferred"; export type ProstglesInitOptions = { dbConnection: DbConnection; + /** + * Called when the prostgles server is ready to accept connections. + * It waits for auth, tableConfig and other async configurations to complete before executing + */ + onReady: OnReadyCallback; dbOptions?: DbConnectionOpts; + + /** + * If defined then a `DBSchemaGenerated.d.ts` file will be created in the provided directory. + * This file exports a `DBSchemaGenerated` type which contains types for the database tables and + * can be used as a generic type input for the prostgles instances to ensure type safety + */ tsGeneratedTypesDir?: string; + + /** + * If true then schema watch, subscriptions and syncs will be disabled. + * No `prostgles` schema will be created which is needed for the realtime features. + * This is useful when you want to connect to a database and prevent any changes to the schema + */ disableRealtime?: boolean; + + /** + * Socket.IO server instance object + */ io?: Server; + + /** + * Data access rules applied to clients. + * By default, nothing is allowed. + */ publish?: Publish; + /** - * If true then will test all table methods on each socket connect + * If true then will test all table methods on each socket connect. + * Not recommended for production */ testRulesOnConnect?: boolean; + + /** + * Custom methods that can be called from the client + */ publishMethods?: PublishMethods; - publishRawSQL?(params: PublishParams): ((boolean | "*") | Promise<(boolean | "*")>); + + /** + * If defined and resolves to true then the connected client can run SQL queries + */ + publishRawSQL?(params: PublishParams): (boolean | "*") | Promise; + + /** + * Allows defining joins between tables: + * - `infered` - uses the foreign keys to infer the joins + * - `Join[]` - specifies the joins manually + */ joins?: Joins; + + /** + * If defined then the specified schemas are included/excluded from the prostgles schema. + * By default the `public` schema is included. + */ schema?: Record | Record; + + /** + * Path to a SQL file that will be executed on startup (but before onReady) + */ sqlFilePath?: string; - onReady: OnReadyCallback; transactions?: string | boolean; wsChannelNamePrefix?: string; + /** + * Called when a socket connects * Use for connection verification. Will disconnect socket on any errors */ - onSocketConnect?: (args: AuthRequestParams & { socket: PRGLIOSocket }) => void | Promise; - onSocketDisconnect?: (args: AuthRequestParams & { socket: PRGLIOSocket }) => void | Promise; + onSocketConnect?: ( + args: AuthRequestParams & { socket: PRGLIOSocket } + ) => void | Promise; + + /** + * Called when a socket disconnects + */ + onSocketDisconnect?: ( + args: AuthRequestParams & { socket: PRGLIOSocket } + ) => void | Promise; + + /** + * Auth configuration. + * Supports email and OAuth strategies + */ auth?: Auth; DEBUG_MODE?: boolean; - onQuery?: (error: any, ctx: pgPromise.IEventContext) => void; - watchSchemaType?: /** + * Callback called when a query is executed. + * Useful for logging or debugging + */ + onQuery?: (error: any, ctx: pgPromise.IEventContext) => void; + watchSchemaType?: /** * Will set database event trigger for schema changes. Requires superuser * Default */ | "DDL_trigger" - /** - * Will check client queries for schema changes - * fallback if DDL not possible - */ - | "prostgles_queries" + /** + * Will check client queries for schema changes + * fallback if DDL not possible + */ + | "prostgles_queries"; /** - * If truthy then DBoGenerated.d.ts will be updated and "onReady" will be called with new schema on both client and server + * If truthy then DBSchemaGenerated.d.ts will be updated + * and "onReady" will be called with new schema on both client and server */ - watchSchema?: - /** - * Will listen only to few events (create table/view) - */ - | boolean + watchSchema?: /** + * Will listen only to few events (create table/view) + */ + | boolean /** * Will listen to specified events (or all if "*" is specified) @@ -161,10 +222,25 @@ export type ProstglesInitOptions void; + + /** + * Enables file storage and serving. + * Currently supports saving files locally or to AWS S3 + */ fileTable?: FileTableConfig; + + /** + * Rest API configuration. + * The REST API allows interacting with the database similarly to the socket connection + * with the exception of subscriptions and realtime features + */ restApi?: RestApiConfig; + /** - * Creates tables and provides UI labels, autocomplete and hints for a given json structure + * A simple way of defining tables through a JSON-schema like object. + * Allowes adding runtime JSONB validation and type safety. + * Should be used with caution because it tends to revert any changes + * made to the database schema through SQL queries */ tableConfig?: TableConfig; tableConfigMigrations?: { @@ -175,9 +251,9 @@ export type ProstglesInitOptions Promise + ) => Promise; }) => void; }; + /** + * Usefull for logging or debugging + */ onLog?: (evt: EventInfo) => Promise; -} \ No newline at end of file +}; diff --git a/lib/SyncReplication.ts b/lib/SyncReplication.ts index f1caa92b..a55d5ebb 100644 --- a/lib/SyncReplication.ts +++ b/lib/SyncReplication.ts @@ -219,13 +219,13 @@ export async function syncData (this: PubSubManager, sync: SyncParams, clientDat updateData.push([syncSafeFilter, omitKeys(upd, id_fields)]) })); - await tbl.updateBatch(updateData, { fixIssues: true }, undefined, table_rules); + await tbl.updateBatch(updateData, { removeDisallowedFields: true }, undefined, table_rules); } else { updates = []; } if (table_rules.insert && inserts.length) { - await tbl.insert(inserts, { fixIssues: true }, undefined, table_rules); + await tbl.insert(inserts, { removeDisallowedFields: true }, undefined, table_rules); } else { inserts = []; } diff --git a/lib/initProstgles.ts b/lib/initProstgles.ts index 69d0a879..1b121a9c 100644 --- a/lib/initProstgles.ts +++ b/lib/initProstgles.ts @@ -1,7 +1,6 @@ import * as pgPromise from "pg-promise"; import pg from "pg-promise/typescript/pg-subset"; import { getKeys, isEmpty } from "prostgles-types"; -import { AuthHandler } from "./Auth/AuthHandler"; import { DBEventsManager } from "./DBEventsManager"; import { DBOFullyTyped } from "./DBSchemaBuilder"; import { DBHandlerServer, Prostgles, getIsSuperUser } from "./Prostgles"; @@ -10,38 +9,48 @@ import { DbTableInfo, PublishParser } from "./PublishParser/PublishParser"; import { SchemaWatch } from "./SchemaWatch/SchemaWatch"; import { sleep } from "./utils"; -export type DbConnection = string | pg.IConnectionParameters; +/** + * Database connection details + */ +export type DbConnection = + /** + * Connection URI + */ + string | pg.IConnectionParameters; export type DbConnectionOpts = pg.IDefaults; export type PGP = pgPromise.IMain<{}, pg.IClient>; export type DB = pgPromise.IDatabase<{}, pg.IClient>; -export type UpdateableOptions = Pick; -export type OnInitReason = - | { - type: "schema change"; - query: string; - command: string; - } +export type UpdateableOptions = Pick< + ProstglesInitOptions, + "fileTable" | "restApi" | "tableConfig" | "schema" | "auth" +>; +export type OnInitReason = | { - type: "prgl.update"; - newOpts: Omit; - } - | { - type: "init" | "prgl.restart" | "TableConfig" - }; + type: "schema change"; + query: string; + command: string; + } + | { + type: "prgl.update"; + newOpts: Omit; + } + | { + type: "init" | "prgl.restart" | "TableConfig"; + }; type OnReadyParamsCommon = { db: DB; tables: DbTableInfo[]; reason: OnInitReason; -} +}; export type OnReadyParamsBasic = OnReadyParamsCommon & { - dbo: DBHandlerServer; -} + dbo: DBHandlerServer; +}; export type OnReadyParams = OnReadyParamsCommon & { - dbo: DBOFullyTyped; -} + dbo: DBOFullyTyped; +}; export type OnReadyCallback = (params: OnReadyParams) => any; export type OnReadyCallbackBasic = (params: OnReadyParamsBasic) => any; @@ -57,48 +66,54 @@ export type InitResult = { */ getTSSchema: () => string; update: (newOpts: UpdateableOptions) => Promise; - restart: () => Promise; + restart: () => Promise; options: ProstglesInitOptions; -} +}; const clientOnlyUpdateKeys = ["auth"] as const satisfies (keyof UpdateableOptions)[]; -export const initProstgles = async function(this: Prostgles, onReady: OnReadyCallbackBasic, reason: OnInitReason): Promise { +export const initProstgles = async function ( + this: Prostgles, + onReady: OnReadyCallbackBasic, + reason: OnInitReason +): Promise { this.loaded = false; if (!this.db) { let existingAppName = ""; let connString = ""; - if(typeof this.opts.dbConnection === "string"){ + if (typeof this.opts.dbConnection === "string") { connString = this.opts.dbConnection; - } else if(this.opts.dbConnection.connectionString){ + } else if (this.opts.dbConnection.connectionString) { connString = this.opts.dbConnection.connectionString; } else { existingAppName = this.opts.dbConnection.application_name ?? ""; } - if(connString){ + if (connString) { try { const url = new URL(connString); - existingAppName = url.searchParams.get("application_name") ?? url.searchParams.get("ApplicationName") ?? ""; - } catch (e) { - - } + existingAppName = + url.searchParams.get("application_name") ?? url.searchParams.get("ApplicationName") ?? ""; + } catch (e) {} } - const conObj = typeof this.opts.dbConnection === "string" ? { connectionString: this.opts.dbConnection } : this.opts.dbConnection + const conObj = + typeof this.opts.dbConnection === "string" + ? { connectionString: this.opts.dbConnection } + : this.opts.dbConnection; const application_name = `prostgles ${this.appId} ${existingAppName}`; /* 1. Connect to db */ const { db, pgp } = getDbConnection({ ...this.opts, - dbConnection: { ...conObj, application_name }, - onNotice: notice => { + dbConnection: { ...conObj, application_name }, + onNotice: (notice) => { if (this.opts.onNotice) this.opts.onNotice(notice); if (this.dbEventsManager) { - this.dbEventsManager.onNotice(notice) + this.dbEventsManager.onNotice(notice); } - } + }, }); this.db = db; this.pgp = pgp; @@ -115,7 +130,6 @@ export const initProstgles = async function(this: Prostgles, onReady: OnReadyCal } try { - await this.refreshDBO(); await this.initTableConfig(reason); await this.initFileTable(); @@ -124,7 +138,6 @@ export const initProstgles = async function(this: Prostgles, onReady: OnReadyCal this.schemaWatch = await SchemaWatch.create(this.dboBuilder); if (this.opts.publish) { - if (!this.opts.io) { console.warn("IO missing. Publish has no effect without io"); } @@ -132,34 +145,39 @@ export const initProstgles = async function(this: Prostgles, onReady: OnReadyCal /* 3.9 Check auth config */ await this.initAuthHandler(); - this.publishParser = new PublishParser(this.opts.publish, this.opts.publishMethods as any, this.opts.publishRawSQL, this.dbo!, this.db, this as any); + this.publishParser = new PublishParser( + this.opts.publish, + this.opts.publishMethods as any, + this.opts.publishRawSQL, + this.dbo!, + this.db, + this as any + ); this.dboBuilder.publishParser = this.publishParser; /* 4. Set publish and auth listeners */ await this.setSocketEvents(); - } else if (this.opts.auth) { throw "Auth config does not work without publish"; } this.dbEventsManager = new DBEventsManager(db, pgp); - this.writeDBSchema(); /* 5. Finish init and provide DBO object */ try { if (this.destroyed) { - console.trace("Prostgles: Instance is destroyed") + console.trace("Prostgles: Instance is destroyed"); } onReady({ - dbo: this.dbo as any, - db: this.db, + dbo: this.dbo as any, + db: this.db, tables: this.dboBuilder.tables, - reason + reason, }); } catch (err) { - console.error("Prostgles: Error within onReady: \n", err) + console.error("Prostgles: Error within onReady: \n", err); } this.loaded = true; @@ -171,36 +189,36 @@ export const initProstgles = async function(this: Prostgles, onReady: OnReadyCal getTSSchema: this.getTSFileContent, options: this.opts, update: async (newOpts) => { - - getKeys(newOpts).forEach(k => { + getKeys(newOpts).forEach((k) => { //@ts-ignore this.opts[k] = newOpts[k]; }); - - if("fileTable" in newOpts){ + if ("fileTable" in newOpts) { await this.initFileTable(); } - if("restApi" in newOpts){ + if ("restApi" in newOpts) { await this.initRestApi(); } - if("tableConfig" in newOpts){ + if ("tableConfig" in newOpts) { await this.initTableConfig({ type: "prgl.update", newOpts }); } - if("schema" in newOpts){ + if ("schema" in newOpts) { await this.refreshDBO(); } - if("auth" in newOpts){ + if ("auth" in newOpts) { await this.initAuthHandler(); } - if(isEmpty(newOpts)) return; + if (isEmpty(newOpts)) return; - /** - * Some of these changes require clients to reconnect + /** + * Some of these changes require clients to reconnect * While others also affect the server and onReady should be called - */ - if(getKeys(newOpts).every(updatedKey => clientOnlyUpdateKeys.includes(updatedKey as any))){ + */ + if ( + getKeys(newOpts).every((updatedKey) => clientOnlyUpdateKeys.includes(updatedKey as any)) + ) { await this.setSocketEvents(); } else { await this.init(onReady, { type: "prgl.update", newOpts }); @@ -208,22 +226,22 @@ export const initProstgles = async function(this: Prostgles, onReady: OnReadyCal }, restart: () => this.init(onReady, { type: "prgl.restart" }), destroy: async () => { - console.log("destroying prgl instance") + console.log("destroying prgl instance"); this.destroyed = true; if (this.opts.io) { this.opts.io.on("connection", () => { - console.log("Socket connected to destroyed instance") + console.log("Socket connected to destroyed instance"); }); /** Try to close IO without stopping http server */ - if(this.opts.io.sockets.constructor.name === "Namespace"){ + if (this.opts.io.sockets.constructor.name === "Namespace") { for (const socket of this.opts.io.sockets.sockets.values()) { socket._onclose("server shutting down"); } } - if(this.opts.io.engine.constructor.name === 'Server'){ + if (this.opts.io.engine.constructor.name === "Server") { this.opts.io.engine.close(); - } + } } this.fileManager?.destroy(); this.dboBuilder?.destroy(); @@ -234,57 +252,74 @@ export const initProstgles = async function(this: Prostgles, onReady: OnReadyCal await db.$pool.end(); await sleep(1000); return true; - } + }, }; } catch (e: any) { - console.trace(e) + console.trace(e); throw "init issues: " + e.toString(); } -} - -type GetDbConnectionArgs = Pick; -const getDbConnection = function({ dbConnection, onQuery, DEBUG_MODE, dbOptions, onNotice }: GetDbConnectionArgs): { db: DB, pgp: PGP } { - - const onQueryOrError: undefined | ((error: any, ctx: pgPromise.IEventContext) => void) = !onQuery && !DEBUG_MODE? undefined : (error, ctx) => { - if (onQuery) { - onQuery(error, ctx); - } else if (DEBUG_MODE) { - if(error){ - console.error(error, ctx); - } else { - console.log(ctx) - } - } - }; - - const pgp: PGP = pgPromise({ - ...(onQueryOrError ? { - query: ctx => onQueryOrError(undefined, ctx), - error: onQueryOrError - } : {}), - ...((onNotice || DEBUG_MODE) ? { - connect: function ({ client, useCount }) { - const isFresh = !useCount; - if (isFresh && !client.listeners('notice').length) { - client.on('notice', function (msg) { - if (onNotice) { - onNotice(msg, msg?.message); +}; + +type GetDbConnectionArgs = Pick< + ProstglesInitOptions, + "DEBUG_MODE" | "onQuery" | "dbConnection" | "dbOptions" | "onNotice" +>; +const getDbConnection = function ({ + dbConnection, + onQuery, + DEBUG_MODE, + dbOptions, + onNotice, +}: GetDbConnectionArgs): { db: DB; pgp: PGP } { + const onQueryOrError: + | undefined + | ((error: any, ctx: pgPromise.IEventContext) => void) = + !onQuery && !DEBUG_MODE + ? undefined + : (error, ctx) => { + if (onQuery) { + onQuery(error, ctx); + } else if (DEBUG_MODE) { + if (error) { + console.error(error, ctx); } else { - console.log("notice: %j", msg?.message); + console.log(ctx); } - }); + } + }; + + const pgp: PGP = pgPromise({ + ...(onQueryOrError + ? { + query: (ctx) => onQueryOrError(undefined, ctx), + error: onQueryOrError, } - if (isFresh && !client.listeners('error').length) { - client.on('error', function (msg) { - if (onNotice) { - onNotice(msg, msg?.message); - } else { - console.log("error: %j", msg?.message); + : {}), + ...(onNotice || DEBUG_MODE + ? { + connect: function ({ client, useCount }) { + const isFresh = !useCount; + if (isFresh && !client.listeners("notice").length) { + client.on("notice", function (msg) { + if (onNotice) { + onNotice(msg, msg?.message); + } else { + console.log("notice: %j", msg?.message); + } + }); } - }); + if (isFresh && !client.listeners("error").length) { + client.on("error", function (msg) { + if (onNotice) { + onNotice(msg, msg?.message); + } else { + console.log("error: %j", msg?.message); + } + }); + } + }, } - }, - } : {}) + : {}), }); // pgp.pg.defaults.max = 70; @@ -301,10 +336,9 @@ const getDbConnection = function({ dbConnection, onQuery, DEBUG_MODE, dbOptions, // pgp.pg.types.setTypeParser(1114, v => v); // timestamp without time zone // pgp.pg.types.setTypeParser(1184, v => v); // timestamp with time zone // pgp.pg.types.setTypeParser(1182, v => v); // date - pgp.pg.types.setTypeParser(pgp.pg.types.builtins.TIMESTAMP, v => v); // timestamp without time zone - pgp.pg.types.setTypeParser(pgp.pg.types.builtins.TIMESTAMPTZ, v => v); // timestamp with time zone - pgp.pg.types.setTypeParser(pgp.pg.types.builtins.DATE, v => v); // date - + pgp.pg.types.setTypeParser(pgp.pg.types.builtins.TIMESTAMP, (v) => v); // timestamp without time zone + pgp.pg.types.setTypeParser(pgp.pg.types.builtins.TIMESTAMPTZ, (v) => v); // timestamp with time zone + pgp.pg.types.setTypeParser(pgp.pg.types.builtins.DATE, (v) => v); // date if (dbOptions) { Object.assign(pgp.pg.defaults, dbOptions); @@ -312,6 +346,6 @@ const getDbConnection = function({ dbConnection, onQuery, DEBUG_MODE, dbOptions, return { db: pgp(dbConnection), - pgp + pgp, }; -} \ No newline at end of file +}; diff --git a/lib/utils.ts b/lib/utils.ts index 12b09db0..8ecc94c8 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,15 +1,14 @@ - export { get } from "prostgles-types"; export const clone = >(obj: T): T => { - if(typeof structuredClone !== "undefined"){ + if (typeof structuredClone !== "undefined") { return structuredClone(obj); } - + return JSON.parse(JSON.stringify(obj)); -} +}; export const sleep = function (ms: number) { return new Promise((resolve) => { setTimeout(resolve, ms); }); -} \ No newline at end of file +}; diff --git a/package-lock.json b/package-lock.json index 09e66145..014075da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "prostgles-server", - "version": "4.2.178", + "version": "4.2.179", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "prostgles-server", - "version": "4.2.178", + "version": "4.2.179", "license": "MIT", "dependencies": { "@aws-sdk/client-ses": "^3.699.0", @@ -28,7 +28,7 @@ "pg": "^8.11.5", "pg-cursor": "^2.11.0", "pg-promise": "^11.9.1", - "prostgles-types": "^4.0.112" + "prostgles-types": "^4.0.117" }, "devDependencies": { "@types/express": "^4.17.21", @@ -41,6 +41,7 @@ "@typescript-eslint/eslint-plugin": "^8.15.0", "@typescript-eslint/parser": "^8.15.0", "eslint": "^8.51.0", + "prettier": "^3.4.2", "socket.io": "^4.8.1", "typescript": "^5.3.3" } @@ -3625,10 +3626,26 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prostgles-types": { - "version": "4.0.112", - "resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.112.tgz", - "integrity": "sha512-/4E0uNK9yll+0aqM8cflTtz2J4ELLBFCE9ipDXmJ8b1MivRvm7T6UTB5BDM9+5IAKP0iafqVoDiCjpI+G654qA==", + "version": "4.0.117", + "resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.117.tgz", + "integrity": "sha512-TX4liHlRL0jGkZIoeao7312jk9MPamcp3xjGBd5IBWfneAFql73APcLz3GiD+Unt80CErZw82fZOLbxUpsSxyQ==", "license": "MIT" }, "node_modules/punycode": { @@ -6839,10 +6856,16 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true + }, "prostgles-types": { - "version": "4.0.112", - "resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.112.tgz", - "integrity": "sha512-/4E0uNK9yll+0aqM8cflTtz2J4ELLBFCE9ipDXmJ8b1MivRvm7T6UTB5BDM9+5IAKP0iafqVoDiCjpI+G654qA==" + "version": "4.0.117", + "resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.117.tgz", + "integrity": "sha512-TX4liHlRL0jGkZIoeao7312jk9MPamcp3xjGBd5IBWfneAFql73APcLz3GiD+Unt80CErZw82fZOLbxUpsSxyQ==" }, "punycode": { "version": "2.3.1", diff --git a/package.json b/package.json index ebcd936a..0f46bf63 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prostgles-server", - "version": "4.2.178", + "version": "4.2.179", "description": "", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -54,7 +54,7 @@ "pg": "^8.11.5", "pg-cursor": "^2.11.0", "pg-promise": "^11.9.1", - "prostgles-types": "^4.0.112" + "prostgles-types": "^4.0.117" }, "devDependencies": { "@types/express": "^4.17.21", @@ -67,6 +67,7 @@ "@typescript-eslint/eslint-plugin": "^8.15.0", "@typescript-eslint/parser": "^8.15.0", "eslint": "^8.51.0", + "prettier": "^3.4.2", "socket.io": "^4.8.1", "typescript": "^5.3.3" } diff --git a/tests/client/package-lock.json b/tests/client/package-lock.json index f5b4b8c5..5fd307cc 100644 --- a/tests/client/package-lock.json +++ b/tests/client/package-lock.json @@ -10,7 +10,7 @@ "license": "ISC", "dependencies": { "@types/node": "^20.9.2", - "prostgles-client": "^4.0.170", + "prostgles-client": "^4.0.176", "prostgles-types": "^4.0.51", "socket.io-client": "^4.7.5" }, @@ -327,12 +327,12 @@ } }, "node_modules/prostgles-client": { - "version": "4.0.170", - "resolved": "https://registry.npmjs.org/prostgles-client/-/prostgles-client-4.0.170.tgz", - "integrity": "sha512-E9/sS07guDqskjsbAX2wOvm6N7Hx2Fbw6LtOtHSNaF2eiDMNUblkhxFs/Lz5lkO69qzcMIIhwI0Ko0iXXyVkKA==", + "version": "4.0.176", + "resolved": "https://registry.npmjs.org/prostgles-client/-/prostgles-client-4.0.176.tgz", + "integrity": "sha512-1U6jYx0mQ12HNU5YNAG2LsVdzwCBDPoAVQKfXjVD7dv5FSyt17giVsGiurRH18zYU1rjKz6plGhn1WeNM9y4FA==", "license": "MIT", "dependencies": { - "prostgles-types": "^4.0.112" + "prostgles-types": "^4.0.117" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", @@ -348,9 +348,9 @@ } }, "node_modules/prostgles-types": { - "version": "4.0.112", - "resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.112.tgz", - "integrity": "sha512-/4E0uNK9yll+0aqM8cflTtz2J4ELLBFCE9ipDXmJ8b1MivRvm7T6UTB5BDM9+5IAKP0iafqVoDiCjpI+G654qA==", + "version": "4.0.117", + "resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.117.tgz", + "integrity": "sha512-TX4liHlRL0jGkZIoeao7312jk9MPamcp3xjGBd5IBWfneAFql73APcLz3GiD+Unt80CErZw82fZOLbxUpsSxyQ==", "license": "MIT" }, "node_modules/psl": { diff --git a/tests/client/package.json b/tests/client/package.json index a4e90e87..d2f43ecc 100644 --- a/tests/client/package.json +++ b/tests/client/package.json @@ -13,7 +13,7 @@ "license": "ISC", "dependencies": { "@types/node": "^20.9.2", - "prostgles-client": "^4.0.170", + "prostgles-client": "^4.0.176", "prostgles-types": "^4.0.51", "socket.io-client": "^4.7.5" }, diff --git a/tests/server/DBoGenerated.d.ts b/tests/server/DBSchemaGenerated.d.ts similarity index 100% rename from tests/server/DBoGenerated.d.ts rename to tests/server/DBSchemaGenerated.d.ts diff --git a/tests/server/index.ts b/tests/server/index.ts index 3ea8dad1..aa25fe94 100644 --- a/tests/server/index.ts +++ b/tests/server/index.ts @@ -1,60 +1,60 @@ - -import path from 'path'; -import express from 'express'; +import path from "path"; +import express from "express"; import prostgles from "prostgles-server"; const app = express(); -const http = require('http').createServer(app); +const http = require("http").createServer(app); import { testPublishTypes } from "./publishTypeCheck"; import { testPublish } from "./testPublish"; import { testTableConfig } from "./testTableConfig"; testPublishTypes(); -const isClientTest = (process.env.TEST_TYPE === "client"); -const io = !isClientTest? undefined : require("socket.io")(http, { path: "/teztz/s" }); -const ioWatchSchema = !isClientTest? undefined : require("socket.io")(http, { path: "/teztz/sWatchSchema" }); +const isClientTest = process.env.TEST_TYPE === "client"; +const io = !isClientTest ? undefined : require("socket.io")(http, { path: "/teztz/s" }); +const ioWatchSchema = + !isClientTest ? undefined : require("socket.io")(http, { path: "/teztz/sWatchSchema" }); http.listen(3001); import { isomorphicQueries } from "../isomorphicQueries.spec"; import { serverOnlyQueries } from "../serverOnlyQueries.spec"; -import { DBSchemaGenerated } from "./DBoGenerated"; +import { DBSchemaGenerated } from "./DBSchemaGenerated"; -import type { DBOFullyTyped } from "prostgles-server/dist/DBSchemaBuilder"; +import type { DBOFullyTyped } from "prostgles-server/dist/DBSchemaBuilder"; import { spawn } from "child_process"; export type { DBHandlerServer } from "prostgles-server/dist/Prostgles"; let logs = []; export const log = (msg: string, extra?: any, trace?: boolean) => { - const msgs = msg.includes("show-logs")? logs : ["(server): " + msg, extra].filter(v => v); - if(trace){ - console.trace(...msgs); - } else { - console.log(...msgs); - } -} + const msgs = msg.includes("show-logs") ? logs : ["(server): " + msg, extra].filter((v) => v); + if (trace) { + console.trace(...msgs); + } else { + console.log(...msgs); + } +}; const stopTest = (err?) => { - log("Stopping server ...") - if(err) { - console.trace(err); - } - process.exit(err? 1 : 0); -} + log("Stopping server ..."); + if (err) { + console.trace(err); + } + process.exit(err ? 1 : 0); +}; -const sessions: { id: string, user_id: string }[] = []; +const sessions: { id: string; user_id: string }[] = []; type USER = { - id: string; - username: string; - password: string; + id: string; + username: string; + password: string; type: string; -} +}; const users: USER[] = [{ id: "1a", username: "john", password: "secret", type: "default" }]; -process.on('unhandledRejection', (reason, p) => { - console.trace('Unhandled Rejection at:', p, 'reason:', reason) - process.exit(1) +process.on("unhandledRejection", (reason, p) => { + console.trace("Unhandled Rejection at:", p, "reason:", reason); + process.exit(1); }); /** * To create a superuser in linux: @@ -63,226 +63,221 @@ process.on('unhandledRejection', (reason, p) => { * createdb prostgles_server_tests -O api */ const dbConnection = { - host: process.env.POSTGRES_HOST || "localhost", - port: +process.env.POSTGRES_PORT || 5432, - database: process.env.POSTGRES_DB || "prostgles_server_tests", - user: process.env.POSTGRES_USER || "api", - password: process.env.POSTGRES_PASSWORD || "api", + host: process.env.POSTGRES_HOST || "localhost", + port: +process.env.POSTGRES_PORT || 5432, + database: process.env.POSTGRES_DB || "prostgles_server_tests", + user: process.env.POSTGRES_USER || "api", + password: process.env.POSTGRES_PASSWORD || "api", }; -function dd(){ - - const dbo: DBOFullyTyped<{ tbl: { is_view: true; columns: { col1: { type: number } } }}> = 1 as any - if(!dbo) return; - dbo.tbl.find; +function dd() { + const dbo: DBOFullyTyped<{ tbl: { is_view: true; columns: { col1: { type: number } } } }> = + 1 as any; + if (!dbo) return; + dbo.tbl.find; } (async () => { - + if (isClientTest && process.env.TEST_NAME === "useProstgles") { + await prostgles({ + dbConnection, + io: ioWatchSchema, + transactions: true, + schema: { public: 1, prostgles_test: 1 }, + onReady: async ({ dbo, db }) => {}, + publish: "*", + watchSchema: true, + }); + } + + prostgles({ + dbConnection, + sqlFilePath: path.join(__dirname + "/../../init.sql"), + io, + tsGeneratedTypesDir: path.join(__dirname + "/../../"), + transactions: true, + schema: { public: 1, prostgles_test: 1 }, + onLog: async (ev) => { + logs.push(ev); + logs = logs.slice(-10); + if (ev.type === "debug" || ev.type === "connect" || ev.type === "disconnect") { + // log("onLog", ev); + } + }, + tableConfig: testTableConfig, + testRulesOnConnect: true, + fileTable: { + referencedTables: { + users_public_info: { + type: "column", + referenceColumns: { + avatar: { + acceptedContent: "*", + }, + }, + }, + }, + localConfig: { + localFolderPath: path.join(__dirname + "/media"), + }, + expressApp: app, + tableName: "files", + }, + // DEBUG_MODE: true, + restApi: { + expressApp: app, + routePrefix: "/api", + }, + + onSocketConnect: ({ socket, db }) => { + console.log("onSocketConnect", socket.id); + if (isClientTest) { + log("Client connected -> console does not work. use log function. socket.id:", socket.id); + socket.emit("start-test", { server_id: Math.random() }); + socket.on("log", async (data, cb) => { + console.log("Client log ", data); + if (typeof data === "string" && data.includes("show-logs")) { + log(data); + } + }); + socket.on("stop-test", async (err, cb) => { + cb(); + console.log("Client test " + (!err ? "successful" : "failed")); + stopTest(err); + }); + } + }, - if(isClientTest && process.env.TEST_NAME === "useProstgles"){ - await prostgles({ - dbConnection, - io: ioWatchSchema, - transactions: true, - schema: { public: 1, prostgles_test: 1 }, - onReady: async ({ dbo, db }) => {}, - publish: "*", - watchSchema: true, - }); - } + onSocketDisconnect: ({ socket, db }) => { + if (isClientTest) { + log("Client disconnected. socket.id:", socket.id); + } + }, - prostgles({ - dbConnection, - sqlFilePath: path.join(__dirname+'/../../init.sql'), - io, - tsGeneratedTypesDir: path.join(__dirname + '/../../'), - transactions: true, - schema: { public: 1, prostgles_test: 1 }, - onLog: async ev => { - logs.push(ev); - logs = logs.slice(-10); - if(ev.type === "debug" || ev.type === "connect" || ev.type === "disconnect"){ - // log("onLog", ev); - } - }, - tableConfig: testTableConfig, - testRulesOnConnect: true, - fileTable: { - referencedTables: { - users_public_info: { - type: "column", - referenceColumns: { - avatar: { - acceptedContent: "*" - } - } - } - }, - localConfig: { - localFolderPath: path.join(__dirname+'/media'), - }, - expressApp: app, - tableName: "files", - }, - // DEBUG_MODE: true, - restApi: { - expressApp: app, - routePrefix: "/api" - }, - - onSocketConnect: ({ socket, db }) => { - console.log("onSocketConnect", socket.id) - if(isClientTest){ - log("Client connected -> console does not work. use log function. socket.id:", socket.id); - socket.emit("start-test", { server_id: Math.random() }); - socket.on("log", async (data, cb) => { - console.log("Client log ", data); - if(typeof data === "string" && data.includes("show-logs")){ - log(data); - } - }); - socket.on("stop-test", async (err, cb) => { - cb(); - console.log("Client test " + (!err? "successful" : "failed")); - stopTest(err); - }); - } - - }, + publishRawSQL: async (params) => { + return true; // Boolean(user && user.type === "admin") + }, + auth: { + sidKeyName: "token", + getUser: async (sid) => { + if (sid) { + const s = sessions.find((s) => s.id === sid); + if (s) { + const user = users.find((u) => s && s.user_id === u.id); + if (user) { + return { + sid: s.id, + user, + clientUser: { sid: s.id, uid: user.id, id: user.id, type: user.type }, + }; + } + } + } + return undefined; + }, + login: async (loginData) => { + if (loginData.type !== "username") throw "Only username login is supported"; + const { username, password } = loginData; + const u = users.find((u) => u.username === username && u.password === password); + if (!u) throw "something went wrong: " + JSON.stringify({ username, password }); + let s = sessions.find((s) => s.user_id === u.id); + if (!s) { + s = { id: "SID" + Date.now(), user_id: u.id }; + sessions.push(s); + } + log("Logged in!"); + return { sid: s.id, expires: Infinity, onExpiration: "redirect" }; + }, + cacheSession: { + getSession: async (sid) => { + const s = sessions.find((s) => s.id === sid); + return s ? { sid: s.id, expires: Infinity, onExpiration: "redirect" } : undefined; + }, + }, + expressConfig: { + app, + onGetRequestOK(req, res, params) { + log(req.originalUrl); + res.sendFile(path.join(__dirname, "../../index.html")); + }, + registrations: { + websiteUrl: "http://localhost:3001", + OAuthProviders: { + github: { + clientID: "GITHUB_CLIENT_ID", + clientSecret: "GITHUB", + }, + }, + onProviderLoginStart: async () => ({ ok: true }), + onProviderLoginFail: console.error, + }, + }, + }, + publishMethods: async (params) => { + return { + get: () => 222, + }; + }, + publish: testPublish, + joins: [ + { + tables: ["items", "items2"], + on: [{ name: "name" }], + type: "many-many", + }, + { + tables: ["items2", "items3"], + on: [{ name: "name" }], + type: "many-many", + }, + { + tables: ["items4a", "items"], + on: [{ items_id: "id" }], + type: "many-many", + }, + { + tables: ["items4a", "items2"], + on: [{ items2_id: "id" }], + type: "many-many", + }, + { + tables: ["items_multi", "items"], + on: [{ items0_id: "id" }, { items1_id: "id" }, { items2_id: "id" }, { items3_id: "id" }], + type: "many-many", + }, + ], + onReady: async ({ dbo, db }) => { + log("prostgles onReady"); - onSocketDisconnect: ({ socket, db }) => { - if(isClientTest){ - log("Client disconnected. socket.id:", socket.id); - } - - }, - - publishRawSQL: async (params) => { - return true;// Boolean(user && user.type === "admin") - }, - auth: { - sidKeyName: "token", - getUser: async (sid) => { - if(sid){ - const s = sessions.find(s => s.id === sid); - if(s) { - const user = users.find(u => s && s.user_id === u.id); - if(user) { - return { sid: s.id, user, clientUser: { sid: s.id, uid: user.id, id: user.id, type: user.type } } - } - } - } - return undefined; - }, - login: async (loginData) => { - if(loginData.type !== "username") throw "Only username login is supported"; - const { username, password } = loginData; - const u = users.find(u => u.username === username && u.password === password); - if(!u) throw "something went wrong: " + JSON.stringify({ username, password }); - let s = sessions.find(s => s.user_id === u.id) - if(!s){ - s = { id: "SID" + Date.now(), user_id: u.id } - sessions.push(s) - } - log("Logged in!") - return { sid: s.id, expires: Infinity, onExpiration: "redirect" } - }, - cacheSession: { - getSession: async (sid) => { - const s = sessions.find(s => s.id === sid); - return s? { sid: s.id, expires: Infinity, onExpiration: "redirect" } : undefined - } - }, - expressConfig: { - app, - onGetRequestOK(req, res, params) { - log(req.originalUrl) - res.sendFile(path.join(__dirname, '../../index.html')); - }, - registrations: { - websiteUrl: "http://localhost:3001", - OAuthProviders: { - github: { - clientID: "GITHUB_CLIENT_ID", - clientSecret: "GITHUB" - }, - }, - onProviderLoginStart: async () => ({ ok: true }), - onProviderLoginFail: console.error, - }, - } - }, - publishMethods: async (params) => { - return { - get: () => 222 - } - }, - publish: testPublish, - joins: [ - { - tables: ["items", "items2"], - on: [{ name: "name" }], - type: "many-many" - }, - { - tables: ["items2", "items3"], - on: [{ name: "name" }], - type: "many-many" - }, - { - tables: ["items4a", "items"], - on: [{ items_id: "id" }], - type: "many-many" - }, - { - tables: ["items4a", "items2"], - on: [{ items2_id: "id" }], - type: "many-many" - }, - { - tables: ["items_multi", "items"], - on: [ - { items0_id: "id" }, - { items1_id: "id" }, - { items2_id: "id" }, - { items3_id: "id" }, - ], - type: "many-many" - } - ], - onReady: async ({ dbo, db }) => { - log("prostgles onReady"); - - try { - - if(isClientTest){ - const execPath = path.resolve(`${__dirname}/../../../client`); - /** For some reason the below doesn't work anymore */ - // const proc = spawn("npm", ["run", "test"], { cwd: execPath, stdio: "inherit" }); - - spawn("node", [ - // "--inspect-brk", - "dist/client/index.js" - ], { cwd: execPath, stdio: "inherit" }); + try { + if (isClientTest) { + const execPath = path.resolve(`${__dirname}/../../../client`); + /** For some reason the below doesn't work anymore */ + // const proc = spawn("npm", ["run", "test"], { cwd: execPath, stdio: "inherit" }); - log("Waiting for client..."); - - } else if(process.env.TEST_TYPE === "server"){ + spawn( + "node", + [ + // "--inspect-brk", + "dist/client/index.js", + ], + { cwd: execPath, stdio: "inherit" } + ); - await serverOnlyQueries(dbo as any); - log("Server-only query tests successful"); - await isomorphicQueries(dbo as any, log); - log("Server isomorphic tests successful"); + log("Waiting for client..."); + } else if (process.env.TEST_TYPE === "server") { + await serverOnlyQueries(dbo as any); + log("Server-only query tests successful"); + await isomorphicQueries(dbo as any, log); + log("Server isomorphic tests successful"); - stopTest() - } - } catch(err) { - console.trace(err) - if(process.env.TEST_TYPE){ - stopTest(err ?? "Error") - } - } - - }, - }); -})(); \ No newline at end of file + stopTest(); + } + } catch (err) { + console.trace(err); + if (process.env.TEST_TYPE) { + stopTest(err ?? "Error"); + } + } + }, + }); +})(); diff --git a/tests/server/package-lock.json b/tests/server/package-lock.json index 7c0a74be..d3530bc6 100644 --- a/tests/server/package-lock.json +++ b/tests/server/package-lock.json @@ -21,7 +21,7 @@ }, "../..": { "name": "prostgles-server", - "version": "4.2.178", + "version": "4.2.179", "license": "MIT", "dependencies": { "@aws-sdk/client-ses": "^3.699.0", @@ -43,7 +43,7 @@ "pg": "^8.11.5", "pg-cursor": "^2.11.0", "pg-promise": "^11.9.1", - "prostgles-types": "^4.0.112" + "prostgles-types": "^4.0.117" }, "devDependencies": { "@types/express": "^4.17.21", @@ -56,6 +56,7 @@ "@typescript-eslint/eslint-plugin": "^8.15.0", "@typescript-eslint/parser": "^8.15.0", "eslint": "^8.51.0", + "prettier": "^3.4.2", "socket.io": "^4.8.1", "typescript": "^5.3.3" } @@ -1826,7 +1827,8 @@ "pg": "^8.11.5", "pg-cursor": "^2.11.0", "pg-promise": "^11.9.1", - "prostgles-types": "^4.0.112", + "prettier": "^3.4.2", + "prostgles-types": "^4.0.117", "socket.io": "^4.8.1", "typescript": "^5.3.3" } diff --git a/tests/server/server.ts b/tests/server/server.ts index c0275066..5ecb49f3 100644 --- a/tests/server/server.ts +++ b/tests/server/server.ts @@ -1,13 +1,12 @@ - -import path from 'path'; -import prostgles from "prostgles-server"; +import path from "path"; +import prostgles from "prostgles-server"; import { DBSchemaGenerated } from "./DBoGenerated"; prostgles({ dbConnection: { - connectionString: process.env.DB_CONNECTION + connectionString: process.env.DB_CONNECTION, }, - tsGeneratedTypesDir: path.join(__dirname + '/'), + tsGeneratedTypesDir: path.join(__dirname + "/"), tableConfig: { user: { columns: { @@ -18,18 +17,15 @@ prostgles({ jsonbSchemaType: { showIntro: { type: "boolean", optional: true }, theme: { enum: ["light", "dark", "auto"], optional: true }, - two_factor_auth: { oneOf: [ - { type: { enum: ["app"] } } - ] } - } + two_factor_auth: { oneOf: [{ type: { enum: ["app"] } }] }, + }, }, - } - } + }, + }, }, onReady: async (db) => { const user = await db.users.findOne({ id: 123 }); - - // user.preferences.theme = - } + // user.preferences.theme = + }, });