@rebasepro/server 0.24.0 → 0.24.1-canary.gcb918fc

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 (224) hide show
  1. package/README.md +27 -2
  2. package/dist/GCSStorageController-fv6JAZ87.js +402 -0
  3. package/dist/GCSStorageController-fv6JAZ87.js.map +1 -0
  4. package/dist/S3StorageController-BNg-pXiY.js +480 -0
  5. package/dist/S3StorageController-BNg-pXiY.js.map +1 -0
  6. package/dist/api/mount-path.d.ts +30 -0
  7. package/dist/api/mount.d.ts +6 -13
  8. package/dist/api/rest/api-generator.d.ts +53 -1
  9. package/dist/api/rest/auth-collection-writes.d.ts +28 -21
  10. package/dist/api/rest/field-access-query.d.ts +28 -8
  11. package/dist/api/rest/field-ops.d.ts +19 -7
  12. package/dist/api/rest/query-body.d.ts +10 -0
  13. package/dist/api/rest/write-validation.d.ts +7 -5
  14. package/dist/api/schema-editor-routes.d.ts +10 -1
  15. package/dist/api/sql-export-routes.d.ts +8 -0
  16. package/dist/api/types.d.ts +35 -0
  17. package/dist/{ast-schema-editor-CWqS_sLJ.js → ast-schema-editor-mCeSC03x.js} +3 -3
  18. package/dist/{ast-schema-editor-CWqS_sLJ.js.map → ast-schema-editor-mCeSC03x.js.map} +1 -1
  19. package/dist/auth/access.d.ts +57 -8
  20. package/dist/auth/admin-user-ops.d.ts +24 -2
  21. package/dist/auth/admin-users-route.d.ts +1 -0
  22. package/dist/auth/api-keys/api-key-middleware.d.ts +10 -3
  23. package/dist/auth/api-keys/api-key-routes.d.ts +4 -1
  24. package/dist/auth/builtin-auth-adapter.d.ts +13 -0
  25. package/dist/auth/email-change-routes.d.ts +3 -0
  26. package/dist/auth/expired-token-sweep.d.ts +5 -1
  27. package/dist/auth/interfaces.d.ts +10 -0
  28. package/dist/auth/jwks-routes.d.ts +5 -1
  29. package/dist/auth/jwt-crypto.d.ts +7 -0
  30. package/dist/auth/jwt-keys.d.ts +4 -0
  31. package/dist/auth/jwt.d.ts +282 -166
  32. package/dist/auth/magic-link-routes.d.ts +3 -0
  33. package/dist/auth/mfa-crypto.d.ts +9 -3
  34. package/dist/auth/mfa-gate.d.ts +4 -2
  35. package/dist/auth/mfa-routes.d.ts +6 -0
  36. package/dist/auth/middleware.d.ts +9 -2
  37. package/dist/auth/otp-routes.d.ts +3 -0
  38. package/dist/auth/rate-limiter.d.ts +61 -34
  39. package/dist/auth/registration-policy.d.ts +17 -0
  40. package/dist/auth/require-auth.d.ts +1 -1
  41. package/dist/auth/routes.d.ts +16 -0
  42. package/dist/auth/session-routes.d.ts +6 -0
  43. package/dist/auth/user-picking.d.ts +37 -0
  44. package/dist/auth/verify-credential.d.ts +25 -4
  45. package/dist/{auth-DMLngxn_.js → auth-GnONzteu.js} +2869 -1871
  46. package/dist/auth-GnONzteu.js.map +1 -0
  47. package/dist/auth_adapter-CIItCTc8.js +21 -0
  48. package/dist/auth_adapter-CIItCTc8.js.map +1 -0
  49. package/dist/backend/create-backend.d.ts +281 -0
  50. package/dist/backend/default-backend.d.ts +35 -0
  51. package/dist/backend/options.d.ts +912 -0
  52. package/dist/backend/scope.d.ts +23 -0
  53. package/dist/backend/services.d.ts +54 -0
  54. package/dist/backend-DTAOsLQc.js.map +1 -1
  55. package/dist/backup/backup-common.d.ts +14 -3
  56. package/dist/backup/backup-routes.d.ts +23 -2
  57. package/dist/backup/backup-storage.d.ts +3 -1
  58. package/dist/backup/index.d.ts +3 -1
  59. package/dist/{backup-CN0s50D2.js → backup-BnvkSLHi.js} +176 -35
  60. package/dist/backup-BnvkSLHi.js.map +1 -0
  61. package/dist/boot/boot.d.ts +2 -2
  62. package/dist/boot/driver.d.ts +3 -0
  63. package/dist/boot/env.d.ts +9 -2
  64. package/dist/boot/http-server.d.ts +22 -0
  65. package/dist/boot/options.d.ts +16 -1
  66. package/dist/boot/provision.d.ts +4 -2
  67. package/dist/boot/runtime-cors.d.ts +17 -0
  68. package/dist/boot/sources.d.ts +2 -0
  69. package/dist/boot/static-routing.d.ts +9 -0
  70. package/dist/{contract-routes-fz8i4pxs.js → contract-routes-Ey6ftnKH.js} +4 -20
  71. package/dist/contract-routes-Ey6ftnKH.js.map +1 -0
  72. package/dist/cron/cron-loader.d.ts +77 -18
  73. package/dist/cron/cron-routes.d.ts +1 -1
  74. package/dist/cron/cron-scheduler.d.ts +69 -3
  75. package/dist/cron/cron-store.d.ts +33 -5
  76. package/dist/cron/define-cron.d.ts +6 -2
  77. package/dist/cron/index.d.ts +3 -3
  78. package/dist/cron-loader-B-u0snqq.js +202 -0
  79. package/dist/cron-loader-B-u0snqq.js.map +1 -0
  80. package/dist/{cron-routes-Bc-SB0Se.js → cron-routes-OD6BstUj.js} +27 -4
  81. package/dist/cron-routes-OD6BstUj.js.map +1 -0
  82. package/dist/{cron-scheduler-CYQgco86.js → cron-scheduler-idtLPoN-.js} +299 -33
  83. package/dist/cron-scheduler-idtLPoN-.js.map +1 -0
  84. package/dist/cron-store-CZ30ZBi-.js +577 -0
  85. package/dist/cron-store-CZ30ZBi-.js.map +1 -0
  86. package/dist/email/smtp-email-service.d.ts +23 -0
  87. package/dist/email/types.d.ts +18 -0
  88. package/dist/env.d.ts +0 -37
  89. package/dist/filter-dialect-O1HK6Nvr.js +1628 -0
  90. package/dist/filter-dialect-O1HK6Nvr.js.map +1 -0
  91. package/dist/{function-loader-D7o5Epjj.js → function-loader-iKhOZatS.js} +68 -38
  92. package/dist/function-loader-iKhOZatS.js.map +1 -0
  93. package/dist/{function-routes-CaNG4waN.js → function-routes-j9TwpvQe.js} +5 -4
  94. package/dist/function-routes-j9TwpvQe.js.map +1 -0
  95. package/dist/functions/context.d.ts +9 -0
  96. package/dist/functions/define-function.d.ts +10 -6
  97. package/dist/functions/function-loader.d.ts +75 -18
  98. package/dist/functions/function-routes.d.ts +2 -1
  99. package/dist/functions/index.d.ts +5 -4
  100. package/dist/functions/index.js +42 -13
  101. package/dist/functions/index.js.map +1 -1
  102. package/dist/functions/internal.d.ts +2 -2
  103. package/dist/functions/proxy.d.ts +6 -2
  104. package/dist/functions/request-timeout.d.ts +7 -5
  105. package/dist/history/history-routes.d.ts +9 -0
  106. package/dist/history/retention-sweep.d.ts +81 -0
  107. package/dist/{history-recorder-Nr8zLvoU.js → history-recorder-BfWRwmPy.js} +3 -3
  108. package/dist/{history-recorder-Nr8zLvoU.js.map → history-recorder-BfWRwmPy.js.map} +1 -1
  109. package/dist/{history-store-rcAm_xFR.js → history-store-DPofGVDp.js} +2 -2
  110. package/dist/{history-store-rcAm_xFR.js.map → history-store-DPofGVDp.js.map} +1 -1
  111. package/dist/index.d.ts +23 -10
  112. package/dist/index.es.js +24423 -20038
  113. package/dist/index.es.js.map +1 -1
  114. package/dist/init/docs.d.ts +27 -10
  115. package/dist/init/process-safety.d.ts +7 -2
  116. package/dist/init/shutdown.d.ts +18 -3
  117. package/dist/init.d.ts +49 -865
  118. package/dist/internal-tables-L2t39RdT.js +136 -0
  119. package/dist/internal-tables-L2t39RdT.js.map +1 -0
  120. package/dist/jobs/job-queue.d.ts +8 -0
  121. package/dist/jobs/job-store.d.ts +24 -0
  122. package/dist/jobs/types.d.ts +25 -4
  123. package/dist/{jobs-DqYNfquG.js → jobs-BCbflZj2.js} +234 -24
  124. package/dist/jobs-BCbflZj2.js.map +1 -0
  125. package/dist/{jwt-R6bSPMjk.js → jwt-BlvuKoR6.js} +529 -454
  126. package/dist/jwt-BlvuKoR6.js.map +1 -0
  127. package/dist/{keys-GAVZqbqx.js → keys-COzQ2aE8.js} +2 -2
  128. package/dist/{keys-GAVZqbqx.js.map → keys-COzQ2aE8.js.map} +1 -1
  129. package/dist/load-failure-L5hvfDoG.js +80 -0
  130. package/dist/load-failure-L5hvfDoG.js.map +1 -0
  131. package/dist/mcp/mcp-routes.d.ts +6 -0
  132. package/dist/mcp/mcp-tools.d.ts +8 -0
  133. package/dist/mcp/oauth-routes.d.ts +7 -0
  134. package/dist/metrics/index.d.ts +18 -0
  135. package/dist/metrics/pool-wait.d.ts +50 -0
  136. package/dist/offline-QNQgsPOq-B2yGeXkn.js +3305 -0
  137. package/dist/offline-QNQgsPOq-B2yGeXkn.js.map +1 -0
  138. package/dist/offline-store-Cm5sAX81-CALiO4Gn.js +1915 -0
  139. package/dist/offline-store-Cm5sAX81-CALiO4Gn.js.map +1 -0
  140. package/dist/{openapi-generator-DAq_XVDu.js → openapi-generator-D9zUQmEp.js} +366 -82
  141. package/dist/openapi-generator-D9zUQmEp.js.map +1 -0
  142. package/dist/policy-PdzX-Opd.js +121 -0
  143. package/dist/policy-PdzX-Opd.js.map +1 -0
  144. package/dist/project_manifest-R6y_KHms.js +59 -0
  145. package/dist/project_manifest-R6y_KHms.js.map +1 -0
  146. package/dist/{proxy-qRlqeUmO.js → proxy-CkTlMGmh.js} +4 -3
  147. package/dist/proxy-CkTlMGmh.js.map +1 -0
  148. package/dist/{query-parser-BgiKJKvc.js → query-parser-4WkMoA_K.js} +510 -15
  149. package/dist/query-parser-4WkMoA_K.js.map +1 -0
  150. package/dist/{request-timeout-DgH7j8qO.js → request-timeout-Dnl2_OBc.js} +15 -8
  151. package/dist/request-timeout-Dnl2_OBc.js.map +1 -0
  152. package/dist/resumable-upload-threshold-CKv-Ry0l.js +24 -0
  153. package/dist/resumable-upload-threshold-CKv-Ry0l.js.map +1 -0
  154. package/dist/schema-edit/apply-schema-change.d.ts +10 -0
  155. package/dist/schema-edit/local-source.d.ts +29 -0
  156. package/dist/schema-edit/remote-source.d.ts +13 -3
  157. package/dist/schema-edit/source-editor.d.ts +28 -0
  158. package/dist/{schema-editor-routes-oIyuWl3L.js → schema-editor-routes-C11Tumyd.js} +54 -10
  159. package/dist/schema-editor-routes-C11Tumyd.js.map +1 -0
  160. package/dist/services/outbound-post.d.ts +50 -0
  161. package/dist/services/outbound-url-guard.d.ts +34 -5
  162. package/dist/services/routed-realtime-service.d.ts +5 -0
  163. package/dist/services/webhook-service.d.ts +66 -14
  164. package/dist/singleton.d.ts +9 -5
  165. package/dist/soft-delete-params-DfV3vVN5.js +668 -0
  166. package/dist/soft-delete-params-DfV3vVN5.js.map +1 -0
  167. package/dist/sql-export-routes-ClRwzIsC.js +240 -0
  168. package/dist/sql-export-routes-ClRwzIsC.js.map +1 -0
  169. package/dist/{src-CatHFUym.js → src-D5Y3N-S4.js} +2 -1
  170. package/dist/{src-CatHFUym.js.map → src-D5Y3N-S4.js.map} +1 -1
  171. package/dist/storage/GCSStorageController.d.ts +46 -2
  172. package/dist/storage/LocalStorageController.d.ts +67 -2
  173. package/dist/storage/S3StorageController.d.ts +37 -3
  174. package/dist/storage/image-transform.d.ts +5 -0
  175. package/dist/storage/maintenance-routes.d.ts +23 -0
  176. package/dist/storage/maintenance.d.ts +126 -0
  177. package/dist/storage/rendition-cache.d.ts +9 -11
  178. package/dist/storage/request-principals.d.ts +30 -0
  179. package/dist/storage/routes.d.ts +20 -0
  180. package/dist/storage/source-variables.d.ts +10 -0
  181. package/dist/storage/streams.d.ts +42 -0
  182. package/dist/storage/tus-handler.d.ts +46 -2
  183. package/dist/storage/types.d.ts +82 -7
  184. package/dist/storage/upload-validation.d.ts +13 -0
  185. package/dist/storage-resumable-VO-cMpse-C_YX71pO.js +125 -0
  186. package/dist/storage-resumable-VO-cMpse-C_YX71pO.js.map +1 -0
  187. package/dist/strings.es-BeZWAodn.js +256 -0
  188. package/dist/strings.es-BeZWAodn.js.map +1 -0
  189. package/dist/upload-validation-Dd-fnsbA.js +172 -0
  190. package/dist/upload-validation-Dd-fnsbA.js.map +1 -0
  191. package/dist/utils/byte-size.d.ts +10 -0
  192. package/dist/utils/compression.d.ts +6 -0
  193. package/dist/utils/load-failure.d.ts +14 -0
  194. package/package.json +5 -5
  195. package/dist/GCSStorageController-BSiP1c-f.js +0 -256
  196. package/dist/GCSStorageController-BSiP1c-f.js.map +0 -1
  197. package/dist/S3StorageController-CAwFRgjV.js +0 -276
  198. package/dist/S3StorageController-CAwFRgjV.js.map +0 -1
  199. package/dist/auth-DMLngxn_.js.map +0 -1
  200. package/dist/backup-CN0s50D2.js.map +0 -1
  201. package/dist/contract-routes-fz8i4pxs.js.map +0 -1
  202. package/dist/cron-loader-CwaANlOG.js +0 -118
  203. package/dist/cron-loader-CwaANlOG.js.map +0 -1
  204. package/dist/cron-routes-Bc-SB0Se.js.map +0 -1
  205. package/dist/cron-scheduler-CYQgco86.js.map +0 -1
  206. package/dist/cron-store-D2Q9-Aco.js +0 -300
  207. package/dist/cron-store-D2Q9-Aco.js.map +0 -1
  208. package/dist/dynamic-import-X40pTZUQ.js +0 -23
  209. package/dist/dynamic-import-X40pTZUQ.js.map +0 -1
  210. package/dist/function-loader-D7o5Epjj.js.map +0 -1
  211. package/dist/function-routes-CaNG4waN.js.map +0 -1
  212. package/dist/jobs-DqYNfquG.js.map +0 -1
  213. package/dist/jwt-R6bSPMjk.js.map +0 -1
  214. package/dist/openapi-generator-DAq_XVDu.js.map +0 -1
  215. package/dist/proxy-qRlqeUmO.js.map +0 -1
  216. package/dist/query-parser-BgiKJKvc.js.map +0 -1
  217. package/dist/request-timeout-DgH7j8qO.js.map +0 -1
  218. package/dist/schema-editor-routes-oIyuWl3L.js.map +0 -1
  219. package/dist/soft-delete-params-BWPilMPF.js +0 -59
  220. package/dist/soft-delete-params-BWPilMPF.js.map +0 -1
  221. package/dist/src-I3aG1PcY.js +0 -6870
  222. package/dist/src-I3aG1PcY.js.map +0 -1
  223. package/dist/types-BfKcm9do.js +0 -39
  224. package/dist/types-BfKcm9do.js.map +0 -1
