json-rest-api 1.0.29 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +10 -670
- package/LICENSE-GPL-3.0 +674 -0
- package/LICENSE-MIT +21 -0
- package/README.md +39 -30
- package/docs/API.md +744 -1273
- package/docs/GUIDE/01-getting-started.md +338 -0
- package/docs/GUIDE/02-resources-and-relationships.md +33 -0
- package/docs/GUIDE/03-running-example.md +87 -0
- package/docs/GUIDE/04-creating-and-querying.md +181 -0
- package/docs/GUIDE/05-belongs-to.md +177 -0
- package/docs/GUIDE/06-has-many.md +184 -0
- package/docs/GUIDE/07-polymorphic-relationships.md +191 -0
- package/docs/GUIDE/08-many-to-many.md +184 -0
- package/docs/GUIDE/09-pagination-and-sorting.md +149 -0
- package/docs/GUIDE/10-put-and-patch.md +174 -0
- package/docs/GUIDE/11-relationship-endpoints.md +174 -0
- package/docs/GUIDE/12-field-transformations.md +287 -0
- package/docs/GUIDE/13-hooks-and-lifecycle.md +479 -0
- package/docs/GUIDE/14-custom-resource-methods.md +125 -0
- package/docs/GUIDE/15-query-projections.md +266 -0
- package/docs/GUIDE/{GUIDE_X_Autofiltering.md → 16-autofiltering.md} +77 -15
- package/docs/GUIDE/{GUIDE_X_Row_Policies.md → 17-row-policies.md} +137 -44
- package/docs/GUIDE/18-labels.md +86 -0
- package/docs/GUIDE/19-managed-transactions.md +179 -0
- package/docs/GUIDE/20-transaction-outcomes.md +207 -0
- package/docs/GUIDE/21-schema-and-migrations.md +733 -0
- package/docs/GUIDE/22-version-field-migrations.md +208 -0
- package/docs/GUIDE/23-fastify.md +179 -0
- package/docs/GUIDE/24-cors.md +172 -0
- package/docs/GUIDE/{GUIDE_X_URL_Management.md → 25-url-management.md} +7 -1
- package/docs/GUIDE/26-file-uploads.md +278 -0
- package/docs/GUIDE/27-bulk-operations.md +277 -0
- package/docs/GUIDE/28-socketio.md +325 -0
- package/docs/GUIDE/29-writing-plugins.md +278 -0
- package/docs/GUIDE/30-backend-capabilities.md +103 -0
- package/docs/GUIDE/31-positioning.md +181 -0
- package/docs/GUIDE/{GUIDE_Y_Appendices.md → 32-date-and-time.md} +74 -30
- package/docs/GUIDE/33-migrating-to-v2.md +3838 -0
- package/docs/GUIDE/index.md +51 -59
- package/docs/QUICKSTART.md +120 -287
- package/docs/architecture.md +75 -0
- package/docs/contributing.md +178 -0
- package/index.d.ts +37 -0
- package/index.js +3 -22
- package/lib/enhanced-logger.js +91 -85
- package/lib/error-context.js +445 -5
- package/lib/error-formatter.js +219 -59
- package/lib/knex-transaction.js +70 -0
- package/lib/logger-types.d.ts +22 -0
- package/lib/missing-package.js +4 -0
- package/lib/rest-api-errors.js +103 -8
- package/lib/runtime/json-rest-api.js +172 -0
- package/lib/transaction-types.d.ts +59 -0
- package/package.json +97 -27
- package/plugins/core/bulk-operations-plugin.js +191 -184
- package/plugins/core/connectors/express-plugin.js +148 -219
- package/plugins/core/connectors/fastify-plugin.js +125 -82
- package/plugins/core/connectors/lib/busboy-detector.js +74 -156
- package/plugins/core/connectors/lib/connector-core.js +144 -20
- package/plugins/core/connectors/lib/formidable-detector.js +63 -120
- package/plugins/core/connectors/lib/http-validators.js +77 -0
- package/plugins/core/connectors/lib/multipart-helpers.js +18 -0
- package/plugins/core/connectors/lib/request-helpers.js +10 -1
- package/plugins/core/connectors/lib/transport-http-helpers.js +89 -8
- package/plugins/core/connectors/lib/transport-route-schemas.js +7 -4
- package/plugins/core/file-handling-plugin.js +87 -177
- package/plugins/core/lib/anyapi/anyapi-registry.js +586 -418
- package/plugins/core/lib/anyapi/canonical-link-store.js +507 -0
- package/plugins/core/lib/anyapi/canonical-relationship-reader.js +598 -0
- package/plugins/core/lib/anyapi/schema-utils.js +120 -56
- package/plugins/core/lib/anyapi/stored-definition.js +109 -0
- package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +0 -64
- package/plugins/core/lib/dbIntrospection.js +228 -21
- package/plugins/core/lib/dbTablesOperations.js +409 -92
- package/plugins/core/lib/fractional-positioning.js +12 -48
- package/plugins/core/lib/querying/identifier-query.js +19 -0
- package/plugins/core/lib/querying/include-query-helpers.js +364 -0
- package/plugins/core/lib/querying/include-to-many.js +428 -0
- package/plugins/core/lib/querying/include-to-one.js +579 -0
- package/plugins/core/lib/querying/knex-cross-table-search.js +31 -307
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +99 -401
- package/plugins/core/lib/querying/knex-pagination-helpers.js +48 -317
- package/plugins/core/lib/querying/knex-process-includes.js +5 -110
- package/plugins/core/lib/querying/knex-query-helpers-base.js +21 -56
- package/plugins/core/lib/querying/knex-query-helpers.js +298 -448
- package/plugins/core/lib/querying/knex-relationship-includes.js +120 -2138
- package/plugins/core/lib/querying/knex-window-queries.js +62 -253
- package/plugins/core/lib/querying/query-builder-utils.js +44 -4
- package/plugins/core/lib/querying/query-constraint.d.ts +12 -0
- package/plugins/core/lib/querying/query-constraint.js +27 -0
- package/plugins/core/lib/querying/query-field-sort-helpers.js +134 -37
- package/plugins/core/lib/querying/relationship-identifiers.js +208 -0
- package/plugins/core/lib/querying/sort-helpers.js +13 -1
- package/plugins/core/lib/querying/storage-adapter-utils.js +13 -5
- package/plugins/core/lib/querying/url-helpers.js +11 -4
- package/plugins/core/lib/querying-writing/compile-schemas.js +148 -249
- package/plugins/core/lib/querying-writing/connectors-query-parser.js +33 -118
- package/plugins/core/lib/querying-writing/database-capabilities.js +149 -223
- package/plugins/core/lib/querying-writing/database-value-normalizers.js +179 -194
- package/plugins/core/lib/querying-writing/default-data-helpers.js +15 -45
- package/plugins/core/lib/querying-writing/field-selection-types.d.ts +43 -0
- package/plugins/core/lib/querying-writing/field-utils.js +113 -92
- package/plugins/core/lib/querying-writing/knex-constants.d.ts +18 -0
- package/plugins/core/lib/querying-writing/knex-constants.js +2 -26
- package/plugins/core/lib/querying-writing/knex-field-helpers.js +100 -290
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +31 -120
- package/plugins/core/lib/querying-writing/query-field-helpers.js +108 -32
- package/plugins/core/lib/querying-writing/query-field-types.d.ts +60 -0
- package/plugins/core/lib/querying-writing/relationship-contracts.js +33 -13
- package/plugins/core/lib/querying-writing/request-contracts.js +93 -38
- package/plugins/core/lib/querying-writing/resource-id-normalization.js +23 -5
- package/plugins/core/lib/querying-writing/response-options.js +35 -0
- package/plugins/core/lib/querying-writing/schema-helpers.js +203 -324
- package/plugins/core/lib/querying-writing/scope-validations.js +92 -290
- package/plugins/core/lib/querying-writing/simplified-helpers.js +113 -712
- package/plugins/core/lib/storage/canonical-data-helpers.js +683 -0
- package/plugins/core/lib/storage/canonical-storage-mapping.js +51 -20
- package/plugins/core/lib/storage/ordinary-data-helpers.js +710 -0
- package/plugins/core/lib/storage/storage-adapter.js +128 -62
- package/plugins/core/lib/storage/storage-mapping.js +78 -28
- package/plugins/core/lib/storage/storage-types.d.ts +502 -0
- package/plugins/core/lib/writing/many-to-many-manipulations.js +114 -284
- package/plugins/core/lib/writing/relationship-processor.js +94 -158
- package/plugins/core/lib/writing/resource-version.js +172 -0
- package/plugins/core/lib/writing/reverse-relationship-manipulations.js +110 -0
- package/plugins/core/rest-api-anyapi-knex-plugin.js +290 -2475
- package/plugins/core/rest-api-autofilter-plugin.js +17 -19
- package/plugins/core/rest-api-cors-plugin.js +52 -205
- package/plugins/core/rest-api-knex-plugin.js +45 -1077
- package/plugins/core/rest-api-label-plugin.js +25 -38
- package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +5 -5
- package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +9 -6
- package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +7 -26
- package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +20 -26
- package/plugins/core/rest-api-plugin-methods/add-route.js +4 -6
- package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +42 -13
- package/plugins/core/rest-api-plugin-methods/check-permissions.js +11 -5
- package/plugins/core/rest-api-plugin-methods/common.js +291 -395
- package/plugins/core/rest-api-plugin-methods/delete-relationship.js +49 -47
- package/plugins/core/rest-api-plugin-methods/delete.js +31 -25
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +108 -148
- package/plugins/core/rest-api-plugin-methods/get-related.js +113 -305
- package/plugins/core/rest-api-plugin-methods/get-relationship.js +25 -18
- package/plugins/core/rest-api-plugin-methods/get.js +36 -68
- package/plugins/core/rest-api-plugin-methods/lifecycle-types.d.ts +225 -0
- package/plugins/core/rest-api-plugin-methods/patch-relationship.js +28 -25
- package/plugins/core/rest-api-plugin-methods/patch.js +46 -99
- package/plugins/core/rest-api-plugin-methods/post-relationship.js +49 -53
- package/plugins/core/rest-api-plugin-methods/post.js +51 -51
- package/plugins/core/rest-api-plugin-methods/put.js +69 -148
- package/plugins/core/rest-api-plugin-methods/query.js +37 -81
- package/plugins/core/rest-api-plugin-methods/release.js +2 -1
- package/plugins/core/rest-api-plugin.js +32 -46
- package/plugins/core/rest-api-positioning-plugin.js +183 -628
- package/plugins/core/rest-api-query-projections-plugin.js +19 -102
- package/plugins/core/rest-api-row-policy-plugin.js +3 -3
- package/plugins/core/socketio-plugin.js +364 -344
- package/plugins/storage/local-storage.js +40 -36
- package/types/anyapi.d.ts +30 -0
- package/types/autofilter.d.ts +57 -0
- package/types/bulk-methods.d.ts +63 -0
- package/types/errors.d.ts +118 -0
- package/types/fastify-options.d.ts +19 -0
- package/types/file-detectors.d.ts +33 -0
- package/types/file-storage.d.ts +48 -0
- package/types/hook-context.d.ts +180 -0
- package/types/plugin-options.d.ts +65 -0
- package/types/relationship-methods.d.ts +74 -0
- package/types/representations.d.ts +50 -0
- package/types/resource-methods.d.ts +95 -0
- package/types/resource-schema.d.ts +157 -0
- package/types/row-policy.d.ts +31 -0
- package/types/runtime.d.ts +132 -0
- package/types/transactions.d.ts +21 -0
- package/.claude/agents/library-guide-expert.md +0 -30
- package/.claude/agents/rest-api-internals-expert.md +0 -54
- package/.claude/settings.local.json +0 -70
- package/AGENTS.md +0 -34
- package/CLAUDE.md +0 -160
- package/CRUSH.md +0 -55
- package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +0 -180
- package/SINGLE_VALIDATION_CONTRACT_TODO.txt +0 -172
- package/TODO.todo +0 -13
- package/connectors_ideas/fastify.js +0 -217
- package/connectors_ideas/koa-plugin.js +0 -112
- package/docs/.bundle/config +0 -2
- package/docs/COMPARISON.md +0 -433
- package/docs/GUIDE/GUIDE_1_Initial_Setup.md +0 -1057
- package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +0 -88
- package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +0 -808
- package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +0 -491
- package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +0 -398
- package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +0 -898
- package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +0 -712
- package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +0 -1247
- package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +0 -748
- package/docs/GUIDE/GUIDE_2_9_Relationships_Urls.md +0 -517
- package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +0 -66
- package/docs/GUIDE/GUIDE_3_Field_Transformations.md +0 -1252
- package/docs/GUIDE/GUIDE_5_Non-Db_Resources.md +0 -1107
- package/docs/GUIDE/GUIDE_6_Positioning.md +0 -692
- package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +0 -1491
- package/docs/GUIDE/GUIDE_X_Bulk_Operations.md +0 -732
- package/docs/GUIDE/GUIDE_X_Cors.md +0 -1957
- package/docs/GUIDE/GUIDE_X_Fastify.md +0 -78
- package/docs/GUIDE/GUIDE_X_File_Uploads.md +0 -729
- package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +0 -370
- package/docs/GUIDE/GUIDE_X_Query_Projections.md +0 -159
- package/docs/GUIDE/GUIDE_X_SocketIO.md +0 -1040
- package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +0 -201
- package/docs/Gemfile +0 -6
- package/docs/Gemfile.lock +0 -308
- package/docs/ONBOARDING.md +0 -315
- package/docs/_config.yml +0 -36
- package/docs/_layouts/default.html +0 -123
- package/docs/apple-touch-icon.svg +0 -4
- package/docs/assets/css/style.scss +0 -392
- package/docs/favicon.svg +0 -4
- package/docs/index.md +0 -164
- package/docs/mini-guides/file-uploads-complete.md +0 -161
- package/eslint.config.js +0 -13
- package/examples/url-override-example.js +0 -137
- package/fixing_plan.md +0 -112
- package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +0 -657
- package/plugins/core/lib/querying-writing/return-record-settings.js +0 -47
- package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +0 -110
- package/plugins/storage/s3-storage.js +0 -93
- package/quickTest.js +0 -136
- package/scripts/debug-anyapi-review-filter.mjs +0 -63
- package/scripts/inspect-anyapi-descriptor-country.mjs +0 -19
- package/scripts/inspect-anyapi-descriptor.mjs +0 -19
- package/scripts/run-docs.js +0 -133
- package/test-debug.js +0 -59
- package/tests/README.md +0 -202
- package/tests/TEST_TEMPLATE.test.js +0 -178
- package/tests/anyapi-basic.test.js +0 -105
- package/tests/anyapi-cursor-pagination.test.js +0 -148
- package/tests/anyapi-custom-idproperty.test.js +0 -163
- package/tests/autofilter.test.js +0 -463
- package/tests/bulk-operations.test.js +0 -657
- package/tests/computed-fields.test.js +0 -586
- package/tests/cors.test.js +0 -834
- package/tests/curl.test.js +0 -467
- package/tests/custom-id-mysql-returning-fallback.test.js +0 -128
- package/tests/custom-id-simplified-post.test.js +0 -75
- package/tests/custom-idproperty-relationships.test.js +0 -932
- package/tests/db-introspection.test.js +0 -521
- package/tests/db-table-operations.test.js +0 -798
- package/tests/fastify-plugin.test.js +0 -184
- package/tests/field-getters.test.js +0 -370
- package/tests/field-setters.test.js +0 -306
- package/tests/file-handling.test.js +0 -155
- package/tests/fixtures/api-configs.js +0 -2566
- package/tests/full-jsonapi-belongsto-linkage.test.js +0 -150
- package/tests/helpers/fake-fastify.js +0 -99
- package/tests/helpers/storage-mode.js +0 -65
- package/tests/helpers/test-utils.js +0 -203
- package/tests/http-connectors-parity.test.js +0 -395
- package/tests/id-normalization.test.js +0 -444
- package/tests/include-depth-validation.test.js +0 -331
- package/tests/includes.test.js +0 -427
- package/tests/local-storage.test.js +0 -96
- package/tests/logical-id-write-validation.test.js +0 -151
- package/tests/nested-includes.test.js +0 -617
- package/tests/pagination-boundaries.test.js +0 -154
- package/tests/pagination-contract.test.js +0 -122
- package/tests/pagination-cursor-multifield.test.js +0 -641
- package/tests/pagination-enhanced.test.js +0 -563
- package/tests/polymorphic-relationships.test.js +0 -347
- package/tests/polymorphic-simple.test.js +0 -148
- package/tests/positioning-advanced.test.js +0 -692
- package/tests/positioning-extended.test.js +0 -915
- package/tests/positioning-multifilter.test.js +0 -699
- package/tests/positioning.test.js +0 -560
- package/tests/post-commit-hooks.test.js +0 -120
- package/tests/projected-fields.test.js +0 -319
- package/tests/public-exports.test.js +0 -15
- package/tests/put-create-parity.test.js +0 -216
- package/tests/queries.test.js +0 -521
- package/tests/query-limits.test.js +0 -584
- package/tests/relationship-endpoints.test.js +0 -620
- package/tests/relationship-include-storage-adapter.test.js +0 -56
- package/tests/relationships.test.js +0 -681
- package/tests/rest-api.test.js +0 -337
- package/tests/return-record-settings.test.js +0 -184
- package/tests/row-policy.test.js +0 -489
- package/tests/s3-storage.test.js +0 -38
- package/tests/searchschema-knex-behavior.test.js +0 -187
- package/tests/searchschema-merge.test.js +0 -151
- package/tests/socketio.test.js +0 -860
- package/tests/storage-mapping.test.js +0 -362
- package/tests/temporal-boundaries.test.js +0 -208
- package/tests/test-development-guide.md +0 -370
- package/tests/virtual-fields.test.js +0 -404
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { DirectResourceId, ResourceFormat, WriteReturning } from './representations.js'
|
|
2
|
+
import type { RemovedResourceOptions } from './resource-methods.js'
|
|
3
|
+
import type { Knex } from 'knex'
|
|
4
|
+
import type { AnyapiRegistry, AnyapiSchemaChange, AnyapiSlotCounts } from './anyapi.js'
|
|
5
|
+
|
|
6
|
+
export type RestApiPluginOptions = RemovedResourceOptions & {
|
|
7
|
+
format?: ResourceFormat
|
|
8
|
+
returning?: WriteReturning
|
|
9
|
+
queryDefaultLimit?: number
|
|
10
|
+
queryMaxLimit?: number
|
|
11
|
+
includeDepthLimit?: number
|
|
12
|
+
enablePaginationCounts?: boolean
|
|
13
|
+
idProperty?: string
|
|
14
|
+
normalizeId?: (value: unknown) => DirectResourceId | null | undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface HttpConnectorOptions {
|
|
18
|
+
mountPath?: string
|
|
19
|
+
publicBaseUrl?: string
|
|
20
|
+
strictContentType?: boolean
|
|
21
|
+
handle404?: boolean
|
|
22
|
+
httpValidators?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ResourceVersionOptions {
|
|
26
|
+
// Compilation validates that this names a stored, untransformed string attribute.
|
|
27
|
+
versionField?: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type CorsOrigin = string | RegExp | ((origin: string) => boolean | Promise<boolean>) | CorsOrigin[]
|
|
31
|
+
export interface CorsPluginOptions {
|
|
32
|
+
origin?: CorsOrigin
|
|
33
|
+
credentials?: boolean
|
|
34
|
+
methods?: string[]
|
|
35
|
+
allowedHeaders?: string[]
|
|
36
|
+
exposedHeaders?: string[]
|
|
37
|
+
maxAge?: number
|
|
38
|
+
optionsSuccessStatus?: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface RestApiKnexPluginOptions {
|
|
42
|
+
knex: Knex
|
|
43
|
+
}
|
|
44
|
+
export interface RestApiAnyapiKnexPluginOptions extends RestApiKnexPluginOptions {
|
|
45
|
+
tenantId?: string
|
|
46
|
+
// Share one registry across tenant APIs on the same Knex instance.
|
|
47
|
+
registry?: AnyapiRegistry
|
|
48
|
+
// false when migrations own the canonical tables.
|
|
49
|
+
ensureSchema?: boolean
|
|
50
|
+
// false leaves a shared Knex instance open when this API is released.
|
|
51
|
+
ownsKnex?: boolean
|
|
52
|
+
slots?: AnyapiSlotCounts
|
|
53
|
+
// Called after a committed schema change, for example to notify other servers.
|
|
54
|
+
onSchemaChanged?: (change: AnyapiSchemaChange) => void | Promise<void>
|
|
55
|
+
// Milliseconds between schema version checks; 0 checks on every request.
|
|
56
|
+
schemaCheckInterval?: number
|
|
57
|
+
}
|
|
58
|
+
export interface BulkOperationsPluginOptions {
|
|
59
|
+
maxBulkOperations?: number
|
|
60
|
+
defaultAtomic?: boolean
|
|
61
|
+
}
|
|
62
|
+
export interface LabelPluginOptions {
|
|
63
|
+
preferNameFields?: string[]
|
|
64
|
+
disable?: boolean
|
|
65
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CollectionResult, DirectResourceId, InputResourceIdentifier, JsonApiDocument,
|
|
3
|
+
JsonApiResource, PlainResource, ResourceFormat, ResourceIdentifier
|
|
4
|
+
} from './representations.js'
|
|
5
|
+
import type { QueryParams, RemovedResourceOptions, SelectionParams, TransactionParam } from './resource-methods.js'
|
|
6
|
+
import type { ManagedTransactionParam } from './transactions.js'
|
|
7
|
+
|
|
8
|
+
// Type-only descriptions of the resource's configured relationships.
|
|
9
|
+
export interface ToOneRelationship<Fields extends object = Record<string, unknown>, Type extends string = string> {
|
|
10
|
+
cardinality: 'one'
|
|
11
|
+
fields: Fields
|
|
12
|
+
type: Type
|
|
13
|
+
}
|
|
14
|
+
export interface ToManyRelationship<Fields extends object = Record<string, unknown>, Type extends string = string> {
|
|
15
|
+
cardinality: 'many'
|
|
16
|
+
fields: Fields
|
|
17
|
+
type: Type
|
|
18
|
+
}
|
|
19
|
+
type RelationshipShape = ToOneRelationship<object> | ToManyRelationship<object>
|
|
20
|
+
type RelationshipKeys<Relations> = Extract<keyof Relations, string>
|
|
21
|
+
type ToManyKeys<Relations> = {
|
|
22
|
+
[Key in keyof Relations]: Extract<Relations[Key], { cardinality: 'many' }> extends never ? never : Key
|
|
23
|
+
}[keyof Relations] & string
|
|
24
|
+
export type RelationshipLinkage<Relation extends RelationshipShape> = Relation extends ToManyRelationship<object>
|
|
25
|
+
? ResourceIdentifier<Relation['type']>[] : ResourceIdentifier<Relation['type']> | null
|
|
26
|
+
export type RelationshipInput<Relation extends RelationshipShape> = Relation extends ToManyRelationship<object>
|
|
27
|
+
? InputResourceIdentifier<Relation['type']>[] : InputResourceIdentifier<Relation['type']> | null
|
|
28
|
+
export type RelatedResourceResult<Relation extends RelationshipShape, Format extends ResourceFormat> =
|
|
29
|
+
Relation extends ToManyRelationship<object>
|
|
30
|
+
? CollectionResult<Format, Relation['fields'], Relation['type']>
|
|
31
|
+
: Format extends 'plain' ? PlainResource<Relation['fields']> | null
|
|
32
|
+
: JsonApiDocument<JsonApiResource<Relation['fields'], Relation['type']> | null>
|
|
33
|
+
type RelationshipOptions<Name extends string> = RemovedResourceOptions & TransactionParam & {
|
|
34
|
+
id: DirectResourceId
|
|
35
|
+
relationshipName: Name
|
|
36
|
+
}
|
|
37
|
+
type RelatedQueryParams<Relation extends RelationshipShape> = Relation extends ToManyRelationship<object> ? QueryParams : SelectionParams
|
|
38
|
+
type RelationshipWriteOptions<Name extends string> = Omit<RelationshipOptions<Name>, 'transaction'> & ManagedTransactionParam
|
|
39
|
+
|
|
40
|
+
export interface RelationshipMethods<
|
|
41
|
+
Relations extends { [Key in keyof Relations]: RelationshipShape } = Record<string, ToOneRelationship | ToManyRelationship>,
|
|
42
|
+
DefaultFormat extends ResourceFormat = 'plain', Context extends object = object
|
|
43
|
+
> {
|
|
44
|
+
getRelationship<Name extends RelationshipKeys<Relations>>(
|
|
45
|
+
params: RelationshipOptions<Name> & { format?: ResourceFormat }, context?: Context
|
|
46
|
+
): Promise<JsonApiDocument<RelationshipLinkage<Relations[Name]>>>
|
|
47
|
+
getRelated<Name extends RelationshipKeys<Relations>, Format extends ResourceFormat = DefaultFormat>(
|
|
48
|
+
params: RelationshipOptions<Name> & {
|
|
49
|
+
format?: Format
|
|
50
|
+
queryParams?: NoInfer<RelatedQueryParams<Relations[Name]>>
|
|
51
|
+
}, context?: Context
|
|
52
|
+
): Promise<RelatedResourceResult<Relations[Name], Format>>
|
|
53
|
+
patchRelationship<Name extends RelationshipKeys<Relations>>(
|
|
54
|
+
params: RelationshipWriteOptions<Name> & {
|
|
55
|
+
format?: ResourceFormat
|
|
56
|
+
relationshipData: NoInfer<RelationshipInput<Relations[Name]>>
|
|
57
|
+
expectedVersion?: string
|
|
58
|
+
}, context?: Context
|
|
59
|
+
): Promise<void>
|
|
60
|
+
postRelationship<Name extends ToManyKeys<Relations>>(
|
|
61
|
+
params: RelationshipWriteOptions<Name> & {
|
|
62
|
+
format?: ResourceFormat
|
|
63
|
+
relationshipData: InputResourceIdentifier<NoInfer<Relations[Name]['type']>>[]
|
|
64
|
+
expectedVersion?: string
|
|
65
|
+
}, context?: Context
|
|
66
|
+
): Promise<void>
|
|
67
|
+
deleteRelationship<Name extends ToManyKeys<Relations>>(
|
|
68
|
+
params: RelationshipWriteOptions<Name> & {
|
|
69
|
+
format?: ResourceFormat
|
|
70
|
+
relationshipData: InputResourceIdentifier<NoInfer<Relations[Name]['type']>>[]
|
|
71
|
+
expectedVersion?: string
|
|
72
|
+
}, context?: Context
|
|
73
|
+
): Promise<void>
|
|
74
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type ResourceFormat = 'plain' | 'jsonapi'
|
|
2
|
+
export type WriteReturning = 'none' | 'minimal' | 'full'
|
|
3
|
+
export type InputResourceId = string | number
|
|
4
|
+
export type DirectResourceId = InputResourceId | bigint
|
|
5
|
+
|
|
6
|
+
export interface ResourceIdentifier<Type extends string = string> {
|
|
7
|
+
type: Type
|
|
8
|
+
id: string
|
|
9
|
+
}
|
|
10
|
+
export interface InputResourceIdentifier<Type extends string = string> {
|
|
11
|
+
type: Type
|
|
12
|
+
id: InputResourceId
|
|
13
|
+
}
|
|
14
|
+
export type JsonApiLink = string | { href: string; meta?: Record<string, unknown> } | null
|
|
15
|
+
export type JsonApiLinks = Record<string, JsonApiLink>
|
|
16
|
+
export interface JsonApiRelationship {
|
|
17
|
+
data?: ResourceIdentifier | ResourceIdentifier[] | null
|
|
18
|
+
links?: JsonApiLinks
|
|
19
|
+
meta?: Record<string, unknown>
|
|
20
|
+
}
|
|
21
|
+
export interface JsonApiResource<Attributes extends object = Record<string, unknown>, Type extends string = string> extends ResourceIdentifier<Type> {
|
|
22
|
+
attributes?: Partial<Attributes>
|
|
23
|
+
relationships?: Record<string, JsonApiRelationship>
|
|
24
|
+
links?: JsonApiLinks
|
|
25
|
+
meta?: Record<string, unknown>
|
|
26
|
+
}
|
|
27
|
+
export interface JsonApiDocument<Data = JsonApiResource | JsonApiResource[] | null> {
|
|
28
|
+
data: Data
|
|
29
|
+
included?: JsonApiResource[]
|
|
30
|
+
links?: JsonApiLinks
|
|
31
|
+
meta?: Record<string, unknown>
|
|
32
|
+
jsonapi?: { version?: string; meta?: Record<string, unknown> }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Supply relationship properties in Fields too; sparse fieldsets can omit either.
|
|
36
|
+
export type PlainResource<Fields extends object = Record<string, unknown>> = { id: string } & Partial<Fields>
|
|
37
|
+
export interface PlainCollection<Fields extends object = Record<string, unknown>> {
|
|
38
|
+
data: PlainResource<Fields>[]
|
|
39
|
+
links?: JsonApiLinks
|
|
40
|
+
meta?: Record<string, unknown>
|
|
41
|
+
}
|
|
42
|
+
export type SingleResourceResult<Format extends ResourceFormat, Fields extends object = Record<string, unknown>, Type extends string = string> =
|
|
43
|
+
Format extends 'plain' ? PlainResource<Fields> : JsonApiDocument<JsonApiResource<Fields, Type>>
|
|
44
|
+
export type CollectionResult<Format extends ResourceFormat, Fields extends object = Record<string, unknown>, Type extends string = string> =
|
|
45
|
+
Format extends 'plain' ? PlainCollection<Fields> : JsonApiDocument<JsonApiResource<Fields, Type>[]>
|
|
46
|
+
export type WriteResult<Format extends ResourceFormat, Returning extends WriteReturning, Fields extends object = Record<string, unknown>, Type extends string = string> =
|
|
47
|
+
Returning extends 'none' ? void
|
|
48
|
+
: Returning extends 'minimal'
|
|
49
|
+
? Format extends 'plain' ? ResourceIdentifier<Type> : JsonApiDocument<ResourceIdentifier<Type>>
|
|
50
|
+
: SingleResourceResult<Format, Fields, Type>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { Knex } from 'knex'
|
|
2
|
+
import type { ManagedTransactionParam } from './transactions.js'
|
|
3
|
+
import type {
|
|
4
|
+
CollectionResult, DirectResourceId, InputResourceId, InputResourceIdentifier, ResourceFormat,
|
|
5
|
+
SingleResourceResult, WriteResult, WriteReturning
|
|
6
|
+
} from './representations.js'
|
|
7
|
+
|
|
8
|
+
export type RemovedResourceOptions = {
|
|
9
|
+
[Key in 'simplified' | 'simplifiedApi' | 'simplifiedTransport' | 'returnFullRecord' |
|
|
10
|
+
'returnRecordApi' | 'returnRecordTransport' | 'isTransport' | 'inputRecord' | 'inputRecords']?: never
|
|
11
|
+
}
|
|
12
|
+
export interface SelectionParams {
|
|
13
|
+
include?: string[]
|
|
14
|
+
fields?: Record<string, string>
|
|
15
|
+
}
|
|
16
|
+
export type PageParams = { size?: number; [extension: string]: unknown } & (
|
|
17
|
+
{ number?: number; after?: never; before?: never } |
|
|
18
|
+
{ number?: never; after: string; before?: never } |
|
|
19
|
+
{ number?: never; after?: never; before: string }
|
|
20
|
+
)
|
|
21
|
+
export interface QueryParams extends SelectionParams {
|
|
22
|
+
filters?: Record<string, unknown>
|
|
23
|
+
sort?: string[]
|
|
24
|
+
page?: PageParams
|
|
25
|
+
}
|
|
26
|
+
export interface TransactionParam {
|
|
27
|
+
transaction?: Knex.Transaction
|
|
28
|
+
}
|
|
29
|
+
export interface JsonApiWriteDocument<Input extends object, Type extends string> {
|
|
30
|
+
data: {
|
|
31
|
+
type: Type
|
|
32
|
+
id?: InputResourceId
|
|
33
|
+
attributes?: Partial<Input>
|
|
34
|
+
relationships?: Record<string, { data: InputResourceIdentifier | InputResourceIdentifier[] | null }>
|
|
35
|
+
}
|
|
36
|
+
meta?: Record<string, unknown>
|
|
37
|
+
links?: Record<string, unknown>
|
|
38
|
+
jsonapi?: Record<string, unknown>
|
|
39
|
+
included?: never
|
|
40
|
+
errors?: never
|
|
41
|
+
}
|
|
42
|
+
export type ResourceWriteInput<Input extends object, Type extends string> =
|
|
43
|
+
{ data: Partial<Input> & { id?: InputResourceId }; document?: never } |
|
|
44
|
+
{ data?: never; document: JsonApiWriteDocument<Input, Type> }
|
|
45
|
+
export type ResourceWriteTarget<Input extends object, Type extends string> =
|
|
46
|
+
({ id: DirectResourceId } & ResourceWriteInput<Input, Type>) |
|
|
47
|
+
{
|
|
48
|
+
id?: never
|
|
49
|
+
data: Partial<Input> & { id: InputResourceId }
|
|
50
|
+
document?: never
|
|
51
|
+
} |
|
|
52
|
+
{
|
|
53
|
+
id?: never
|
|
54
|
+
data?: never
|
|
55
|
+
document: JsonApiWriteDocument<Input, Type> & { data: { id: InputResourceId } }
|
|
56
|
+
}
|
|
57
|
+
export type ReadOptions<Format extends ResourceFormat> = RemovedResourceOptions & TransactionParam & {
|
|
58
|
+
format?: Format
|
|
59
|
+
queryParams?: SelectionParams
|
|
60
|
+
}
|
|
61
|
+
export type WriteOptions<Format extends ResourceFormat, Returning extends WriteReturning> = Omit<ReadOptions<Format>, 'transaction'> & ManagedTransactionParam & {
|
|
62
|
+
returning?: Returning
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Format selects output only; data and document select the input representation.
|
|
66
|
+
// Output fields and writable input fields can differ (computed/read-only fields).
|
|
67
|
+
export interface ResourceCoreMethods<
|
|
68
|
+
Fields extends object = Record<string, unknown>,
|
|
69
|
+
Input extends object = Fields,
|
|
70
|
+
Type extends string = string,
|
|
71
|
+
DefaultFormat extends ResourceFormat = 'plain',
|
|
72
|
+
DefaultReturning extends WriteReturning = 'full',
|
|
73
|
+
Context extends object = object
|
|
74
|
+
> {
|
|
75
|
+
get<Format extends ResourceFormat = DefaultFormat>(
|
|
76
|
+
params: ReadOptions<Format> & { id: DirectResourceId }, context?: Context
|
|
77
|
+
): Promise<SingleResourceResult<Format, Fields, Type>>
|
|
78
|
+
query<Format extends ResourceFormat = DefaultFormat>(
|
|
79
|
+
params?: RemovedResourceOptions & TransactionParam & { format?: Format; queryParams?: QueryParams }, context?: Context
|
|
80
|
+
): Promise<CollectionResult<Format, Fields, Type>>
|
|
81
|
+
post<Format extends ResourceFormat = DefaultFormat, Returning extends WriteReturning = DefaultReturning>(
|
|
82
|
+
params: WriteOptions<Format, Returning> & ResourceWriteInput<Input, Type> & {
|
|
83
|
+
expectedVersion?: never
|
|
84
|
+
}, context?: Context
|
|
85
|
+
): Promise<WriteResult<Format, Returning, Fields, Type>>
|
|
86
|
+
put<Format extends ResourceFormat = DefaultFormat, Returning extends WriteReturning = DefaultReturning>(
|
|
87
|
+
params: WriteOptions<Format, Returning> & ResourceWriteTarget<Input, Type> & { expectedVersion?: string }, context?: Context
|
|
88
|
+
): Promise<WriteResult<Format, Returning, Fields, Type>>
|
|
89
|
+
patch<Format extends ResourceFormat = DefaultFormat, Returning extends WriteReturning = DefaultReturning>(
|
|
90
|
+
params: WriteOptions<Format, Returning> & ResourceWriteTarget<Input, Type> & { expectedVersion?: string }, context?: Context
|
|
91
|
+
): Promise<WriteResult<Format, Returning, Fields, Type>>
|
|
92
|
+
delete(
|
|
93
|
+
params: RemovedResourceOptions & ManagedTransactionParam & { id: DirectResourceId; format?: ResourceFormat; expectedVersion?: string }, context?: Context
|
|
94
|
+
): Promise<void>
|
|
95
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { StorageFieldDefinition, StorageQuery } from '../plugins/core/lib/storage/storage-types.js'
|
|
2
|
+
import type { FileStorage } from './file-storage.js'
|
|
3
|
+
|
|
4
|
+
export type ResourceFieldType = 'string' | 'number' | 'integer' | 'boolean' | 'id' |
|
|
5
|
+
'date' | 'time' | 'dateTime' | 'epochSeconds' | 'epochMilliseconds' |
|
|
6
|
+
'array' | 'object' | 'file' | 'blob' | 'serialize' | 'none'
|
|
7
|
+
|
|
8
|
+
// The schema library accepts Schema instances here, not raw nested field maps.
|
|
9
|
+
export interface ResourceNestedSchema<Schema extends ResourceSchema = ResourceSchema> {
|
|
10
|
+
structure: Schema
|
|
11
|
+
operations: object
|
|
12
|
+
validateWith(...args: never[]): unknown
|
|
13
|
+
toJsonSchema(...args: never[]): unknown
|
|
14
|
+
cleanup(...args: never[]): unknown
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ResourceFieldOptions {
|
|
18
|
+
required?: boolean
|
|
19
|
+
nullable?: boolean
|
|
20
|
+
nullOnEmpty?: boolean
|
|
21
|
+
defaultTo?: unknown
|
|
22
|
+
enum?: readonly unknown[]
|
|
23
|
+
setValues?: readonly string[]
|
|
24
|
+
min?: number
|
|
25
|
+
max?: number
|
|
26
|
+
minLength?: number
|
|
27
|
+
maxLength?: number
|
|
28
|
+
length?: number
|
|
29
|
+
pattern?: string | RegExp
|
|
30
|
+
notEmpty?: boolean
|
|
31
|
+
noTrim?: boolean
|
|
32
|
+
uppercase?: boolean
|
|
33
|
+
lowercase?: boolean
|
|
34
|
+
strictBoolean?: boolean
|
|
35
|
+
stringTrueWhen?: string
|
|
36
|
+
stringFalseWhen?: string
|
|
37
|
+
unsigned?: boolean
|
|
38
|
+
precision?: number
|
|
39
|
+
scale?: number
|
|
40
|
+
temporalPrecision?: number
|
|
41
|
+
indexed?: boolean
|
|
42
|
+
unique?: boolean
|
|
43
|
+
primary?: boolean
|
|
44
|
+
comment?: string
|
|
45
|
+
hidden?: boolean
|
|
46
|
+
normallyHidden?: boolean
|
|
47
|
+
virtual?: boolean
|
|
48
|
+
computed?: boolean
|
|
49
|
+
include?: ResourceRelationshipOptions['include']
|
|
50
|
+
search?: boolean | ResourceSearchOptions | Record<string, ResourceSearchOptions>
|
|
51
|
+
dependencies?: readonly string[]
|
|
52
|
+
runGetterAfter?: readonly string[]
|
|
53
|
+
runSetterAfter?: readonly string[]
|
|
54
|
+
items?: ResourceField | ResourceNestedSchema
|
|
55
|
+
values?: ResourceField | ResourceNestedSchema
|
|
56
|
+
schema?: ResourceNestedSchema
|
|
57
|
+
additionalProperties?: true
|
|
58
|
+
// Custom callbacks retain their explicit annotations; inference does not run them.
|
|
59
|
+
getter?(value: unknown, context: Record<string, unknown>): unknown
|
|
60
|
+
setter?(value: unknown, context: Record<string, unknown>): unknown
|
|
61
|
+
compute?(context: Record<string, unknown>): unknown
|
|
62
|
+
validator?(value: unknown, object: Record<string, unknown>, context: unknown): string | void
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type ResourceField = ResourceFieldOptions & (
|
|
66
|
+
{ type: Exclude<ResourceFieldType, 'file'>; belongsTo?: never; as?: never; storage?: StorageFieldDefinition['storage'] } |
|
|
67
|
+
{ type: 'file'; belongsTo?: never; as?: never; storage?: StorageFieldDefinition['storage'] | FileStorage } |
|
|
68
|
+
{ type?: ResourceFieldType; belongsTo: string; as: string; storage?: StorageFieldDefinition['storage'] }
|
|
69
|
+
)
|
|
70
|
+
export type ResourceSchema = Readonly<Record<string, ResourceField>>
|
|
71
|
+
|
|
72
|
+
export interface ResourceSearchOptions extends ResourceFieldOptions {
|
|
73
|
+
type?: ResourceFieldType
|
|
74
|
+
actualField?: string
|
|
75
|
+
filterOperator?: string
|
|
76
|
+
oneOf?: readonly string[]
|
|
77
|
+
splitBy?: string | RegExp
|
|
78
|
+
matchAll?: boolean
|
|
79
|
+
globalSearch?: boolean
|
|
80
|
+
polymorphicField?: string
|
|
81
|
+
targetFields?: Readonly<Record<string, string>>
|
|
82
|
+
applyFilter?(query: StorageQuery, input: unknown, helpers: {
|
|
83
|
+
column(field: string): string
|
|
84
|
+
value(field: string, input: unknown): unknown
|
|
85
|
+
context: Record<string, unknown>
|
|
86
|
+
scopeName: string
|
|
87
|
+
}): unknown
|
|
88
|
+
}
|
|
89
|
+
export type ResourceSearchField = ResourceField & ResourceSearchOptions
|
|
90
|
+
export type ResourceSearchSchema = Readonly<Record<string, ResourceSearchField>>
|
|
91
|
+
|
|
92
|
+
export interface ResourceRelationshipOptions {
|
|
93
|
+
include?: { strategy?: 'window'; limit?: number | null | false; orderBy?: readonly string[] | null }
|
|
94
|
+
}
|
|
95
|
+
export type ResourceRelationship = ResourceRelationshipOptions & (
|
|
96
|
+
{ type: 'hasMany'; target: string } & (
|
|
97
|
+
{ foreignKey: string; via?: string } | { foreignKey?: string; via: string }
|
|
98
|
+
) |
|
|
99
|
+
{ type: 'hasOne'; target: string; foreignKey: string } |
|
|
100
|
+
{ type: 'manyToMany'; target: string; through: string; foreignKey: string; otherKey: string } |
|
|
101
|
+
{ belongsToPolymorphic: { types: readonly string[]; typeField: string; idField: string } }
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
type Direction = 'input' | 'output'
|
|
105
|
+
type NestedValue<Definition, Mode extends Direction> =
|
|
106
|
+
Definition extends ResourceNestedSchema<infer Schema>
|
|
107
|
+
? { -readonly [Key in keyof Schema]?: FieldValue<Schema[Key], Mode> }
|
|
108
|
+
: FieldValue<Definition, Mode>
|
|
109
|
+
|
|
110
|
+
type ObjectValue<Field, Mode extends Direction> =
|
|
111
|
+
Field extends { schema: infer Schema }
|
|
112
|
+
? NestedValue<Schema, Mode> & (Field extends { additionalProperties: true } ? Record<string, unknown> : unknown)
|
|
113
|
+
: Field extends { values: infer Values } ? Record<string, NestedValue<Values, Mode>> : Record<string, unknown>
|
|
114
|
+
|
|
115
|
+
type BaseValue<Field, Mode extends Direction> =
|
|
116
|
+
Field extends { type: 'string' | 'file' | 'date' | 'time' | 'dateTime' } ? string
|
|
117
|
+
: Field extends { type: 'number'; precision: number; scale: number } ? Mode extends 'output' ? string | number : number
|
|
118
|
+
: Field extends { type: 'number' | 'integer' | 'epochSeconds' | 'epochMilliseconds' } ? number
|
|
119
|
+
: Field extends { type: 'boolean' } ? boolean
|
|
120
|
+
: Field extends { type: 'id' } ? string | number
|
|
121
|
+
: Field extends { type: 'array' } ? Field extends { items: infer Items } ? NestedValue<Items, Mode>[] : unknown[]
|
|
122
|
+
: Field extends { type: 'object' } ? ObjectValue<Field, Mode> : unknown
|
|
123
|
+
|
|
124
|
+
type EnumValue<Field, Value> = Field extends { enum: readonly (infer Member)[] }
|
|
125
|
+
? [Member] extends [string | number | boolean] ? Extract<Member, Value> : Value
|
|
126
|
+
: Value
|
|
127
|
+
|
|
128
|
+
type NullValue<Field, Mode extends Direction> = Field extends { nullable: true } ? null
|
|
129
|
+
: Mode extends 'output' ? Field extends { nullOnEmpty: true } ? null : never : never
|
|
130
|
+
|
|
131
|
+
type FieldValue<Field, Mode extends Direction> =
|
|
132
|
+
Mode extends 'output'
|
|
133
|
+
? Field extends { getter: unknown } | { setter: unknown } | { compute: unknown } | { storage: { serialize: unknown } }
|
|
134
|
+
? unknown
|
|
135
|
+
: Field extends { uppercase: true } | { lowercase: true } | { length: number } |
|
|
136
|
+
{ type: 'id' } | { type: 'number'; precision: number; scale: number }
|
|
137
|
+
? BaseValue<Field, Mode> | NullValue<Field, Mode>
|
|
138
|
+
: EnumValue<Field, BaseValue<Field, Mode>> | NullValue<Field, Mode>
|
|
139
|
+
: EnumValue<Field, BaseValue<Field, Mode>> | NullValue<Field, Mode>
|
|
140
|
+
|
|
141
|
+
type AttributeKey<Schema, Key extends keyof Schema> = Key extends 'id' ? never
|
|
142
|
+
: Schema[Key] extends { belongsTo: string } ? never : Key
|
|
143
|
+
|
|
144
|
+
// Optional stored fields without a NOT NULL declaration can read SQL null.
|
|
145
|
+
type StoredNull<Field> = Field extends { required: true } | { nullable: false } | { virtual: true } | { computed: true } ? never : null
|
|
146
|
+
|
|
147
|
+
// Attribute helpers exclude the conventional ID and relationship backing fields.
|
|
148
|
+
// Hooks/defaults may supply required values, so caller input stays optional.
|
|
149
|
+
export type InferInput<Schema extends object> = {
|
|
150
|
+
-readonly [Key in keyof Schema as Schema[Key] extends { computed: true } ? never : AttributeKey<Schema, Key>]?: FieldValue<Schema[Key], 'input'>
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Resource result types add optionality for sparse fields and permissions.
|
|
154
|
+
// Use explicit ResourceCoreMethods fields for callbacks, relationships and custom IDs.
|
|
155
|
+
export type InferOutput<Schema extends object> = {
|
|
156
|
+
-readonly [Key in keyof Schema as Schema[Key] extends { hidden: true } ? never : AttributeKey<Schema, Key>]: FieldValue<Schema[Key], 'output'> | StoredNull<Schema[Key]>
|
|
157
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { StorageAdapter, StorageDatabase, StorageQuery } from '../plugins/core/lib/storage/storage-types.js'
|
|
2
|
+
|
|
3
|
+
export interface RowPolicyParams<Context extends object = Record<string, unknown>, Api extends object = object> {
|
|
4
|
+
query: StorageQuery
|
|
5
|
+
context: Context
|
|
6
|
+
scopeName: string
|
|
7
|
+
tableName?: string
|
|
8
|
+
queryPurpose: string
|
|
9
|
+
db?: StorageDatabase
|
|
10
|
+
isAnyApi: boolean
|
|
11
|
+
storageAdapter?: StorageAdapter | null
|
|
12
|
+
column(field: string, options?: { scopeName?: string; alias?: string | null }): string
|
|
13
|
+
value(field: string, rawValue: unknown, options?: { scopeName?: string }): unknown
|
|
14
|
+
api: Api
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type RowPolicy<Context extends object = Record<string, unknown>, Api extends object = object> = (
|
|
18
|
+
params: RowPolicyParams<Context, Api>
|
|
19
|
+
) => boolean | PromiseLike<boolean>
|
|
20
|
+
|
|
21
|
+
export interface RowPolicyPluginOptions<Context extends object = Record<string, unknown>, Api extends object = object> {
|
|
22
|
+
policies?: Record<string, RowPolicy<Context, Api>>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type ResourceRowPolicy<Context extends object = Record<string, unknown>, Api extends object = object> =
|
|
26
|
+
string | RowPolicy<Context, Api> | false | null
|
|
27
|
+
|
|
28
|
+
export interface RowPolicyRegistry {
|
|
29
|
+
getConfig(): { policies: string[] }
|
|
30
|
+
getScopeConfig(scopeName: string): { policy: string; source: 'inline' | 'registry' } | null
|
|
31
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { DiagnosticLogger, DiagnosticWriter } from '../lib/logger-types.js'
|
|
2
|
+
import type { TransactionMethods } from './transactions.js'
|
|
3
|
+
import type { ResourceFormat, WriteReturning } from './representations.js'
|
|
4
|
+
import type { ResourceCoreMethods } from './resource-methods.js'
|
|
5
|
+
import type { ResourceField, ResourceSchema, ResourceSearchSchema, ResourceRelationship, InferInput, InferOutput } from './resource-schema.js'
|
|
6
|
+
import type { KnownHookName, HookHandler } from './hook-context.js'
|
|
7
|
+
import type { RestApiPluginOptions } from './plugin-options.js'
|
|
8
|
+
import type { ResourceAutoFilter } from './autofilter.js'
|
|
9
|
+
import type { ResourceRowPolicy } from './row-policy.js'
|
|
10
|
+
import type { CanonicalDescriptor, StorageConfig } from '../plugins/core/lib/storage/storage-types.js'
|
|
11
|
+
|
|
12
|
+
export type RuntimeLogger = Record<'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal', DiagnosticWriter>
|
|
13
|
+
export interface RuntimeResource {
|
|
14
|
+
name: string
|
|
15
|
+
scopeOptions: Readonly<Record<string, unknown>>
|
|
16
|
+
vars: Record<string, unknown>
|
|
17
|
+
helpers: Record<string, unknown>
|
|
18
|
+
[method: string]: unknown
|
|
19
|
+
}
|
|
20
|
+
export interface RuntimeArguments<Context extends object = Record<string, unknown>> {
|
|
21
|
+
api: JsonRestApi
|
|
22
|
+
name: string | undefined
|
|
23
|
+
apiOptions: Readonly<Record<string, unknown>>
|
|
24
|
+
params: Record<string, unknown>
|
|
25
|
+
context: Context
|
|
26
|
+
scope: RuntimeResource | undefined
|
|
27
|
+
scopeName: string | undefined
|
|
28
|
+
scopeOptions: Readonly<Record<string, unknown>> | undefined
|
|
29
|
+
scopes: Record<string, RuntimeResource>
|
|
30
|
+
vars: Record<string, unknown>
|
|
31
|
+
helpers: Record<string, unknown>
|
|
32
|
+
log: RuntimeLogger
|
|
33
|
+
runHooks(event: string, context?: Record<string, unknown>): Promise<boolean>
|
|
34
|
+
}
|
|
35
|
+
export type RuntimeMethod<Context extends object = Record<string, unknown>> = (args: RuntimeArguments<Context>) => unknown
|
|
36
|
+
export type HookPlacement = { beforeFunction?: string; afterFunction?: never } | { beforeFunction?: never; afterFunction?: string }
|
|
37
|
+
type HookDefinition<Handler> = Handler | (HookPlacement & { functionName?: string; handler: Handler })
|
|
38
|
+
export type RuntimeHook<Context extends object = Record<string, unknown>> = HookDefinition<RuntimeMethod<Context>>
|
|
39
|
+
export type RuntimeHooks<Context extends object = Record<string, unknown>> = {
|
|
40
|
+
[Event in KnownHookName]?: HookDefinition<HookHandler<Event, Context>>
|
|
41
|
+
} & {
|
|
42
|
+
// Custom plugins own their context shape; annotate them with RuntimeHook<Context>.
|
|
43
|
+
[event: string]: RuntimeHook<any> | undefined
|
|
44
|
+
}
|
|
45
|
+
export interface RuntimeCustomization<Context extends object = Record<string, unknown>> {
|
|
46
|
+
hooks?: RuntimeHooks<Context>
|
|
47
|
+
methods?: Record<string, RuntimeMethod>
|
|
48
|
+
vars?: Record<string, unknown>
|
|
49
|
+
helpers?: Record<string, unknown>
|
|
50
|
+
}
|
|
51
|
+
export interface RuntimeResourceOptions<Context extends object = Record<string, unknown>> extends RuntimeCustomization<Context>, RestApiPluginOptions {
|
|
52
|
+
schema?: ResourceSchema
|
|
53
|
+
searchSchema?: ResourceSearchSchema
|
|
54
|
+
relationships?: Record<string, ResourceRelationship>
|
|
55
|
+
tableName?: string
|
|
56
|
+
storage?: StorageConfig
|
|
57
|
+
canonicalFieldsMap?: CanonicalDescriptor['canonicalFieldMap']
|
|
58
|
+
sortableFields?: string[]
|
|
59
|
+
defaultSort?: string[]
|
|
60
|
+
versionField?: string
|
|
61
|
+
rowPolicy?: ResourceRowPolicy<Context>
|
|
62
|
+
autofilter?: ResourceAutoFilter<Context>
|
|
63
|
+
indexes?: Array<{ name?: string; columns: string[]; unique?: boolean }>
|
|
64
|
+
foreignKeys?: Array<{
|
|
65
|
+
name?: string
|
|
66
|
+
columns: string[]
|
|
67
|
+
referencedTableName: string
|
|
68
|
+
referencedColumns: string[]
|
|
69
|
+
deleteRule?: string
|
|
70
|
+
updateRule?: string
|
|
71
|
+
}>
|
|
72
|
+
checkConstraints?: Array<{ name?: string; clause: string }>
|
|
73
|
+
scopeMethods?: never
|
|
74
|
+
}
|
|
75
|
+
export interface PluginInstallation extends RuntimeArguments {
|
|
76
|
+
pluginOptions: Record<string, unknown>
|
|
77
|
+
addApiMethod(name: string, handler: RuntimeMethod): void
|
|
78
|
+
addResourceMethod(name: string, handler: RuntimeMethod): void
|
|
79
|
+
addHook(event: string, name: string, placement: HookPlacement, handler: RuntimeMethod): void
|
|
80
|
+
}
|
|
81
|
+
export interface LibraryPlugin<Name extends string = string> {
|
|
82
|
+
name: Name
|
|
83
|
+
dependencies?: (string | string[])[]
|
|
84
|
+
install(context: PluginInstallation): unknown
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type InferredResource<
|
|
88
|
+
Schema extends ResourceSchema,
|
|
89
|
+
Name extends string,
|
|
90
|
+
Format extends ResourceFormat = 'plain',
|
|
91
|
+
Returning extends WriteReturning = 'full',
|
|
92
|
+
Context extends object = Record<string, unknown>,
|
|
93
|
+
IdProperty extends string = 'id'
|
|
94
|
+
> = string extends IdProperty ? RuntimeResource
|
|
95
|
+
: RuntimeResource & ResourceCoreMethods<Omit<InferOutput<Schema>, IdProperty>, Omit<InferInput<Schema>, IdProperty>, Name, Format, Returning, Context>
|
|
96
|
+
|
|
97
|
+
// Check literal field option names without attempting to infer arbitrary plugin extensions.
|
|
98
|
+
type CheckedResourceSchema<Schema extends ResourceSchema> = {
|
|
99
|
+
[Key in keyof Schema]: Schema[Key] & Record<Exclude<keyof Schema[Key], keyof ResourceField>, never>
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Inference describes built-in CRUD with declared schemas and standard API defaults.
|
|
103
|
+
// Supply an explicit resource map for dynamic enrichment, method overrides or other defaults.
|
|
104
|
+
export class JsonRestApi<Resources extends object = Record<string, RuntimeResource>, Context extends object = Record<string, unknown>> {
|
|
105
|
+
constructor(options?: { name?: string; logger?: DiagnosticLogger })
|
|
106
|
+
name: string | undefined
|
|
107
|
+
log: RuntimeLogger
|
|
108
|
+
options: Readonly<Record<string, unknown>>
|
|
109
|
+
vars: Record<string, unknown>
|
|
110
|
+
helpers: Record<string, unknown>
|
|
111
|
+
resources: Resources
|
|
112
|
+
use(plugin: LibraryPlugin, options?: object): Promise<this>
|
|
113
|
+
addResource<
|
|
114
|
+
const Name extends keyof Resources & string,
|
|
115
|
+
const Schema extends ResourceSchema,
|
|
116
|
+
const Format extends ResourceFormat = 'plain',
|
|
117
|
+
const Returning extends WriteReturning = 'full',
|
|
118
|
+
const IdProperty extends string = 'id'
|
|
119
|
+
>(name: Name, options: RuntimeResourceOptions<Context> & {
|
|
120
|
+
schema: Schema & CheckedResourceSchema<Schema>
|
|
121
|
+
format?: Format
|
|
122
|
+
returning?: Returning
|
|
123
|
+
idProperty?: IdProperty
|
|
124
|
+
methods?: never
|
|
125
|
+
}): Promise<string extends keyof Resources ? InferredResource<Schema, Name, Format, Returning, Context, IdProperty> : Resources[Name]>
|
|
126
|
+
addResource<Name extends keyof Resources & string>(name: Name, options?: RuntimeResourceOptions<Context>): Promise<Resources[Name]>
|
|
127
|
+
customize(options?: RuntimeCustomization<Context>): Promise<this>
|
|
128
|
+
runHooks(event: string, context?: Record<string, unknown>): Promise<boolean>
|
|
129
|
+
// Available after installing RestApiPlugin.
|
|
130
|
+
transaction: TransactionMethods['transaction']
|
|
131
|
+
release(): Promise<void>
|
|
132
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Knex } from 'knex'
|
|
2
|
+
|
|
3
|
+
declare const managedTransaction: unique symbol
|
|
4
|
+
|
|
5
|
+
// Type-only provenance: the runtime tracks ownership privately, not on this symbol.
|
|
6
|
+
export type ManagedTransaction = Knex.Transaction & {
|
|
7
|
+
readonly [managedTransaction]: true
|
|
8
|
+
commit: never
|
|
9
|
+
rollback: never
|
|
10
|
+
transaction: never
|
|
11
|
+
savepoint: never
|
|
12
|
+
}
|
|
13
|
+
export interface ManagedTransactionParam {
|
|
14
|
+
transaction?: ManagedTransaction
|
|
15
|
+
}
|
|
16
|
+
export interface TransactionMethods<Context extends object = object> {
|
|
17
|
+
transaction<Result>(
|
|
18
|
+
callback: (transaction: ManagedTransaction) => Result | PromiseLike<Result>,
|
|
19
|
+
context?: Context
|
|
20
|
+
): Promise<Awaited<Result>>
|
|
21
|
+
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: library-guide-expert
|
|
3
|
-
description: Use this agent when you need to understand how to use a library based on its GUIDE documentation, implement features using the library's recommended patterns, answer questions about library usage, or ensure code follows the library's best practices. This agent has comprehensive knowledge of all files in the GUIDE/* directory and can provide authoritative guidance on proper library usage.\n\nExamples:\n- <example>\n Context: User needs help implementing a feature using the library\n user: "How do I create a REST endpoint with authentication?"\n assistant: "I'll use the library-guide-expert agent to check the GUIDE documentation for the proper way to implement authenticated endpoints."\n <commentary>\n Since the user is asking about library usage, use the Task tool to launch the library-guide-expert agent to provide guidance based on the GUIDE documentation.\n </commentary>\n</example>\n- <example>\n Context: User wants to know the recommended pattern for something\n user: "What's the best way to handle errors in this library?"\n assistant: "Let me consult the library-guide-expert agent to find the error handling patterns documented in the GUIDE."\n <commentary>\n The user needs library-specific best practices, so use the library-guide-expert agent to reference the official documentation.\n </commentary>\n</example>\n- <example>\n Context: After writing code that uses the library\n user: "I've implemented the user registration endpoint"\n assistant: "I'll have the library-guide-expert agent review this to ensure it follows the patterns documented in the GUIDE."\n <commentary>\n Proactively use the library-guide-expert agent to verify the implementation aligns with documented patterns.\n </commentary>\n</example>
|
|
4
|
-
model: inherit
|
|
5
|
-
color: blue
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
You are an expert library documentation specialist with comprehensive knowledge of all files in the GUIDE/* directory. You have meticulously studied every guide, example, and best practice documented there and can provide authoritative guidance on proper library usage.
|
|
9
|
-
|
|
10
|
-
Your primary responsibilities:
|
|
11
|
-
|
|
12
|
-
1. **Deep Documentation Knowledge**: You have read and internalized every file in GUIDE/*. You understand not just what the documentation says, but the underlying patterns, philosophies, and design decisions that inform the library's architecture.
|
|
13
|
-
|
|
14
|
-
2. **Practical Implementation Guidance**: When users ask how to implement features, you provide specific, actionable advice based on the documented patterns. You reference relevant sections of the GUIDE and explain how to apply them to the user's specific use case.
|
|
15
|
-
|
|
16
|
-
3. **Best Practices Enforcement**: You ensure all code recommendations follow the library's documented best practices. You can identify when proposed solutions deviate from recommended patterns and suggest corrections.
|
|
17
|
-
|
|
18
|
-
4. **Example-Driven Teaching**: You leverage examples from the GUIDE to illustrate proper usage. When the documentation includes code samples, you adapt them to fit the user's needs while maintaining the core patterns.
|
|
19
|
-
|
|
20
|
-
5. **Comprehensive Cross-Referencing**: You understand how different parts of the documentation relate to each other. You can connect concepts from multiple GUIDE files to provide complete solutions.
|
|
21
|
-
|
|
22
|
-
When responding:
|
|
23
|
-
- Always ground your answers in specific documentation from GUIDE/*
|
|
24
|
-
- Cite which GUIDE file contains the relevant information
|
|
25
|
-
- Provide code examples that follow the documented patterns exactly
|
|
26
|
-
- Explain not just 'how' but 'why' based on the library's design principles
|
|
27
|
-
- If something isn't covered in the GUIDE, explicitly state this limitation
|
|
28
|
-
- Proactively mention related documentation that might be helpful
|
|
29
|
-
|
|
30
|
-
Your expertise comes from thorough study of the GUIDE documentation. Every recommendation you make is backed by official documentation, ensuring users implement features correctly and maintainably according to the library's intended design.
|