json-rest-api 1.0.29 → 2.0.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.
Files changed (292) hide show
  1. package/LICENSE +10 -670
  2. package/LICENSE-GPL-3.0 +674 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +39 -30
  5. package/docs/API.md +744 -1273
  6. package/docs/GUIDE/01-getting-started.md +338 -0
  7. package/docs/GUIDE/02-resources-and-relationships.md +33 -0
  8. package/docs/GUIDE/03-running-example.md +87 -0
  9. package/docs/GUIDE/04-creating-and-querying.md +181 -0
  10. package/docs/GUIDE/05-belongs-to.md +177 -0
  11. package/docs/GUIDE/06-has-many.md +184 -0
  12. package/docs/GUIDE/07-polymorphic-relationships.md +191 -0
  13. package/docs/GUIDE/08-many-to-many.md +184 -0
  14. package/docs/GUIDE/09-pagination-and-sorting.md +149 -0
  15. package/docs/GUIDE/10-put-and-patch.md +174 -0
  16. package/docs/GUIDE/11-relationship-endpoints.md +174 -0
  17. package/docs/GUIDE/12-field-transformations.md +287 -0
  18. package/docs/GUIDE/13-hooks-and-lifecycle.md +479 -0
  19. package/docs/GUIDE/14-custom-resource-methods.md +125 -0
  20. package/docs/GUIDE/15-query-projections.md +266 -0
  21. package/docs/GUIDE/{GUIDE_X_Autofiltering.md → 16-autofiltering.md} +77 -15
  22. package/docs/GUIDE/{GUIDE_X_Row_Policies.md → 17-row-policies.md} +137 -44
  23. package/docs/GUIDE/18-labels.md +86 -0
  24. package/docs/GUIDE/19-managed-transactions.md +179 -0
  25. package/docs/GUIDE/20-transaction-outcomes.md +207 -0
  26. package/docs/GUIDE/21-schema-and-migrations.md +634 -0
  27. package/docs/GUIDE/22-version-field-migrations.md +208 -0
  28. package/docs/GUIDE/23-fastify.md +179 -0
  29. package/docs/GUIDE/24-cors.md +172 -0
  30. package/docs/GUIDE/{GUIDE_X_URL_Management.md → 25-url-management.md} +7 -1
  31. package/docs/GUIDE/26-file-uploads.md +278 -0
  32. package/docs/GUIDE/27-bulk-operations.md +277 -0
  33. package/docs/GUIDE/28-socketio.md +325 -0
  34. package/docs/GUIDE/29-writing-plugins.md +278 -0
  35. package/docs/GUIDE/30-backend-capabilities.md +100 -0
  36. package/docs/GUIDE/31-positioning.md +181 -0
  37. package/docs/GUIDE/{GUIDE_Y_Appendices.md → 32-date-and-time.md} +74 -30
  38. package/docs/GUIDE/33-migrating-to-v2.md +3827 -0
  39. package/docs/GUIDE/index.md +51 -59
  40. package/docs/QUICKSTART.md +120 -287
  41. package/docs/architecture.md +75 -0
  42. package/docs/contributing.md +178 -0
  43. package/index.d.ts +36 -0
  44. package/index.js +1 -22
  45. package/lib/enhanced-logger.js +91 -85
  46. package/lib/error-context.js +445 -5
  47. package/lib/error-formatter.js +219 -59
  48. package/lib/knex-transaction.js +64 -0
  49. package/lib/logger-types.d.ts +22 -0
  50. package/lib/missing-package.js +4 -0
  51. package/lib/rest-api-errors.js +103 -8
  52. package/lib/runtime/json-rest-api.js +172 -0
  53. package/lib/transaction-types.d.ts +59 -0
  54. package/package.json +97 -27
  55. package/plugins/core/bulk-operations-plugin.js +191 -184
  56. package/plugins/core/connectors/express-plugin.js +148 -219
  57. package/plugins/core/connectors/fastify-plugin.js +125 -82
  58. package/plugins/core/connectors/lib/busboy-detector.js +74 -156
  59. package/plugins/core/connectors/lib/connector-core.js +144 -20
  60. package/plugins/core/connectors/lib/formidable-detector.js +63 -120
  61. package/plugins/core/connectors/lib/http-validators.js +77 -0
  62. package/plugins/core/connectors/lib/multipart-helpers.js +18 -0
  63. package/plugins/core/connectors/lib/request-helpers.js +10 -1
  64. package/plugins/core/connectors/lib/transport-http-helpers.js +89 -8
  65. package/plugins/core/connectors/lib/transport-route-schemas.js +7 -4
  66. package/plugins/core/file-handling-plugin.js +87 -177
  67. package/plugins/core/lib/anyapi/anyapi-registry.js +213 -164
  68. package/plugins/core/lib/anyapi/canonical-link-store.js +507 -0
  69. package/plugins/core/lib/anyapi/canonical-relationship-reader.js +598 -0
  70. package/plugins/core/lib/anyapi/schema-utils.js +25 -16
  71. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +0 -64
  72. package/plugins/core/lib/dbIntrospection.js +228 -21
  73. package/plugins/core/lib/dbTablesOperations.js +409 -92
  74. package/plugins/core/lib/fractional-positioning.js +12 -48
  75. package/plugins/core/lib/querying/identifier-query.js +19 -0
  76. package/plugins/core/lib/querying/include-query-helpers.js +364 -0
  77. package/plugins/core/lib/querying/include-to-many.js +428 -0
  78. package/plugins/core/lib/querying/include-to-one.js +579 -0
  79. package/plugins/core/lib/querying/knex-cross-table-search.js +31 -307
  80. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +99 -401
  81. package/plugins/core/lib/querying/knex-pagination-helpers.js +48 -317
  82. package/plugins/core/lib/querying/knex-process-includes.js +5 -110
  83. package/plugins/core/lib/querying/knex-query-helpers-base.js +21 -56
  84. package/plugins/core/lib/querying/knex-query-helpers.js +298 -448
  85. package/plugins/core/lib/querying/knex-relationship-includes.js +120 -2138
  86. package/plugins/core/lib/querying/knex-window-queries.js +62 -253
  87. package/plugins/core/lib/querying/query-builder-utils.js +44 -4
  88. package/plugins/core/lib/querying/query-constraint.d.ts +12 -0
  89. package/plugins/core/lib/querying/query-constraint.js +27 -0
  90. package/plugins/core/lib/querying/query-field-sort-helpers.js +134 -37
  91. package/plugins/core/lib/querying/relationship-identifiers.js +208 -0
  92. package/plugins/core/lib/querying/sort-helpers.js +13 -1
  93. package/plugins/core/lib/querying/storage-adapter-utils.js +13 -5
  94. package/plugins/core/lib/querying/url-helpers.js +11 -4
  95. package/plugins/core/lib/querying-writing/compile-schemas.js +148 -249
  96. package/plugins/core/lib/querying-writing/connectors-query-parser.js +33 -118
  97. package/plugins/core/lib/querying-writing/database-capabilities.js +149 -223
  98. package/plugins/core/lib/querying-writing/database-value-normalizers.js +179 -194
  99. package/plugins/core/lib/querying-writing/default-data-helpers.js +15 -45
  100. package/plugins/core/lib/querying-writing/field-selection-types.d.ts +43 -0
  101. package/plugins/core/lib/querying-writing/field-utils.js +113 -92
  102. package/plugins/core/lib/querying-writing/knex-constants.d.ts +18 -0
  103. package/plugins/core/lib/querying-writing/knex-constants.js +2 -26
  104. package/plugins/core/lib/querying-writing/knex-field-helpers.js +100 -290
  105. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +31 -120
  106. package/plugins/core/lib/querying-writing/query-field-helpers.js +108 -32
  107. package/plugins/core/lib/querying-writing/query-field-types.d.ts +60 -0
  108. package/plugins/core/lib/querying-writing/relationship-contracts.js +33 -13
  109. package/plugins/core/lib/querying-writing/request-contracts.js +93 -38
  110. package/plugins/core/lib/querying-writing/resource-id-normalization.js +23 -5
  111. package/plugins/core/lib/querying-writing/response-options.js +35 -0
  112. package/plugins/core/lib/querying-writing/schema-helpers.js +203 -324
  113. package/plugins/core/lib/querying-writing/scope-validations.js +92 -290
  114. package/plugins/core/lib/querying-writing/simplified-helpers.js +113 -712
  115. package/plugins/core/lib/storage/canonical-data-helpers.js +683 -0
  116. package/plugins/core/lib/storage/canonical-storage-mapping.js +51 -20
  117. package/plugins/core/lib/storage/ordinary-data-helpers.js +710 -0
  118. package/plugins/core/lib/storage/storage-adapter.js +128 -62
  119. package/plugins/core/lib/storage/storage-mapping.js +78 -28
  120. package/plugins/core/lib/storage/storage-types.d.ts +502 -0
  121. package/plugins/core/lib/writing/many-to-many-manipulations.js +114 -284
  122. package/plugins/core/lib/writing/relationship-processor.js +94 -158
  123. package/plugins/core/lib/writing/resource-version.js +172 -0
  124. package/plugins/core/lib/writing/reverse-relationship-manipulations.js +110 -0
  125. package/plugins/core/rest-api-anyapi-knex-plugin.js +116 -2485
  126. package/plugins/core/rest-api-autofilter-plugin.js +17 -19
  127. package/plugins/core/rest-api-cors-plugin.js +52 -205
  128. package/plugins/core/rest-api-knex-plugin.js +45 -1077
  129. package/plugins/core/rest-api-label-plugin.js +25 -38
  130. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +5 -5
  131. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +9 -6
  132. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +7 -26
  133. package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +20 -26
  134. package/plugins/core/rest-api-plugin-methods/add-route.js +4 -6
  135. package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +42 -13
  136. package/plugins/core/rest-api-plugin-methods/check-permissions.js +11 -5
  137. package/plugins/core/rest-api-plugin-methods/common.js +291 -395
  138. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +49 -47
  139. package/plugins/core/rest-api-plugin-methods/delete.js +31 -25
  140. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +108 -148
  141. package/plugins/core/rest-api-plugin-methods/get-related.js +113 -305
  142. package/plugins/core/rest-api-plugin-methods/get-relationship.js +25 -18
  143. package/plugins/core/rest-api-plugin-methods/get.js +36 -68
  144. package/plugins/core/rest-api-plugin-methods/lifecycle-types.d.ts +225 -0
  145. package/plugins/core/rest-api-plugin-methods/patch-relationship.js +28 -25
  146. package/plugins/core/rest-api-plugin-methods/patch.js +46 -99
  147. package/plugins/core/rest-api-plugin-methods/post-relationship.js +49 -53
  148. package/plugins/core/rest-api-plugin-methods/post.js +51 -51
  149. package/plugins/core/rest-api-plugin-methods/put.js +69 -148
  150. package/plugins/core/rest-api-plugin-methods/query.js +37 -81
  151. package/plugins/core/rest-api-plugin-methods/release.js +2 -1
  152. package/plugins/core/rest-api-plugin.js +32 -46
  153. package/plugins/core/rest-api-positioning-plugin.js +183 -628
  154. package/plugins/core/rest-api-query-projections-plugin.js +19 -102
  155. package/plugins/core/rest-api-row-policy-plugin.js +3 -3
  156. package/plugins/core/socketio-plugin.js +364 -344
  157. package/plugins/storage/local-storage.js +40 -36
  158. package/types/autofilter.d.ts +57 -0
  159. package/types/bulk-methods.d.ts +63 -0
  160. package/types/errors.d.ts +118 -0
  161. package/types/fastify-options.d.ts +19 -0
  162. package/types/file-detectors.d.ts +33 -0
  163. package/types/file-storage.d.ts +48 -0
  164. package/types/hook-context.d.ts +180 -0
  165. package/types/plugin-options.d.ts +53 -0
  166. package/types/relationship-methods.d.ts +74 -0
  167. package/types/representations.d.ts +50 -0
  168. package/types/resource-methods.d.ts +95 -0
  169. package/types/resource-schema.d.ts +157 -0
  170. package/types/row-policy.d.ts +31 -0
  171. package/types/runtime.d.ts +132 -0
  172. package/types/transactions.d.ts +21 -0
  173. package/.claude/agents/library-guide-expert.md +0 -30
  174. package/.claude/agents/rest-api-internals-expert.md +0 -54
  175. package/.claude/settings.local.json +0 -70
  176. package/AGENTS.md +0 -34
  177. package/CLAUDE.md +0 -160
  178. package/CRUSH.md +0 -55
  179. package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +0 -180
  180. package/SINGLE_VALIDATION_CONTRACT_TODO.txt +0 -172
  181. package/TODO.todo +0 -13
  182. package/connectors_ideas/fastify.js +0 -217
  183. package/connectors_ideas/koa-plugin.js +0 -112
  184. package/docs/.bundle/config +0 -2
  185. package/docs/COMPARISON.md +0 -433
  186. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +0 -1057
  187. package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +0 -88
  188. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +0 -808
  189. package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +0 -491
  190. package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +0 -398
  191. package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +0 -898
  192. package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +0 -712
  193. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +0 -1247
  194. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +0 -748
  195. package/docs/GUIDE/GUIDE_2_9_Relationships_Urls.md +0 -517
  196. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +0 -66
  197. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +0 -1252
  198. package/docs/GUIDE/GUIDE_5_Non-Db_Resources.md +0 -1107
  199. package/docs/GUIDE/GUIDE_6_Positioning.md +0 -692
  200. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +0 -1491
  201. package/docs/GUIDE/GUIDE_X_Bulk_Operations.md +0 -732
  202. package/docs/GUIDE/GUIDE_X_Cors.md +0 -1957
  203. package/docs/GUIDE/GUIDE_X_Fastify.md +0 -78
  204. package/docs/GUIDE/GUIDE_X_File_Uploads.md +0 -729
  205. package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +0 -370
  206. package/docs/GUIDE/GUIDE_X_Query_Projections.md +0 -159
  207. package/docs/GUIDE/GUIDE_X_SocketIO.md +0 -1040
  208. package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +0 -201
  209. package/docs/Gemfile +0 -6
  210. package/docs/Gemfile.lock +0 -308
  211. package/docs/ONBOARDING.md +0 -315
  212. package/docs/_config.yml +0 -36
  213. package/docs/_layouts/default.html +0 -123
  214. package/docs/apple-touch-icon.svg +0 -4
  215. package/docs/assets/css/style.scss +0 -392
  216. package/docs/favicon.svg +0 -4
  217. package/docs/index.md +0 -164
  218. package/docs/mini-guides/file-uploads-complete.md +0 -161
  219. package/eslint.config.js +0 -13
  220. package/examples/url-override-example.js +0 -137
  221. package/fixing_plan.md +0 -112
  222. package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +0 -657
  223. package/plugins/core/lib/querying-writing/return-record-settings.js +0 -47
  224. package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +0 -110
  225. package/plugins/storage/s3-storage.js +0 -93
  226. package/quickTest.js +0 -136
  227. package/scripts/debug-anyapi-review-filter.mjs +0 -63
  228. package/scripts/inspect-anyapi-descriptor-country.mjs +0 -19
  229. package/scripts/inspect-anyapi-descriptor.mjs +0 -19
  230. package/scripts/run-docs.js +0 -133
  231. package/test-debug.js +0 -59
  232. package/tests/README.md +0 -202
  233. package/tests/TEST_TEMPLATE.test.js +0 -178
  234. package/tests/anyapi-basic.test.js +0 -105
  235. package/tests/anyapi-cursor-pagination.test.js +0 -148
  236. package/tests/anyapi-custom-idproperty.test.js +0 -163
  237. package/tests/autofilter.test.js +0 -463
  238. package/tests/bulk-operations.test.js +0 -657
  239. package/tests/computed-fields.test.js +0 -586
  240. package/tests/cors.test.js +0 -834
  241. package/tests/curl.test.js +0 -467
  242. package/tests/custom-id-mysql-returning-fallback.test.js +0 -128
  243. package/tests/custom-id-simplified-post.test.js +0 -75
  244. package/tests/custom-idproperty-relationships.test.js +0 -932
  245. package/tests/db-introspection.test.js +0 -521
  246. package/tests/db-table-operations.test.js +0 -798
  247. package/tests/fastify-plugin.test.js +0 -184
  248. package/tests/field-getters.test.js +0 -370
  249. package/tests/field-setters.test.js +0 -306
  250. package/tests/file-handling.test.js +0 -155
  251. package/tests/fixtures/api-configs.js +0 -2566
  252. package/tests/full-jsonapi-belongsto-linkage.test.js +0 -150
  253. package/tests/helpers/fake-fastify.js +0 -99
  254. package/tests/helpers/storage-mode.js +0 -65
  255. package/tests/helpers/test-utils.js +0 -203
  256. package/tests/http-connectors-parity.test.js +0 -395
  257. package/tests/id-normalization.test.js +0 -444
  258. package/tests/include-depth-validation.test.js +0 -331
  259. package/tests/includes.test.js +0 -427
  260. package/tests/local-storage.test.js +0 -96
  261. package/tests/logical-id-write-validation.test.js +0 -151
  262. package/tests/nested-includes.test.js +0 -617
  263. package/tests/pagination-boundaries.test.js +0 -154
  264. package/tests/pagination-contract.test.js +0 -122
  265. package/tests/pagination-cursor-multifield.test.js +0 -641
  266. package/tests/pagination-enhanced.test.js +0 -563
  267. package/tests/polymorphic-relationships.test.js +0 -347
  268. package/tests/polymorphic-simple.test.js +0 -148
  269. package/tests/positioning-advanced.test.js +0 -692
  270. package/tests/positioning-extended.test.js +0 -915
  271. package/tests/positioning-multifilter.test.js +0 -699
  272. package/tests/positioning.test.js +0 -560
  273. package/tests/post-commit-hooks.test.js +0 -120
  274. package/tests/projected-fields.test.js +0 -319
  275. package/tests/public-exports.test.js +0 -15
  276. package/tests/put-create-parity.test.js +0 -216
  277. package/tests/queries.test.js +0 -521
  278. package/tests/query-limits.test.js +0 -584
  279. package/tests/relationship-endpoints.test.js +0 -620
  280. package/tests/relationship-include-storage-adapter.test.js +0 -56
  281. package/tests/relationships.test.js +0 -681
  282. package/tests/rest-api.test.js +0 -337
  283. package/tests/return-record-settings.test.js +0 -184
  284. package/tests/row-policy.test.js +0 -489
  285. package/tests/s3-storage.test.js +0 -38
  286. package/tests/searchschema-knex-behavior.test.js +0 -187
  287. package/tests/searchschema-merge.test.js +0 -151
  288. package/tests/socketio.test.js +0 -860
  289. package/tests/storage-mapping.test.js +0 -362
  290. package/tests/temporal-boundaries.test.js +0 -208
  291. package/tests/test-development-guide.md +0 -370
  292. package/tests/virtual-fields.test.js +0 -404