@@ -2,10 +2,262 @@ import { createRequire as __rebaseCreateRequire } from "module";
2
2
  import __rebaseProcess from "process";
3
3
  globalThis.process ??= __rebaseProcess;
4
4
  __rebaseCreateRequire(import.meta.url);
5
- import { C as decodeCursor, E as restrictedFieldNames, W as resolveCollectionRelations, _ as OrderBySpecError, a as deserializeFilter, b as CursorError, et as ListLimitError, f as IncludeSpecError, h as normalizeInclude, i as UnknownFilterOperatorError, nt as resolveClientListLimit, o as deserializeLogicalCondition, p as deserializeInclude, r as RESERVED_QUERY_KEYS, w as reconcileCursorOrder, x as CursorMismatchError } from "./src-I3aG1PcY.js";
6
- import "./src-CatHFUym.js";
5
+ import { i as ListLimitError, o as resolveClientListLimit } from "./strings.es-BeZWAodn.js";
6
+ import { d as IncludeSpecError, f as deserializeInclude, g as OrderBySpecError, i as deserializeLogicalCondition, m as normalizeInclude, n as UnknownFilterOperatorError, r as deserializeFilter, s as PAGE_WITH_OFFSET_MESSAGE, t as RESERVED_QUERY_KEYS } from "./filter-dialect-O1HK6Nvr.js";
7
+ import { $ as parseRelationAggregateSort } from "./src-D5Y3N-S4.js";
8
+ import { a as apiHiddenFieldNames, c as fieldKeyForColumn, i as parseWithDeleted, l as findRelation, m as resolveCollectionRelations, s as restrictedFieldNames, t as DELETED_QUERY_PARAM } from "./soft-delete-params-DfV3vVN5.js";
7
9
  import { t as ApiError } from "./errors-D6_y86c5.js";
8
- import { i as parseWithDeleted, t as DELETED_QUERY_PARAM } from "./soft-delete-params-BWPilMPF.js";
10
+ //#region ../common/src/collections/unknown-field.ts
11
+ /**
12
+ * The fields an unknown-field answer offers as valid, sorted: what a query on
13
+ * this collection can name as a column. A relation contributes its foreign key
14
+ * (`authorId`) when the key is on this table, and nothing otherwise. An
15
+ * `excludeFromApi` field is never offered.
16
+ *
17
+ * Read off the collection rather than the table, so the REST check — which
18
+ * holds no table — lists exactly what the driver lists.
19
+ */
20
+ function queryableFieldNames(collection) {
21
+ const hidden = apiHiddenFieldNames(collection);
22
+ const names = /* @__PURE__ */ new Set();
23
+ let relations;
24
+ for (const [key, property] of Object.entries(collection.properties ?? {})) {
25
+ if (property?.type === "relation") {
26
+ relations ??= resolveCollectionRelations(collection);
27
+ const relation = findRelation(relations, key);
28
+ if (relation?.kind === "belongsTo") names.add(fieldKeyForColumn(collection, relation.localKey));
29
+ continue;
30
+ }
31
+ names.add(key);
32
+ }
33
+ return [...names].filter((name) => !hidden.has(name)).sort();
34
+ }
35
+ var validList = (validFields) => validFields.length > 0 ? `. Valid fields: ${validFields.join(", ")}` : "";
36
+ /** A filter key that resolves to nothing on `collectionPath`. */
37
+ function unknownFilterField(field, collectionPath, validFields) {
38
+ return {
39
+ message: `Unknown filter field '${field}' on collection '${collectionPath}'${validList(validFields)}`,
40
+ code: "UNKNOWN_FILTER_FIELD",
41
+ details: {
42
+ field,
43
+ collection: collectionPath,
44
+ ...validFields.length > 0 && { validFields }
45
+ }
46
+ };
47
+ }
48
+ /** `author.emial` — a relation path whose last segment names nothing on the relation's target. */
49
+ function unknownRelationTargetField(path, target, collectionPath) {
50
+ return {
51
+ message: `Unknown field '${path.fieldKey}' on '${target.slug}', the target of relation '${path.relationKey}' on collection '${collectionPath}'${validList(target.validFields)}`,
52
+ code: "UNKNOWN_FILTER_FIELD",
53
+ details: {
54
+ field: path.field,
55
+ collection: collectionPath,
56
+ relation: path.relationKey,
57
+ targetCollection: target.slug,
58
+ ...target.validFields.length > 0 && { validFields: target.validFields }
59
+ }
60
+ };
61
+ }
62
+ /** An `orderBy` key that resolves to nothing. `collectionName` is absent when the read carries no collection. */
63
+ function unknownOrderByField(orderBy, collectionName, validFields) {
64
+ return {
65
+ message: `Unknown orderBy field '${orderBy}'${collectionName ? ` on collection '${collectionName}'` : ""}${validList(validFields)}`,
66
+ code: "UNKNOWN_ORDER_BY_FIELD",
67
+ details: {
68
+ field: orderBy,
69
+ ...collectionName && { collection: collectionName },
70
+ ...validFields.length > 0 && { validFields }
71
+ }
72
+ };
73
+ }
74
+ /** A `fields` entry that names nothing. */
75
+ function unknownProjectionField(name, collectionName, validFields) {
76
+ return {
77
+ message: `Unknown field '${name}' in \`fields\` on collection '${collectionName}'. Valid fields: ${validFields.join(", ")}`,
78
+ code: "UNKNOWN_FIELD",
79
+ details: {
80
+ field: name,
81
+ collection: collectionName
82
+ }
83
+ };
84
+ }
85
+ /**
86
+ * An `include` naming no relation of the collection. `validRelations` is what
87
+ * the collection declares, never an `excludeFromApi` relation.
88
+ */
89
+ function unknownIncludeRelation(key, collectionName, validRelations) {
90
+ return {
91
+ message: `Unknown relation '${key}' on collection '${collectionName}'` + (validRelations.length > 0 ? `. Its relations are: ${validRelations.join(", ")}` : ". It declares no relations."),
92
+ code: "UNKNOWN_RELATION",
93
+ details: {
94
+ relation: key,
95
+ collection: collectionName,
96
+ ...validRelations.length > 0 && { validRelations }
97
+ }
98
+ };
99
+ }
100
+ /** The relations an unknown-relation answer offers, sorted — never an `excludeFromApi` one. */
101
+ function includableRelationNames(collection) {
102
+ const hidden = apiHiddenFieldNames(collection);
103
+ return Object.keys(resolveCollectionRelations(collection)).filter((key) => !hidden.has(key)).sort();
104
+ }
105
+ /**
106
+ * An aggregate's `select` field or `groupBy` key that names no column.
107
+ * `forWhat` is where it was named: `sum()`, `count()`, `groupBy`.
108
+ * `validFields` is {@link queryableFieldNames}, never an `excludeFromApi` field.
109
+ */
110
+ function unknownAggregateField(field, forWhat, validFields) {
111
+ return {
112
+ message: `Unknown field '${field}' in ${forWhat}${validList(validFields)}`,
113
+ code: "UNKNOWN_AGGREGATE_FIELD",
114
+ details: {
115
+ field,
116
+ ...validFields.length > 0 && { validFields }
117
+ }
118
+ };
119
+ }
120
+ /**
121
+ * The properties a `vector_search` can name, sorted: those declared
122
+ * `{ type: "vector" }`, never an `excludeFromApi` one.
123
+ */
124
+ function vectorPropertyNames(collection) {
125
+ const hidden = apiHiddenFieldNames(collection);
126
+ return Object.entries(collection.properties ?? {}).filter(([key, property]) => property?.type === "vector" && !hidden.has(key)).map(([key]) => key).sort();
127
+ }
128
+ /** A `vector_search` naming no vector property. `vectorProperties` is {@link vectorPropertyNames}. */
129
+ function unknownVectorProperty(property, vectorProperties) {
130
+ return {
131
+ message: `Unknown vector property "${property}". ` + (vectorProperties.length > 0 ? `This collection's vector properties are: ${vectorProperties.join(", ")}.` : "This collection declares no `vector` property to search."),
132
+ code: "UNKNOWN_VECTOR_PROPERTY",
133
+ details: {
134
+ property,
135
+ ...vectorProperties.length > 0 && { vectorProperties }
136
+ }
137
+ };
138
+ }
139
+ //#endregion
140
+ //#region ../common/src/data/cursor.ts
141
+ /** A cursor that cannot be read at all — truncated, re-encoded, or invented. */
142
+ var CursorError = class CursorError extends Error {
143
+ code = "INVALID_CURSOR";
144
+ constructor(detail) {
145
+ super(`Invalid \`after\` cursor: ${detail}. Pass back the \`meta.nextCursor\` from the previous page unchanged — it is opaque and must not be built by hand.`);
146
+ this.name = "CursorError";
147
+ Object.setPrototypeOf(this, CursorError.prototype);
148
+ }
149
+ };
150
+ /**
151
+ * A cursor that reads fine but describes a different query.
152
+ *
153
+ * Separate from {@link CursorError} because the fix is different: this one is
154
+ * not a corrupt string, it is a correct cursor used against a sort it was not
155
+ * produced under. Seeking anyway would return rows in an order nobody asked
156
+ * for, and — worse — would look like it worked.
157
+ */
158
+ var CursorMismatchError = class CursorMismatchError extends Error {
159
+ code = "CURSOR_ORDER_MISMATCH";
160
+ constructor(cursorKeys, queryKeys) {
161
+ super(`The \`after\` cursor was produced by a query ordered by ${cursorKeys.map((k) => `"${k}"`).join(", ") || "(nothing)"}, but this query orders by ${queryKeys.map((k) => `"${k}"`).join(", ") || "(nothing)"}. A cursor only continues the listing it came from — keep \`orderBy\` identical across pages, or drop \`after\` to start over.`);
162
+ this.name = "CursorMismatchError";
163
+ Object.setPrototypeOf(this, CursorMismatchError.prototype);
164
+ }
165
+ };
166
+ /**
167
+ * Tag for a value whose JSON round-trip would otherwise lose its type.
168
+ *
169
+ * A `timestamp` column comes back from the driver as a `Date`; JSON turns it
170
+ * into a string, and the string would then be compared against the column by
171
+ * whatever cast Postgres chose. Round-tripping it as a `Date` keeps the
172
+ * comparison the one the ORDER BY made.
173
+ */
174
+ var DATE_TAG = "$date";
175
+ function decodeValue(value) {
176
+ if (value && typeof value === "object" && !Array.isArray(value)) {
177
+ const tagged = value[DATE_TAG];
178
+ if (typeof tagged === "string") {
179
+ const date = new Date(tagged);
180
+ return Number.isNaN(date.getTime()) ? tagged : date;
181
+ }
182
+ }
183
+ return value;
184
+ }
185
+ function fromBase64Url(encoded) {
186
+ const padded = encoded.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - encoded.length % 4) % 4);
187
+ const binary = atob(padded);
188
+ const bytes = new Uint8Array(binary.length);
189
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
190
+ return new TextDecoder().decode(bytes);
191
+ }
192
+ /**
193
+ * Read a cursor produced by {@link encodeCursor}.
194
+ *
195
+ * @throws {CursorError} when the string is not a cursor this codec wrote.
196
+ */
197
+ function decodeCursor(raw) {
198
+ let parsed;
199
+ try {
200
+ parsed = JSON.parse(fromBase64Url(raw.trim()));
201
+ } catch {
202
+ throw new CursorError("it is not a cursor this API issued");
203
+ }
204
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new CursorError("it does not decode to a cursor");
205
+ const body = parsed;
206
+ if (!Array.isArray(body.k)) throw new CursorError("it carries no sort keys");
207
+ if (body.i === void 0) throw new CursorError("it carries no row id");
208
+ const orderBy = [];
209
+ for (const entry of body.k) {
210
+ if (!Array.isArray(entry) || typeof entry[0] !== "string") throw new CursorError("one of its sort keys is malformed");
211
+ const direction = entry[1] === "desc" ? "desc" : "asc";
212
+ orderBy.push(entry[2] === "first" || entry[2] === "last" ? [
213
+ entry[0],
214
+ direction,
215
+ entry[2]
216
+ ] : [entry[0], direction]);
217
+ }
218
+ const rawValues = body.v && typeof body.v === "object" && !Array.isArray(body.v) ? body.v : {};
219
+ const values = {};
220
+ for (const [field, value] of Object.entries(rawValues)) values[field] = decodeValue(value);
221
+ return {
222
+ orderBy,
223
+ values,
224
+ id: decodeValue(body.i)
225
+ };
226
+ }
227
+ /**
228
+ * The `orderBy` a request should run under, given a cursor and whatever sort
229
+ * the request itself named.
230
+ *
231
+ * A request that names no sort **adopts the cursor's** — that is what makes
232
+ * `find({ after })` work without restating the `orderBy` from the previous
233
+ * call, and it cannot be wrong, since the cursor is the only sort in play.
234
+ * A request that names one must name the *same* one, key for key, direction for
235
+ * direction, nulls for nulls; anything else is {@link CursorMismatchError}.
236
+ *
237
+ * @throws {CursorMismatchError}
238
+ */
239
+ function reconcileCursorOrder(cursor, requested) {
240
+ if (!requested || requested.length === 0) return cursor.orderBy;
241
+ const spell = (keys) => keys.map(([field, direction, nulls]) => `${field}:${direction}${nulls ? `:${nulls}` : ""}`);
242
+ const cursorKeys = spell(cursor.orderBy);
243
+ const queryKeys = spell(requested);
244
+ if (cursorKeys.length !== queryKeys.length || cursorKeys.some((key, i) => key !== queryKeys[i])) throw new CursorMismatchError(cursorKeys, queryKeys);
245
+ return requested;
246
+ }
247
+ /**
248
+ * The `startAfter` shape the driver contract takes, built from a cursor.
249
+ *
250
+ * The driver has always accepted `{ id, values }`; this is the one place that
251
+ * shape is produced, so the REST route and the WebSocket ingress cannot drift
252
+ * into two spellings of the same seek.
253
+ */
254
+ function cursorToStartAfter(cursor) {
255
+ return {
256
+ id: cursor.id,
257
+ values: cursor.values
258
+ };
259
+ }
260
+ //#endregion
9
261
  //#region src/api/rest/field-access-query.ts