@@ -0,0 +1,53 @@
1
+ import type { DirectResourceId, ResourceFormat, WriteReturning } from './representations.js'
2
+ import type { RemovedResourceOptions } from './resource-methods.js'
3
+ import type { Knex } from 'knex'
4
+
5
+ export type RestApiPluginOptions = RemovedResourceOptions & {
6
+ format?: ResourceFormat
7
+ returning?: WriteReturning
8
+ queryDefaultLimit?: number
9
+ queryMaxLimit?: number
10
+ includeDepthLimit?: number
11
+ enablePaginationCounts?: boolean
12
+ idProperty?: string
13
+ normalizeId?: (value: unknown) => DirectResourceId | null | undefined
14
+ }
15
+
16
+ export interface HttpConnectorOptions {
17
+ mountPath?: string
18
+ publicBaseUrl?: string
19
+ strictContentType?: boolean
20
+ handle404?: boolean
21
+ httpValidators?: boolean
22
+ }
23
+
24
+ export interface ResourceVersionOptions {
25
+ // Compilation validates that this names a stored, untransformed string attribute.
26
+ versionField?: string
27
+ }
28
+
29
+ export type CorsOrigin = string | RegExp | ((origin: string) => boolean | Promise<boolean>) | CorsOrigin[]
30
+ export interface CorsPluginOptions {
31
+ origin?: CorsOrigin
32
+ credentials?: boolean
33
+ methods?: string[]
34
+ allowedHeaders?: string[]
35
+ exposedHeaders?: string[]
36
+ maxAge?: number
37
+ optionsSuccessStatus?: number
38
+ }
39
+
40
+ export interface RestApiKnexPluginOptions {
41
+ knex: Knex
42
+ }
43
+ export interface RestApiAnyapiKnexPluginOptions extends RestApiKnexPluginOptions {
44
+ tenantId?: string
45
+ }
46
+ export interface BulkOperationsPluginOptions {
47
+ maxBulkOperations?: number
48
+ defaultAtomic?: boolean
49
+ }
50
+ export interface LabelPluginOptions {
51
+ preferNameFields?: string[]
52
+ disable?: boolean
53
+ }
@@ -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.