10
262
  /**
11
263
  * A read may not name a field the caller cannot read.
@@ -16,13 +268,22 @@ import { i as parseWithDeleted, t as DELETED_QUERY_PARAM } from "./soft-delete-p
16
268
  * `?orderBy=salary` returns them in order of it. A column no response can carry
17
269
  * has to be a column no query can interrogate, or the read rule is decoration.
18
270
  *
19
- * The refusal names the field. That is deliberate and it is not a leak: the
20
- * published OpenAPI lists every property of every collection, including the ones
21
- * a given caller cannot read, because the document is one document and is served
22
- * off the app rather than off the authenticated data router. Hiding the name
23
- * here would protect nothing and would answer a caller's genuine typo with
24
- * "unknown field", sending them to look for a spelling mistake that is not
25
- * there. Field *names* are public; field *values* are not.
271
+ * The refusal names a field restricted by role. That is deliberate and it is
272
+ * not a leak: the published OpenAPI lists every property of every collection,
273
+ * including the ones a given caller cannot read, because the document is one
274
+ * document and is served off the app rather than off the authenticated data
275
+ * router. Hiding the name here would protect nothing and would answer a
276
+ * caller's genuine typo with "unknown field", sending them to look for a
277
+ * spelling mistake that is not there. Those field *names* are public; their
278
+ * *values* are not.
279
+ *
280
+ * An `excludeFromApi` field is the exception, because its name is *not*
281
+ * public: OpenAPI, `list_collections` and the generated SDK all leave it out.
282
+ * "`email` is not readable on `authors`" would confirm the one thing those hide,
283
+ * so a filter, sort, projection, include, aggregate or vector search naming one
284
+ * is answered as one naming a field that does not exist — built by the functions the driver answers a
285
+ * typo with (`@rebasepro/common`'s `unknown-field`), so the two answers are the
286
+ * same bytes. See {@link assertNoHiddenNames}.
26
287
  *
27
288
  * @module
28
289
  */
@@ -86,7 +347,8 @@ function assertReadableFields(names, collection, viewer, where) {
86
347
  if (names.length === 0) return;
87
348
  const { refused } = restrictedFieldNames(collection, viewer, "read");
88
349
  if (refused.size === 0) return;
89
- const named = [...new Set(names.filter((n) => n !== void 0 && refused.has(n)))];
350
+ const hidden = apiHiddenFieldNames(collection);
351
+ const named = [...new Set(names.filter((n) => n !== void 0 && refused.has(n) && !hidden.has(n)))];
90
352
  if (named.length === 0) return;
91
353
  throw ApiError.badRequest(`${named.map((f) => `'${f}'`).join(", ")} ${named.length > 1 ? "are" : "is"} not readable on '${collection.slug}' with your roles, so ${named.length > 1 ? "they" : "it"} cannot be used in ${WHERE_LABEL[where]}.`, "FIELD_NOT_READABLE", {
92
354
  collection: collection.slug,
@@ -106,15 +368,240 @@ function assertReadableFields(names, collection, viewer, where) {
106
368
  * parsed and then dropped by the list route.
107
369
  */
108
370
  function assertQueryFieldsReadable(options, collection, viewer) {
371
+ const filtered = assertRestrictedFieldsUnused(options, collection, viewer);
372
+ const include = options.include !== void 0 ? normalizeInclude(options.include) : void 0;
373
+ assertNoHiddenNames({
374
+ ...options,
375
+ filtered,
376
+ include: include?.tree
377
+ }, collection);
378
+ }
379
+ /**
380
+ * The aggregate route's `select` and `groupBy`, which it parses itself: an
381
+ * `excludeFromApi` name there is answered exactly as the driver answers a
382
+ * column that does not exist. Walked in the driver's order — each aggregate's
383
+ * field, then each group key — and the first hidden *or* unknown name gets the
384
+ * answer, so a typo before a hidden name is still the one named.
385
+ */
386
+ function assertNoHiddenAggregateNames(aggregates, groupBy, collection) {
387
+ const hidden = apiHiddenFieldNames(collection);
388
+ if (hidden.size === 0) return;
389
+ const asked = [...aggregates.flatMap(({ fn, field }) => field === void 0 ? [] : [{
390
+ name: field,
391
+ forWhat: `${fn}()`
392
+ }]), ...groupBy.map((name) => ({
393
+ name,
394
+ forWhat: "groupBy"
395
+ }))];
396
+ if (!asked.some(({ name }) => hidden.has(name))) return;
397
+ const valid = queryableFieldNames(collection);
398
+ const known = (name) => !hidden.has(name) && (valid.includes(name) || name in (collection.properties ?? {}));
399
+ for (const { name, forWhat } of asked) {
400
+ if (known(name)) continue;
401
+ const answer = unknownAggregateField(name, forWhat, valid);
402
+ throw ApiError.badRequest(answer.message, answer.code, answer.details);
403
+ }
404
+ }
405
+ /**
406
+ * Refuse every field restricted by role that the read names, at every level of
407
+ * its include tree. Returns the filter's column names, for the hidden pass.
408
+ */
409
+ function assertRestrictedFieldsUnused(options, collection, viewer) {
109
410
  const filtered = [];
110
411
  if (options.where) filtered.push(...Object.keys(options.where));
111
412
  logicalColumns(options.logical, filtered);
112
- assertReadableFields(filtered, collection, viewer, "filter");
413
+ assertReadableFields(filtered.map(filterHead), collection, viewer, "filter");
414
+ assertRelationPathsReadable(filtered, collection, viewer);
113
415
  assertReadableFields((options.orderBy ?? []).map((entry) => orderByColumn(entry.field)), collection, viewer, "orderBy");
416
+ assertAggregateSortsReadable(options.orderBy ?? [], collection, viewer);
114
417
  assertReadableFields(options.fields ?? [], collection, viewer, "fields");
115
418
  assertReadableFields(options.vectorSearch ? [options.vectorSearch.property] : [], collection, viewer, "vector_search");
116
419
  const include = options.include !== void 0 ? normalizeInclude(options.include) : void 0;
117
420
  if (include) assertIncludeFieldsReadable(include.tree, collection, viewer);
421
+ return filtered;
422
+ }
423
+ /**
424
+ * The field a filter key reads on *this* collection: the column before a JSON
425
+ * path's first arrow (`metadata->theme` reads `metadata`), the relation before
426
+ * a relation path's dot (`author.email` reads `author`), the key itself
427
+ * otherwise. A key that is a property outright wins, as the driver resolves it.
428
+ */
429
+ function filterHead(name) {
430
+ const arrow = name.indexOf("->");
431
+ if (arrow > 0) return name.slice(0, arrow);
432
+ const dot = name.indexOf(".");
433
+ if (dot > 0) return name.slice(0, dot);
434
+ return name;
435
+ }
436
+ /**
437
+ * `orderBy=max(staff.salary)` ranks the parents by the target's `salary` — the
438
+ * same reading `orderBy=salary` on `staff` is — so the aggregated field is
439
+ * judged by the target's rule. Whether the relation itself may be read is the
440
+ * driver's check (`assertRelationReadable`), which runs on every door.
441
+ */
442
+ function assertAggregateSortsReadable(orderBy, collection, viewer) {
443
+ let relations;
444
+ for (const { field } of orderBy) {
445
+ const spec = parseRelationAggregateSort(field);
446
+ if (!spec?.field) continue;
447
+ relations ??= resolveCollectionRelations(collection);
448
+ const relation = findRelation(relations, spec.relation);
449
+ if (!relation) continue;
450
+ assertReadableFields([spec.field], relation.target(), viewer, "orderBy");
451
+ }
452
+ }
453
+ /**
454
+ * `?author.salary=gt.100000` reads the target's `salary`, a row at a time, as
455
+ * `?salary=gt.100000` on `authors` would — so it is judged by the target's
456
+ * rule. Only a path whose first segment is a relation here; anything else is
457
+ * the driver's to refuse.
458
+ */
459
+ function assertRelationPathsReadable(filtered, collection, viewer) {
460
+ let relations;
461
+ for (const name of filtered) {
462
+ const dot = name.indexOf(".");
463
+ if (dot <= 0 || name.indexOf("->") >= 0) continue;
464
+ relations ??= resolveCollectionRelations(collection);
465
+ const relation = findRelation(relations, name.slice(0, dot));
466
+ if (!relation) continue;
467
+ assertReadableFields([name.slice(dot + 1)], relation.target(), viewer, "filter");
468
+ }
469
+ }
470
+ /**
471
+ * Answer a read naming an `excludeFromApi` field — or relation — exactly as the
472
+ * driver answers a read naming a field that does not exist.
473
+ *
474
+ * The names are walked in the order the driver resolves them — the `fields`
475
+ * projection, then the filter, then the sort, then each include, parent before
476
+ * child — and the first that is hidden *or* unknown gets the answer. Answering
477
+ * only the hidden one would still tell the two apart: with `?aaa=x&email=x` the
478
+ * driver names `aaa` when `email` does not exist, so this has to as well.
479
+ *
480
+ * A request naming nothing hidden is left alone entirely, so the driver stays
481
+ * the only one answering ordinary typos.
482
+ */
483
+ function assertNoHiddenNames(options, collection) {
484
+ const hidden = apiHiddenFieldNames(collection);
485
+ if (hidden.size > 0) {
486
+ const shapes = [
487
+ ...(options.fields ?? []).map((name) => ({
488
+ kind: "fields",
489
+ name
490
+ })),
491
+ ...options.filtered.map((name) => ({
492
+ kind: "filter",
493
+ name
494
+ })),
495
+ ...(options.orderBy ?? []).flatMap((entry) => {
496
+ const name = orderByColumn(entry.field);
497
+ return name === void 0 ? [] : [{
498
+ kind: "orderBy",
499
+ name
500
+ }];
501
+ })
502
+ ].map(({ kind, name }) => ({
503
+ kind,
504
+ name,
505
+ shape: nameShape(name, collection, hidden)
506
+ }));
507
+ const vectorHidden = options.vectorSearch !== void 0 && hidden.has(options.vectorSearch.property);
508
+ if (vectorHidden || shapes.some((entry) => entry.shape.hidden)) for (const { kind, name, shape } of shapes) {
509
+ if (shape.known && !shape.hidden) continue;
510
+ const answer = unknownAnswer(kind, name, shape, collection);
511
+ throw ApiError.badRequest(answer.message, answer.code, answer.details);
512
+ }
513
+ if (vectorHidden && options.vectorSearch) {
514
+ const answer = unknownVectorProperty(options.vectorSearch.property, vectorPropertyNames(collection));
515
+ throw ApiError.badRequest(answer.message, answer.code, answer.details);
516
+ }
517
+ }
518
+ const relations = resolveCollectionRelations(collection);
519
+ for (const [key, node] of Object.entries(options.include ?? {})) {
520
+ if (hidden.has(key)) {
521
+ const answer = unknownIncludeRelation(key, collection.slug ?? collection.name, includableRelationNames(collection));
522
+ throw ApiError.badRequest(answer.message, answer.code, answer.details);
523
+ }
524
+ const relation = relations[key];
525
+ if (!relation) continue;
526
+ const filtered = [];
527
+ if (node.where) filtered.push(...Object.keys(node.where));
528
+ logicalColumns(node.logical, filtered);
529
+ assertNoHiddenNames({
530
+ filtered,
531
+ orderBy: node.orderBy?.map(([field]) => ({ field })),
532
+ fields: node.fields,
533
+ include: node.children
534
+ }, relation.target());
535
+ }
536
+ }
537
+ function nameShape(name, collection, hidden) {
538
+ if (hidden.has(name)) return {
539
+ hidden: true,
540
+ known: false
541
+ };
542
+ const columns = new Set(queryableFieldNames(collection));
543
+ const relations = resolveCollectionRelations(collection);
544
+ if (columns.has(name) || name in (collection.properties ?? {})) return {
545
+ hidden: false,
546
+ known: true
547
+ };
548
+ const arrow = name.indexOf("->");
549
+ if (arrow > 0) {
550
+ const head = name.slice(0, arrow);
551
+ return {
552
+ hidden: hidden.has(head),
553
+ known: columns.has(head)
554
+ };
555
+ }
556
+ const dot = name.indexOf(".");
557
+ if (dot > 0) {
558
+ const relationKey = name.slice(0, dot);
559
+ const fieldKey = name.slice(dot + 1);
560
+ if (hidden.has(relationKey)) return {
561
+ hidden: true,
562
+ known: false
563
+ };
564
+ const relation = findRelation(relations, relationKey);
565
+ if (!relation) return {
566
+ hidden: false,
567
+ known: false
568
+ };
569
+ const target = relation.target();
570
+ const targetHidden = apiHiddenFieldNames(target);
571
+ const path = {
572
+ relationKey,
573
+ fieldKey,
574
+ target
575
+ };
576
+ if (targetHidden.has(fieldKey)) return {
577
+ hidden: true,
578
+ known: false,
579
+ path
580
+ };
581
+ return {
582
+ hidden: false,
583
+ known: queryableFieldNames(target).includes(fieldKey) || fieldKey in (target.properties ?? {}),
584
+ path
585
+ };
586
+ }
587
+ return {
588
+ hidden: false,
589
+ known: false
590
+ };
591
+ }
592
+ function unknownAnswer(kind, name, shape, collection) {
593
+ const slug = collection.slug ?? collection.name;
594
+ if (kind === "fields") return unknownProjectionField(name, slug, queryableFieldNames(collection));
595
+ if (kind === "orderBy") return unknownOrderByField(name, slug, queryableFieldNames(collection));
596
+ if (shape.path) return unknownRelationTargetField({
597
+ field: name,
598
+ relationKey: shape.path.relationKey,
599
+ fieldKey: shape.path.fieldKey
600
+ }, {
601
+ slug: shape.path.target.slug,
602
+ validFields: queryableFieldNames(shape.path.target)
603
+ }, slug);
604
+ return unknownFilterField(name, slug, queryableFieldNames(collection));
118
605
  }
119
606
  /**
120
607
  * Every relation an `include` loads, judged like the read it is part of.
@@ -136,7 +623,7 @@ function assertIncludeFieldsReadable(tree, collection, viewer) {
136
623
  const relation = relations[key];
137
624
  if (!relation) continue;
138
625
  const target = relation.target();
139
- assertQueryFieldsReadable({
626
+ assertRestrictedFieldsUnused({
140
627
  where: node.where,
141
628
  logical: node.logical,
142
629
  orderBy: node.orderBy?.map(([field]) => ({ field })),
@@ -213,6 +700,7 @@ function parseLogicalGroup(type, raw) {
213
700
  try {
214
701
  parsed = deserializeLogicalCondition(`${type}(${inner})`);
215
702
  } catch (e) {
703
+ if (e instanceof UnknownFilterOperatorError) throw invalidParam(e.message, e.code, e.details);
216
704
  throw invalidParam(`Invalid \`${type}\` parameter: ${e instanceof Error ? e.message : String(e)}`, "INVALID_LOGICAL_GROUP");
217
705
  }
218
706
  return "type" in parsed ? parsed : void 0;
@@ -464,6 +952,7 @@ function parseQueryOptions(query, limits = {}, access) {
464
952
  const offsetVal = getLastValue(query.offset);
465
953
  if (offsetVal) options.offset = parseWindowParam(offsetVal, "offset", 0, "INVALID_OFFSET");
466
954
  const pageVal = getLastValue(query.page);
955
+ if (pageVal && offsetVal) throw invalidParam(PAGE_WITH_OFFSET_MESSAGE, "PAGE_WITH_OFFSET");
467
956
  if (pageVal) {
468
957
  const page = parseWindowParam(pageVal, "page", 1, "INVALID_PAGE");
469
958
  const limit = resolveListLimitParam(rawLimit, {
@@ -516,6 +1005,12 @@ function parseQueryOptions(query, limits = {}, access) {
516
1005
  if (text !== "true" && text !== "1" && text !== "false" && text !== "0") throw invalidParam(`Invalid \`distinct\` parameter: expected \`true\` or \`false\`, got ${JSON.stringify(String(distinctVal))}.`, "INVALID_DISTINCT");
517
1006
  options.distinct = text === "true" || text === "1";
518
1007
  }
1008
+ const countVal = getLastValue(query.count);
1009
+ if (countVal !== void 0 && countVal !== null && String(countVal) !== "") {
1010
+ const text = String(countVal).trim().toLowerCase();
1011
+ if (text !== "exact" && text !== "none") throw invalidParam(`Invalid \`count\` parameter: expected \`exact\` or \`none\`, got ${JSON.stringify(String(countVal))}.`, "INVALID_COUNT");
1012
+ options.count = text;
1013
+ }
519
1014
  const afterVal = getLastValue(query.after);
520
1015
  if (afterVal !== void 0 && afterVal !== null && String(afterVal).trim() !== "") {
521
1016
  let cursor;
@@ -578,6 +1073,6 @@ function parseQueryOptions(query, limits = {}, access) {
578
1073
  return options;
579
1074
  }
580
1075
  //#endregion
581
- export { resolveListLimitParam as a, requestViewer as c, parseQueryOptions as i, parseAggregateSelect as n, assertQueryFieldsReadable as o, parseGroupBy as r, assertReadableFields as s, orderByEntriesToTuples as t };
1076
+ export { resolveListLimitParam as a, assertReadableFields as c, decodeCursor as d, reconcileCursorOrder as f, parseQueryOptions as i, requestViewer as l, parseAggregateSelect as n, assertNoHiddenAggregateNames as o, parseGroupBy as r, assertQueryFieldsReadable as s, orderByEntriesToTuples as t, cursorToStartAfter as u };
582
1077
 
583
- //# sourceMappingURL=query-parser-BgiKJKvc.js.map
1078
+ //# sourceMappingURL=query-parser-4WkMoA_K.js.map