@rebasepro/server 0.23.0 → 0.24.1-canary.g56af4a4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/rebase-server.js +4 -2
- package/dist/GCSStorageController-DivE9bAx.js +402 -0
- package/dist/GCSStorageController-DivE9bAx.js.map +1 -0
- package/dist/S3StorageController-D_vKs4kP.js +480 -0
- package/dist/S3StorageController-D_vKs4kP.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +92 -1
- package/dist/api/errors.d.ts +9 -0
- package/dist/api/live-schema-routes.d.ts +38 -8
- package/dist/api/logs-routes.d.ts +39 -1
- package/dist/api/openapi-generator.d.ts +17 -0
- package/dist/api/rest/api-generator.d.ts +97 -11
- package/dist/api/rest/auth-collection-writes.d.ts +28 -21
- package/dist/api/rest/field-access-query.d.ts +28 -8
- package/dist/api/rest/field-ops.d.ts +19 -7
- package/dist/api/rest/query-body.d.ts +10 -0
- package/dist/api/rest/write-validation.d.ts +9 -7
- package/dist/api/schema-editor-routes.d.ts +10 -1
- package/dist/api/sql-export-routes.d.ts +8 -0
- package/dist/api/types.d.ts +28 -1
- package/dist/{ast-schema-editor-Mvr50v_S.js → ast-schema-editor-CSdL0iCk.js} +310 -12
- package/dist/ast-schema-editor-CSdL0iCk.js.map +1 -0
- package/dist/auth/access.d.ts +121 -0
- package/dist/auth/adapter-middleware.d.ts +2 -1
- package/dist/auth/address-ownership.d.ts +16 -1
- package/dist/auth/admin-roles-route.d.ts +4 -2
- package/dist/auth/admin-roles.d.ts +17 -20
- package/dist/auth/admin-user-ops.d.ts +24 -2
- package/dist/auth/admin-users-route.d.ts +2 -0
- package/dist/auth/api-keys/api-key-middleware.d.ts +56 -55
- package/dist/auth/api-keys/api-key-routes.d.ts +41 -11
- package/dist/auth/api-keys/api-key-store.d.ts +31 -8
- package/dist/auth/api-keys/api-key-types.d.ts +14 -16
- package/dist/auth/api-keys/http-operation.d.ts +19 -0
- package/dist/auth/api-keys/index.d.ts +11 -11
- package/dist/auth/api-keys/key-grant.d.ts +41 -0
- package/dist/auth/api-keys/legacy-permissions.d.ts +33 -0
- package/dist/auth/auth-hooks.d.ts +46 -7
- package/dist/auth/builtin-auth-adapter.d.ts +8 -0
- package/dist/auth/cookie-utils.d.ts +7 -0
- package/dist/auth/deliverable-address.d.ts +6 -0
- package/dist/auth/email-change-routes.d.ts +41 -0
- package/dist/auth/expired-token-sweep.d.ts +71 -0
- package/dist/auth/impersonation.d.ts +110 -0
- package/dist/auth/index.d.ts +4 -2
- package/dist/auth/interfaces.d.ts +120 -59
- package/dist/auth/jwt-crypto.d.ts +7 -0
- package/dist/auth/jwt-keys.d.ts +4 -0
- package/dist/auth/jwt.d.ts +54 -3
- package/dist/auth/magic-link-routes.d.ts +2 -6
- package/dist/auth/mfa-routes.d.ts +2 -9
- package/dist/auth/middleware.d.ts +17 -5
- package/dist/auth/otp-routes.d.ts +2 -6
- package/dist/auth/passwordless-signup.d.ts +27 -0
- package/dist/auth/platform-token.d.ts +122 -0
- package/dist/auth/rate-limiter.d.ts +54 -1
- package/dist/auth/registration-policy.d.ts +17 -0
- package/dist/auth/routes.d.ts +45 -0
- package/dist/auth/scope-routes.d.ts +22 -0
- package/dist/auth/session-routes.d.ts +11 -6
- package/dist/auth/token-revocation.d.ts +50 -1
- package/dist/auth/user-picking.d.ts +35 -0
- package/dist/auth/verify-credential.d.ts +28 -0
- package/dist/{auth-B-GIMpDG.js → auth-OPq_FeMs.js} +3115 -638
- package/dist/auth-OPq_FeMs.js.map +1 -0
- package/dist/auth_adapter-CIItCTc8.js +21 -0
- package/dist/auth_adapter-CIItCTc8.js.map +1 -0
- package/dist/backend-DTAOsLQc.js.map +1 -1
- package/dist/backup/backup-common.d.ts +24 -3
- package/dist/backup/backup-routes.d.ts +46 -5
- package/dist/backup/backup-schedule.d.ts +33 -0
- package/dist/backup/backup-storage.d.ts +16 -0
- package/dist/backup/index.d.ts +5 -1
- package/dist/backup-5d7XhLX3.js +581 -0
- package/dist/backup-5d7XhLX3.js.map +1 -0
- package/dist/boot/bundle.d.ts +19 -0
- package/dist/boot/driver.d.ts +3 -0
- package/dist/boot/env.d.ts +56 -4
- package/dist/boot/http-server.d.ts +22 -0
- package/dist/boot/options.d.ts +16 -1
- package/dist/boot/runtime-cors.d.ts +17 -0
- package/dist/boot/security-headers.d.ts +26 -0
- package/dist/boot/static-routing.d.ts +65 -0
- package/dist/collection_patch-BRu-BvDv.js +472 -0
- package/dist/collection_patch-BRu-BvDv.js.map +1 -0
- package/dist/{contract-routes-CbFjuBwa.js → contract-routes-02lASyJI.js} +5 -8
- package/dist/contract-routes-02lASyJI.js.map +1 -0
- package/dist/cron/cron-routes.d.ts +1 -1
- package/dist/cron/cron-scheduler.d.ts +94 -23
- package/dist/cron/cron-store.d.ts +31 -4
- package/dist/cron/define-cron.d.ts +6 -2
- package/dist/cron/index.d.ts +1 -1
- package/dist/{cron-loader-DfTj2Hbi.js → cron-loader-D7OmleAb.js} +11 -7
- package/dist/cron-loader-D7OmleAb.js.map +1 -0
- package/dist/{cron-routes-eE8nif_b.js → cron-routes-CBfiaqEL.js} +34 -8
- package/dist/cron-routes-CBfiaqEL.js.map +1 -0
- package/dist/{cron-scheduler-B0pLfAix.js → cron-scheduler-C2vbETMo.js} +349 -65
- package/dist/cron-scheduler-C2vbETMo.js.map +1 -0
- package/dist/cron-store-B2Ih0gCN.js +540 -0
- package/dist/cron-store-B2Ih0gCN.js.map +1 -0
- package/dist/{ddl-bootstrap-C6mo0Kmz.js → ddl-bootstrap-BaqMSa4Y.js} +2 -2
- package/dist/{ddl-bootstrap-C6mo0Kmz.js.map → ddl-bootstrap-BaqMSa4Y.js.map} +1 -1
- package/dist/email/index.d.ts +2 -2
- package/dist/email/smtp-email-service.d.ts +23 -0
- package/dist/email/templates.d.ts +22 -0
- package/dist/email/types.d.ts +44 -0
- package/dist/env.d.ts +1 -39
- package/dist/{errors-DWsX4yTd.js → errors-D6_y86c5.js} +102 -8
- package/dist/errors-D6_y86c5.js.map +1 -0
- package/dist/filter-dialect-REl-TjZr.js +1628 -0
- package/dist/filter-dialect-REl-TjZr.js.map +1 -0
- package/dist/{function-loader-xnbDAPfa.js → function-loader-BDKKiBmy.js} +5 -4
- package/dist/function-loader-BDKKiBmy.js.map +1 -0
- package/dist/{function-routes-Chet4-lB.js → function-routes-DWB7BBsi.js} +25 -13
- package/dist/function-routes-DWB7BBsi.js.map +1 -0
- package/dist/functions/context.d.ts +26 -6
- package/dist/functions/define-function.d.ts +7 -4
- package/dist/functions/guards.d.ts +22 -5
- package/dist/functions/index.d.ts +2 -2
- package/dist/functions/index.js +114 -37
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/request-timeout.d.ts +7 -5
- package/dist/history/history-routes.d.ts +9 -0
- package/dist/history/retention-sweep.d.ts +81 -0
- package/dist/{history-recorder-B4MpJfJK.js → history-recorder-DHenr3lm.js} +4 -4
- package/dist/{history-recorder-B4MpJfJK.js.map → history-recorder-DHenr3lm.js.map} +1 -1
- package/dist/{history-store-BhxWOuz9.js → history-store-DPofGVDp.js} +2 -2
- package/dist/{history-store-BhxWOuz9.js.map → history-store-DPofGVDp.js.map} +1 -1
- package/dist/index.d.ts +14 -7
- package/dist/index.es.js +23363 -17552
- package/dist/index.es.js.map +1 -1
- package/dist/init/health.d.ts +17 -2
- package/dist/init/shutdown.d.ts +20 -0
- package/dist/init.d.ts +70 -6
- package/dist/internal-tables-L2t39RdT.js +136 -0
- package/dist/internal-tables-L2t39RdT.js.map +1 -0
- package/dist/jobs/job-queue.d.ts +8 -0
- package/dist/jobs/job-store.d.ts +24 -0
- package/dist/jobs/types.d.ts +25 -4
- package/dist/{jobs-CazMYhyy.js → jobs-BpD4Q0qR.js} +235 -25
- package/dist/jobs-BpD4Q0qR.js.map +1 -0
- package/dist/{jwt-DnQHNFCl.js → jwt-Bqcu7cGt.js} +207 -77
- package/dist/jwt-Bqcu7cGt.js.map +1 -0
- package/dist/{keys-CogCQpxG.js → keys-GAVZqbqx.js} +3 -17
- package/dist/{keys-CogCQpxG.js.map → keys-GAVZqbqx.js.map} +1 -1
- package/dist/load-failure-UvzZgGXT.js +48 -0
- package/dist/load-failure-UvzZgGXT.js.map +1 -0
- package/dist/{logger-DO2PZc4i.js → logger-D-S-hO5e.js} +26 -3
- package/dist/logger-D-S-hO5e.js.map +1 -0
- package/dist/{logs-routes-Bj4TYYUl.js → logs-routes-DAdv37GI.js} +48 -8
- package/dist/logs-routes-DAdv37GI.js.map +1 -0
- package/dist/mcp/consent-page.d.ts +1 -1
- package/dist/mcp/mcp-routes.d.ts +7 -0
- package/dist/mcp/mcp-tools.d.ts +23 -9
- package/dist/mcp/oauth-metadata.d.ts +21 -16
- package/dist/mcp/oauth-routes.d.ts +7 -1
- package/dist/metrics/index.d.ts +18 -0
- package/dist/metrics/pool-wait.d.ts +50 -0
- package/dist/offline-8Ks3yp9n-CadBKm2D.js +3305 -0
- package/dist/offline-8Ks3yp9n-CadBKm2D.js.map +1 -0
- package/dist/offline-store-D9ytNNaa-BCr2vsFB.js +1903 -0
- package/dist/offline-store-D9ytNNaa-BCr2vsFB.js.map +1 -0
- package/dist/{openapi-generator-O_O24MAT.js → openapi-generator-BqoUh4Ij.js} +455 -80
- package/dist/openapi-generator-BqoUh4Ij.js.map +1 -0
- package/dist/policy-PdzX-Opd.js +121 -0
- package/dist/policy-PdzX-Opd.js.map +1 -0
- package/dist/project_manifest-R6y_KHms.js +59 -0
- package/dist/project_manifest-R6y_KHms.js.map +1 -0
- package/dist/{proxy-Czngl3p9.js → proxy-qRlqeUmO.js} +2 -2
- package/dist/{proxy-Czngl3p9.js.map → proxy-qRlqeUmO.js.map} +1 -1
- package/dist/{query-parser-DGRVFNM3.js → query-parser-DK9eMkb9.js} +497 -52
- package/dist/query-parser-DK9eMkb9.js.map +1 -0
- package/dist/{request-timeout-C_4C2BeR.js → request-timeout-Dnl2_OBc.js} +17 -10
- package/dist/request-timeout-Dnl2_OBc.js.map +1 -0
- package/dist/resumable-upload-threshold-CKv-Ry0l.js +24 -0
- package/dist/resumable-upload-threshold-CKv-Ry0l.js.map +1 -0
- package/dist/schema-edit/apply-schema-change.d.ts +73 -3
- package/dist/schema-edit/local-source.d.ts +29 -0
- package/dist/schema-edit/project-root.d.ts +3 -2
- package/dist/schema-edit/remote-source.d.ts +20 -5
- package/dist/schema-edit/source-editor.d.ts +28 -0
- package/dist/schema-editor-routes-BFf3i8jm.js +136 -0
- package/dist/schema-editor-routes-BFf3i8jm.js.map +1 -0
- package/dist/serve-spa.d.ts +58 -0
- package/dist/services/outbound-post.d.ts +50 -0
- package/dist/services/outbound-url-guard.d.ts +34 -5
- package/dist/services/routed-realtime-service.d.ts +16 -0
- package/dist/services/webhook-service.d.ts +66 -14
- package/dist/soft-delete-params-47yBGs3_.js +668 -0
- package/dist/soft-delete-params-47yBGs3_.js.map +1 -0
- package/dist/sql-export-routes-BNNuGGLP.js +240 -0
- package/dist/sql-export-routes-BNNuGGLP.js.map +1 -0
- package/dist/{src-vkcwKXbT.js → src-CatHFUym.js} +439 -20
- package/dist/src-CatHFUym.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +48 -2
- package/dist/storage/LocalStorageController.d.ts +69 -2
- package/dist/storage/S3StorageController.d.ts +39 -3
- package/dist/storage/image-transform.d.ts +5 -0
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/maintenance-routes.d.ts +23 -0
- package/dist/storage/maintenance.d.ts +126 -0
- package/dist/storage/property-limits.d.ts +41 -6
- package/dist/storage/rendition-cache.d.ts +9 -11
- package/dist/storage/request-keys.d.ts +15 -0
- package/dist/storage/request-principals.d.ts +30 -0
- package/dist/storage/requested-object.d.ts +74 -0
- package/dist/storage/routes.d.ts +52 -14
- package/dist/storage/source-variables.d.ts +10 -0
- package/dist/storage/streams.d.ts +42 -0
- package/dist/storage/tus-handler.d.ts +76 -7
- package/dist/storage/types.d.ts +101 -7
- package/dist/storage/upload-validation.d.ts +13 -0
- package/dist/storage-resumable-VO-cMpse-C_YX71pO.js +125 -0
- package/dist/storage-resumable-VO-cMpse-C_YX71pO.js.map +1 -0
- package/dist/strings.es-BeZWAodn.js +256 -0
- package/dist/strings.es-BeZWAodn.js.map +1 -0
- package/dist/upload-validation-Dd-fnsbA.js +172 -0
- package/dist/upload-validation-Dd-fnsbA.js.map +1 -0
- package/dist/utils/byte-size.d.ts +10 -0
- package/dist/utils/compression.d.ts +6 -0
- package/dist/utils/load-failure.d.ts +14 -0
- package/dist/utils/logger.d.ts +12 -0
- package/package.json +5 -5
- package/dist/GCSStorageController-CjrA4PMo.js +0 -242
- package/dist/GCSStorageController-CjrA4PMo.js.map +0 -1
- package/dist/S3StorageController-B6pKDNVj.js +0 -264
- package/dist/S3StorageController-B6pKDNVj.js.map +0 -1
- package/dist/admin-roles-vYdp_Pil.js +0 -36
- package/dist/admin-roles-vYdp_Pil.js.map +0 -1
- package/dist/admin_block-DxKLmdiv.js +0 -206
- package/dist/admin_block-DxKLmdiv.js.map +0 -1
- package/dist/ast-schema-editor-Mvr50v_S.js.map +0 -1
- package/dist/auth/api-keys/api-key-permission-guard.d.ts +0 -65
- package/dist/auth-B-GIMpDG.js.map +0 -1
- package/dist/backup-D7YR94N3.js +0 -253
- package/dist/backup-D7YR94N3.js.map +0 -1
- package/dist/contract-routes-CbFjuBwa.js.map +0 -1
- package/dist/cron-loader-DfTj2Hbi.js.map +0 -1
- package/dist/cron-routes-eE8nif_b.js.map +0 -1
- package/dist/cron-scheduler-B0pLfAix.js.map +0 -1
- package/dist/cron-store-TcoGz-xS.js +0 -305
- package/dist/cron-store-TcoGz-xS.js.map +0 -1
- package/dist/dynamic-import-X40pTZUQ.js +0 -23
- package/dist/dynamic-import-X40pTZUQ.js.map +0 -1
- package/dist/errors-DWsX4yTd.js.map +0 -1
- package/dist/function-loader-xnbDAPfa.js.map +0 -1
- package/dist/function-routes-Chet4-lB.js.map +0 -1
- package/dist/jobs-CazMYhyy.js.map +0 -1
- package/dist/jwt-DnQHNFCl.js.map +0 -1
- package/dist/logger-DO2PZc4i.js.map +0 -1
- package/dist/logs-routes-Bj4TYYUl.js.map +0 -1
- package/dist/openapi-generator-O_O24MAT.js.map +0 -1
- package/dist/query-parser-DGRVFNM3.js.map +0 -1
- package/dist/request-timeout-C_4C2BeR.js.map +0 -1
- package/dist/schema-editor-routes-C5-lh_jO.js +0 -87
- package/dist/schema-editor-routes-C5-lh_jO.js.map +0 -1
- package/dist/src-pmvW7BFx.js +0 -6688
- package/dist/src-pmvW7BFx.js.map +0 -1
- package/dist/src-vkcwKXbT.js.map +0 -1
- package/dist/types-BfKcm9do.js +0 -39
- package/dist/types-BfKcm9do.js.map +0 -1
package/dist/api/errors.d.ts
CHANGED
|
@@ -23,6 +23,15 @@ export declare function schemaDriftRemedy(): {
|
|
|
23
23
|
short: string;
|
|
24
24
|
lines: string[];
|
|
25
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* Did the database refuse a value as unreadable for its column's type —
|
|
28
|
+
* SQLSTATE class 22, anywhere down the cause chain?
|
|
29
|
+
*
|
|
30
|
+
* For a lookup by an id the caller supplied, that is an answer rather than a
|
|
31
|
+
* failure: `abc` against a UUID key names no row, exactly as a well-formed id
|
|
32
|
+
* nobody has does.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isDataException(error: unknown): boolean;
|
|
26
35
|
/**
|
|
27
36
|
* Standardized API error class.
|
|
28
37
|
* Throw this from any route handler — the errorHandler middleware
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* — `applySchemaChange` re-checks rather than trusting the plan it is handed.
|
|
30
30
|
*/
|
|
31
31
|
import { Hono } from "hono";
|
|
32
|
-
import { type CollectionConfig, type DatabaseAdmin, type SchemaCommitPaths } from "@rebasepro/types";
|
|
32
|
+
import { type CollectionPatch, type CollectionConfig, type DatabaseAdmin, type SchemaCommitPaths } from "@rebasepro/types";
|
|
33
33
|
import { HonoEnv } from "./types.js";
|
|
34
34
|
import { type SchemaEditRepository } from "../schema-edit/apply-schema-change.js";
|
|
35
35
|
import { type SchemaEditPolicy } from "../schema-edit/schema-edit-permissions.js";
|
|
@@ -87,8 +87,8 @@ export interface LiveSchemaRoutesConfig {
|
|
|
87
87
|
*
|
|
88
88
|
* Absent for a project that *is* the repository, which is what the defaults
|
|
89
89
|
* describe. Present for one in a subdirectory, where the defaults would
|
|
90
|
-
* write `backend/`
|
|
91
|
-
*
|
|
90
|
+
* write `backend/` beside `.git` and leave the project's real generated
|
|
91
|
+
* file untouched — committing a source change alongside a stale
|
|
92
92
|
* schema, which is the failure committing the whole change exists to avoid.
|
|
93
93
|
*/
|
|
94
94
|
commitPaths?: Partial<SchemaCommitPaths>;
|
|
@@ -97,8 +97,8 @@ export interface LiveSchemaRoutesConfig {
|
|
|
97
97
|
* environment.
|
|
98
98
|
*
|
|
99
99
|
* If it does, applying here is not the whole job: the DDL runs against
|
|
100
|
-
* *this* database and
|
|
101
|
-
*
|
|
100
|
+
* *this* database and the collection is committed, but no migration is
|
|
101
|
+
* written — a server cannot mint one, since that needs Atlas and a
|
|
102
102
|
* throwaway database. The next environment built by replaying migrations
|
|
103
103
|
* would not have this change, and nothing would have said so.
|
|
104
104
|
*/
|
|
@@ -111,11 +111,31 @@ export interface LiveSchemaRoutesConfig {
|
|
|
111
111
|
*/
|
|
112
112
|
policy?: SchemaEditPolicy;
|
|
113
113
|
}
|
|
114
|
-
/**
|
|
114
|
+
/**
|
|
115
|
+
* What the panel posts about one collection.
|
|
116
|
+
*
|
|
117
|
+
* For a collection that exists, a `patch`: what the person changed, as the
|
|
118
|
+
* difference between the JSON the editor loaded and the JSON it is saving (see
|
|
119
|
+
* `collection_patch.ts` in `@rebasepro/types`). The whole collection travelled
|
|
120
|
+
* here once, and the write rewrote every key it carried and deleted whatever
|
|
121
|
+
* JSON had dropped — handlers, shared properties, imported enums, comments.
|
|
122
|
+
*
|
|
123
|
+
* For a collection that does not exist yet, the whole `collection`. A caller
|
|
124
|
+
* that sends a whole collection for one that exists has it turned into the
|
|
125
|
+
* patch of what differs (see {@link asPatch}).
|
|
126
|
+
*/
|
|
115
127
|
export interface ProposedChange {
|
|
116
128
|
collectionId: string;
|
|
117
|
-
/** The whole collection
|
|
118
|
-
collection
|
|
129
|
+
/** The whole collection — only for one that does not exist yet. */
|
|
130
|
+
collection?: Record<string, unknown>;
|
|
131
|
+
/** What changed about an existing collection. */
|
|
132
|
+
patch?: CollectionPatch;
|
|
133
|
+
/**
|
|
134
|
+
* Delete the collection's source: its file, and its entry in `index.ts`.
|
|
135
|
+
* Planned like any change — the table and its rows stay, and say so — and
|
|
136
|
+
* committed source-only.
|
|
137
|
+
*/
|
|
138
|
+
remove?: true;
|
|
119
139
|
}
|
|
120
140
|
/**
|
|
121
141
|
* The git identity for the caller.
|
|
@@ -128,6 +148,16 @@ export declare function commitAuthor(user: unknown): {
|
|
|
128
148
|
name: string;
|
|
129
149
|
email: string;
|
|
130
150
|
} | undefined;
|
|
151
|
+
/**
|
|
152
|
+
* The change as a patch against the collection as it is.
|
|
153
|
+
*
|
|
154
|
+
* A whole collection posted for one that exists is the difference from the
|
|
155
|
+
* current one: a caller that sends JSON cannot then delete what JSON could not
|
|
156
|
+
* carry. A key it left out is removed only when its value is pure data — a
|
|
157
|
+
* key holding code (`callbacks`, a handler, a component) is not something a
|
|
158
|
+
* JSON caller could have meant to drop.
|
|
159
|
+
*/
|
|
160
|
+
export declare function asPatch(existing: CollectionConfig, change: ProposedChange): CollectionPatch;
|
|
131
161
|
/**
|
|
132
162
|
* The proposed collection set: everything as it is, with one collection
|
|
133
163
|
* replaced or added.
|
|
@@ -44,6 +44,15 @@ declare class LogRingBuffer {
|
|
|
44
44
|
getLatest(count?: number): LogEntry[];
|
|
45
45
|
}
|
|
46
46
|
export declare const logBuffer: LogRingBuffer;
|
|
47
|
+
/**
|
|
48
|
+
* Which process this log belongs to.
|
|
49
|
+
*
|
|
50
|
+
* The ring is this process's memory, and nothing gathers the rings of a
|
|
51
|
+
* deployment's other replicas or of its split runtime roles. So a reader is
|
|
52
|
+
* told whose log it is: `HOSTNAME` is the pod name on Kubernetes and the
|
|
53
|
+
* container id under Docker; the pid tells two local processes apart.
|
|
54
|
+
*/
|
|
55
|
+
export declare function logInstanceName(): string;
|
|
47
56
|
/** Add a log entry */
|
|
48
57
|
export declare function addLog(level: LogEntry["level"], source: LogEntry["source"], message: string, metadata?: Record<string, unknown>): void;
|
|
49
58
|
export interface LogMiddlewareOptions {
|
|
@@ -55,7 +64,25 @@ export interface LogMiddlewareOptions {
|
|
|
55
64
|
* server it is also the thing evicting real entries out of the ring.
|
|
56
65
|
*/
|
|
57
66
|
ignorePaths?: string[];
|
|
67
|
+
/**
|
|
68
|
+
* Where the API is mounted (`/api`). The segment after it names the
|
|
69
|
+
* subsystem a request is filed under — see {@link sourceForRequestPath}.
|
|
70
|
+
* Unset, the first segment of the path is read.
|
|
71
|
+
*/
|
|
72
|
+
basePath?: string;
|
|
58
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* The `source` of a request entry: the subsystem its path addresses.
|
|
76
|
+
*
|
|
77
|
+
* `/api/auth/*` and the MCP authorization server under `/api/oauth/*` are
|
|
78
|
+
* `auth`, `/api/storage/*` is `storage`, and everything else is `api`. The
|
|
79
|
+
* Logs Explorer's Source filter matches this field, so a sign-in filed as
|
|
80
|
+
* `api` was invisible under Auth. The realtime socket is not an HTTP request
|
|
81
|
+
* and is not recorded here.
|
|
82
|
+
*
|
|
83
|
+
* Exported for its test.
|
|
84
|
+
*/
|
|
85
|
+
export declare function sourceForRequestPath(path: string, basePath?: string): LogEntry["source"];
|
|
59
86
|
/** Hono middleware to log API requests */
|
|
60
87
|
export declare function logMiddleware(options?: LogMiddlewareOptions): MiddlewareHandler<HonoEnv>;
|
|
61
88
|
/**
|
|
@@ -79,6 +106,17 @@ export interface LogStreamTiming {
|
|
|
79
106
|
heartbeatMs?: number;
|
|
80
107
|
maxPending?: number;
|
|
81
108
|
}
|
|
82
|
-
|
|
109
|
+
/** What ends the stream from the server's side. */
|
|
110
|
+
export interface LogsRoutesOptions {
|
|
111
|
+
/**
|
|
112
|
+
* Aborted when the backend shuts down. Every open stream ends then, rather
|
|
113
|
+
* than when its client happens to leave: `server.close()` waits on every
|
|
114
|
+
* open connection, so one Studio → Logs tab held a SIGTERM for the whole
|
|
115
|
+
* force timeout — longer than Cloud Run's or `docker stop`'s grace period,
|
|
116
|
+
* which killed the process before the pool was closed.
|
|
117
|
+
*/
|
|
118
|
+
closeSignal?: AbortSignal;
|
|
119
|
+
}
|
|
120
|
+
export declare function createLogsRoutes(timing?: LogStreamTiming, options?: LogsRoutesOptions): Hono<HonoEnv>;
|
|
83
121
|
declare const _default: Hono<HonoEnv, import("hono/types").BlankSchema, "/">;
|
|
84
122
|
export default _default;
|
|
@@ -28,3 +28,20 @@ export interface OpenApiGeneratorOptions {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
export declare function generateOpenApiSpec(collections: CollectionConfig[], options?: OpenApiGeneratorOptions): Record<string, unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* Build the component schema for a collection (output / read shape).
|
|
33
|
+
*
|
|
34
|
+
* Every declared property except the ones {@link isDocumentedProperty} rules
|
|
35
|
+
* out, plus the foreign keys and relations {@link emitRelationProperties} adds.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildCollectionSchema(collection: CollectionConfig, registeredSchemas: ReadonlySet<string>): Record<string, unknown>;
|
|
38
|
+
/**
|
|
39
|
+
* Build an input schema (for POST/PUT) — excludes auto-generated fields.
|
|
40
|
+
*
|
|
41
|
+
* `excludeFromApi` columns are left out too, and the server now agrees: a write
|
|
42
|
+
* naming one is refused (`write-validation.ts`), which is what the flag's name
|
|
43
|
+
* and the generated SDK's own documentation always said. This comment used to
|
|
44
|
+
* record that such a write was "still accepted" — the document was right and
|
|
45
|
+
* the server was the thing that had not caught up.
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildCollectionInputSchema(collection: CollectionConfig): Record<string, unknown>;
|
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { AuthAdapter, DataDriver, CollectionConfig } from "@rebasepro/types";
|
|
3
|
-
import { HonoEnv } from "../types.js";
|
|
3
|
+
import { QueryOptions, HonoEnv } from "../types.js";
|
|
4
4
|
import { type ListLimitOptions } from "./query-parser.js";
|
|
5
|
+
/**
|
|
6
|
+
* The row a single-row write addresses, as its route found it.
|
|
7
|
+
*
|
|
8
|
+
* `/comments/7` and `/posts/1/comments/7` address the same kind of row — a row
|
|
9
|
+
* of `comments` — two ways, and a write to it is one operation whichever way
|
|
10
|
+
* it arrived: the same body checks, the same `If-Match`, the same
|
|
11
|
+
* `Idempotency-Key`, the same answer. It was two pipelines, and the nested one
|
|
12
|
+
* re-listed two of the root's checks by hand and skipped the rest.
|
|
13
|
+
*/
|
|
14
|
+
export interface RowAddress {
|
|
15
|
+
/** The collection the row belongs to: what a body is checked against and an `ETag` computed for. */
|
|
16
|
+
collection: CollectionConfig;
|
|
17
|
+
/**
|
|
18
|
+
* The path the driver reads and writes at: the collection's own, or a
|
|
19
|
+
* nested one, which the driver resolves and scopes to its parent row.
|
|
20
|
+
*/
|
|
21
|
+
path: string;
|
|
22
|
+
/**
|
|
23
|
+
* Handed to the driver beside `path`. Unset for a nested path, whose
|
|
24
|
+
* collection the driver resolves from the path itself.
|
|
25
|
+
*/
|
|
26
|
+
driverCollection?: CollectionConfig;
|
|
27
|
+
/** What a 404 calls the address. */
|
|
28
|
+
name: string;
|
|
29
|
+
}
|
|
5
30
|
/**
|
|
6
31
|
* Lightweight REST API generator that leverages existing Rebase DataDriver.
|
|
7
32
|
* Supports `include` query parameter for eager-loading relations via Drizzle.
|
|
@@ -113,13 +138,16 @@ export declare class RestApiGenerator {
|
|
|
113
138
|
*/
|
|
114
139
|
private createUnmatchedRoute;
|
|
115
140
|
/**
|
|
116
|
-
* Check
|
|
117
|
-
*
|
|
118
|
-
*
|
|
141
|
+
* Check a narrowed credential's data scope for a collection operation:
|
|
142
|
+
* `data:<operation>` on the collection, or unqualified.
|
|
143
|
+
*
|
|
144
|
+
* Throws 403 when it is missing. A person's own session holds the whole
|
|
145
|
+
* data plane — their rows are RLS's to decide — so this only ever refuses
|
|
146
|
+
* an API key or a token.
|
|
119
147
|
*/
|
|
120
148
|
private enforceApiKeyPermission;
|
|
121
149
|
/**
|
|
122
|
-
*
|
|
150
|
+
* Data-scope check for nested paths.
|
|
123
151
|
*
|
|
124
152
|
* The operation lands on the collection the path addresses — the target
|
|
125
153
|
* of its last relation — so that is the collection the key must hold the
|
|
@@ -237,7 +265,9 @@ export declare class RestApiGenerator {
|
|
|
237
265
|
*
|
|
238
266
|
* `respond` is applied to the *stored* result rather than the stored result
|
|
239
267
|
* being a response, so a replay honours the `Prefer` header of the request
|
|
240
|
-
* replaying it rather than the one that first answered.
|
|
268
|
+
* replaying it rather than the one that first answered. `replayed` says
|
|
269
|
+
* the result is the stored one, so a route can restore what it put in a
|
|
270
|
+
* header rather than the body (the `PATCH`'s `ETag`).
|
|
241
271
|
*/
|
|
242
272
|
private runIdempotent;
|
|
243
273
|
/**
|
|
@@ -260,7 +290,7 @@ export declare class RestApiGenerator {
|
|
|
260
290
|
* and a tag compared against a read that hides it compares against
|
|
261
291
|
* nothing.
|
|
262
292
|
*/
|
|
263
|
-
|
|
293
|
+
static rowForETag(driver: DataDriver, address: RowAddress, id: string, alreadyRead: Record<string, unknown> | undefined, withDeleted?: QueryOptions["withDeleted"]): Promise<Record<string, unknown> | undefined>;
|
|
264
294
|
/**
|
|
265
295
|
* `GET /:id`, at a row's own address or through a parent: the row as the
|
|
266
296
|
* query asked for it, and the `ETag` naming its version.
|
|
@@ -272,7 +302,10 @@ export declare class RestApiGenerator {
|
|
|
272
302
|
* relation was a 412. A narrowed read therefore takes its tag from
|
|
273
303
|
* {@link rowForETag}, the read the write routes make.
|
|
274
304
|
*/
|
|
275
|
-
|
|
305
|
+
static readRow(driver: DataDriver, address: RowAddress, id: string, queryOptions: QueryOptions): Promise<{
|
|
306
|
+
row: Record<string, unknown>;
|
|
307
|
+
etag: string | undefined;
|
|
308
|
+
} | undefined>;
|
|
276
309
|
/**
|
|
277
310
|
* Answer a bulk write, honouring `Prefer: return=minimal`.
|
|
278
311
|
*
|
|
@@ -299,11 +332,61 @@ export declare class RestApiGenerator {
|
|
|
299
332
|
* through a parent. Errors are not re-classified; see {@link createRow}.
|
|
300
333
|
*/
|
|
301
334
|
private updateRow;
|
|
335
|
+
/**
|
|
336
|
+
* The 404 and the `If-Match` of an update or a delete, as a precondition
|
|
337
|
+
* the driver runs inside the write — or, for a driver that cannot, and for
|
|
338
|
+
* the auth collection, the same judgement made here on a read first.
|
|
339
|
+
*
|
|
340
|
+
* Inside the write is the point. The route used to read the row in a
|
|
341
|
+
* transaction of its own, through the admin's view-model walk, before
|
|
342
|
+
* writing it: every declared relation of the row was loaded — all 200,000
|
|
343
|
+
* comments of a post, for a PATCH of its title — and thrown away, and the
|
|
344
|
+
* write's own read of the row then repeated the lookup. And two writes
|
|
345
|
+
* carrying the same tag could both pass: each compared before either had
|
|
346
|
+
* written. Judged on the write's own read, after the row is locked when a
|
|
347
|
+
* tag is sent, the second one reads what the first wrote and is a 412.
|
|
348
|
+
*
|
|
349
|
+
* The auth collection is still read here first, without relations: the
|
|
350
|
+
* adapter's steps run before the write (`prepareUserUpdates`, and a
|
|
351
|
+
* deletion's `beforeUserDelete` veto, a hook of the application's), and an
|
|
352
|
+
* unknown uid must be the 404 before them, as it always was.
|
|
353
|
+
*
|
|
354
|
+
* @returns The precondition to hand the driver, or `undefined` for a driver
|
|
355
|
+
* that does not run one (the row was judged here).
|
|
356
|
+
*/
|
|
357
|
+
private storedRowPrecondition;
|
|
302
358
|
/**
|
|
303
359
|
* Delete one row: `DELETE /<collection>/<id>`, and the same row reached
|
|
304
360
|
* through a parent.
|
|
305
361
|
*/
|
|
306
362
|
private deleteRow;
|
|
363
|
+
/**
|
|
364
|
+
* A listing — `GET /<collection>` and `POST /<collection>/query`, and the
|
|
365
|
+
* same two through a parent — from the parameters as the `GET` reads them
|
|
366
|
+
* off its URL.
|
|
367
|
+
*
|
|
368
|
+
* One method for both verbs is the whole contract of the POST route: the
|
|
369
|
+
* same parser, the same field-access refusals, the same page, count,
|
|
370
|
+
* cursor and include, the same errors. The caller has already checked the
|
|
371
|
+
* credential's scope for `collection`.
|
|
372
|
+
*
|
|
373
|
+
* @param collectionPath a nested listing's `parent/:id/child` path; unset
|
|
374
|
+
* for the collection's own.
|
|
375
|
+
*/
|
|
376
|
+
private listRows;
|
|
377
|
+
/**
|
|
378
|
+
* A collection's count, from the query parameters in `queryDict`: the
|
|
379
|
+
* URL's for `GET …/count`, a body's for `POST …/count`. One method for
|
|
380
|
+
* both, as {@link listRows} is for the listing — the same parser, the same
|
|
381
|
+
* field-access refusals, the same count. The caller has already checked
|
|
382
|
+
* the credential's scope for `collection`.
|
|
383
|
+
*
|
|
384
|
+
* @param collectionPath a nested count's `parent/:id/child` path; unset
|
|
385
|
+
* for the collection's own.
|
|
386
|
+
*/
|
|
387
|
+
private countRows;
|
|
388
|
+
/** A `POST …/query` or `POST …/count` body, as the dictionary {@link listRows} and {@link countRows} read. See `query-body.ts`. */
|
|
389
|
+
private queryDictFromRequest;
|
|
307
390
|
/**
|
|
308
391
|
* Create REST routes for a collection using existing Rebase patterns
|
|
309
392
|
*/
|
|
@@ -344,13 +427,16 @@ export declare class RestApiGenerator {
|
|
|
344
427
|
* @param collectionPath the path to read — a nested listing passes its own
|
|
345
428
|
* `parent/:id/child` path, which the driver resolves.
|
|
346
429
|
*/
|
|
347
|
-
|
|
430
|
+
static readPage(driver: DataDriver, collection: CollectionConfig, queryOptions: QueryOptions, searchString?: string, searchExplain?: boolean, collectionPath?: string): Promise<{
|
|
431
|
+
rows: Record<string, unknown>[];
|
|
432
|
+
meta: Record<string, unknown>;
|
|
433
|
+
}>;
|
|
348
434
|
/**
|
|
349
435
|
* Fetch raw collection data without Entity wrapper (fallback for non-Postgres)
|
|
350
436
|
*/
|
|
351
|
-
|
|
437
|
+
static fetchRawCollection(driver: DataDriver, collection: CollectionConfig, queryOptions: QueryOptions, searchString?: string, searchExplain?: boolean, collectionPath?: string, startAfter?: Record<string, unknown>): Promise<Record<string, unknown>[]>;
|
|
352
438
|
/**
|
|
353
439
|
* Count raw entities for a collection
|
|
354
440
|
*/
|
|
355
|
-
|
|
441
|
+
static countRawEntities(driver: DataDriver, collection: CollectionConfig, queryOptions: QueryOptions, searchString?: string, collectionPath?: string): Promise<number>;
|
|
356
442
|
}
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A write to the auth collection is user administration.
|
|
3
|
-
*
|
|
4
|
-
* The auth collection's rows are the users. `/admin/users` holds a change to
|
|
5
|
-
* one to the auth adapter's rules: a create goes through the adapter's user
|
|
6
|
-
* creation (the password hashed, the email normalized, the collection's
|
|
7
|
-
* `onCreateUser`, the invitation), an update may not leave the project without
|
|
8
|
-
* an administrator and stores an email the way sign-in looks it up, and a
|
|
9
|
-
* deletion runs `beforeUserDelete` (a veto), ends the sessions and runs
|
|
10
|
-
* `afterUserDelete`. The data doors that write the same rows — the REST routes
|
|
11
|
-
* and MCP's write tools — call these, so that no door writes a user by rules
|
|
12
|
-
* of its own. (The Postgres socket calls the adapter's methods directly.)
|
|
13
|
-
*
|
|
14
|
-
* Every function here leaves any other collection, and an adapter without the
|
|
15
|
-
* step, exactly as it was: the rows are then written as any table's.
|
|
16
|
-
*/
|
|
17
1
|
import type { AuthAdapter, CollectionConfig, UserCreationFinalizeResult, UserCreationPrepareResult } from "@rebasepro/types";
|
|
18
2
|
import type { FieldViewer } from "@rebasepro/common";
|
|
19
3
|
/** Whether `collection` is the auth collection — its rows are the users. */
|
|
@@ -26,6 +10,21 @@ export type UserCreatingAdapter = AuthAdapter & Required<Pick<AuthAdapter, "prep
|
|
|
26
10
|
* the door writes the row as any other.
|
|
27
11
|
*/
|
|
28
12
|
export declare function createsUsers(adapter: AuthAdapter | undefined, collection: CollectionConfig): adapter is UserCreatingAdapter;
|
|
13
|
+
/**
|
|
14
|
+
* Refuse an upsert on the auth collection.
|
|
15
|
+
*
|
|
16
|
+
* An upsert is a create or an update, decided inside the write by whether the
|
|
17
|
+
* row is already there, and on the auth collection the two follow different
|
|
18
|
+
* rules: a create sets the password and sends the invitation
|
|
19
|
+
* ({@link createUserThroughAuthCollection}), an update may not demote the last
|
|
20
|
+
* administrator and normalizes the email ({@link prepareAuthCollectionUpdates}).
|
|
21
|
+
* Neither can be applied before the branch is known, so an upsert took
|
|
22
|
+
* neither — `{ id: <the last admin>, roles: ["viewer"] }` demoted them.
|
|
23
|
+
*
|
|
24
|
+
* Only where the adapter administers users; with no such step there is no
|
|
25
|
+
* rule for an upsert to miss. `where` prefixes the message (`Operation 3`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function assertNoUserUpsert(adapter: AuthAdapter | undefined, collection: CollectionConfig, where?: string): void;
|
|
29
28
|
/**
|
|
30
29
|
* Check a create body for the auth collection against what the adapter says a
|
|
31
30
|
* create consumes (`describeUserCreationContract`).
|
|
@@ -43,8 +42,16 @@ export declare function assertUserCreationBodyValid(adapter: AuthAdapter | undef
|
|
|
43
42
|
* through the adapter's `prepareUserCreation` (the password hashed or
|
|
44
43
|
* generated, the email normalized, the collection's `onCreateUser`), in order.
|
|
45
44
|
* The `values` of each result are what the door writes.
|
|
45
|
+
*
|
|
46
|
+
* `caller` is who the door is writing for: an administrator may waive the
|
|
47
|
+
* password policy for an import (`skipPasswordPolicy: true`), anyone else may
|
|
48
|
+
* not.
|
|
49
|
+
*
|
|
50
|
+
* `nameRows` is set by a bulk door: a refusal of one body (a `WEAK_PASSWORD`,
|
|
51
|
+
* a hook's 4xx) is then prefixed `Row <index>: `, as the bulk's other row
|
|
52
|
+
* errors are, so an import can say which line of its file failed.
|
|
46
53
|
*/
|
|
47
|
-
export declare function prepareUserCreations(adapter: UserCreatingAdapter, collection: CollectionConfig, bodies: readonly Record<string, unknown>[]): Promise<UserCreationPrepareResult[]>;
|
|
54
|
+
export declare function prepareUserCreations(adapter: UserCreatingAdapter, collection: CollectionConfig, bodies: readonly Record<string, unknown>[], caller: FieldViewer, nameRows?: boolean): Promise<UserCreationPrepareResult[]>;
|
|
48
55
|
/**
|
|
49
56
|
* The second half, once the rows are written — and only then, so a write that
|
|
50
57
|
* fails invites nobody: the credentials' delivery through
|
|
@@ -55,14 +62,14 @@ export declare function prepareUserCreations(adapter: UserCreatingAdapter, colle
|
|
|
55
62
|
*/
|
|
56
63
|
export declare function completeUserCreations(adapter: UserCreatingAdapter, prepared: readonly UserCreationPrepareResult[], rows: readonly Record<string, unknown>[]): Promise<UserCreationFinalizeResult[]>;
|
|
57
64
|
/**
|
|
58
|
-
* Create one user through the auth collection
|
|
59
|
-
* then `save` (the door's own driver write),
|
|
60
|
-
* {@link completeUserCreations}.
|
|
65
|
+
* Create one user through the auth collection, for `caller`:
|
|
66
|
+
* {@link prepareUserCreations}, then `save` (the door's own driver write),
|
|
67
|
+
* then {@link completeUserCreations}.
|
|
61
68
|
*
|
|
62
69
|
* `undefined` when the create is not a user creation ({@link createsUsers}) —
|
|
63
70
|
* the door then writes the row as any other.
|
|
64
71
|
*/
|
|
65
|
-
export declare function createUserThroughAuthCollection(adapter: AuthAdapter | undefined, collection: CollectionConfig, body: Record<string, unknown>, save: (values: Record<string, unknown>) => Promise<Record<string, unknown>>): Promise<{
|
|
72
|
+
export declare function createUserThroughAuthCollection(adapter: AuthAdapter | undefined, collection: CollectionConfig, body: Record<string, unknown>, caller: FieldViewer, save: (values: Record<string, unknown>) => Promise<Record<string, unknown>>): Promise<{
|
|
66
73
|
row: Record<string, unknown>;
|
|
67
74
|
delivery: UserCreationFinalizeResult;
|
|
68
75
|
} | undefined>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CollectionConfig, IncludeSpec } from "@rebasepro/types";
|
|
2
|
-
import type
|
|
2
|
+
import { type LogicalCondition } from "@rebasepro/types";
|
|
3
3
|
import { type FieldViewer } from "@rebasepro/common";
|
|
4
4
|
/**
|
|
5
5
|
* A read may not name a field the caller cannot read.
|
|
@@ -10,13 +10,22 @@ import { type FieldViewer } from "@rebasepro/common";
|
|
|
10
10
|
* `?orderBy=salary` returns them in order of it. A column no response can carry
|
|
11
11
|
* has to be a column no query can interrogate, or the read rule is decoration.
|
|
12
12
|
*
|
|
13
|
-
* The refusal names
|
|
14
|
-
* published OpenAPI lists every property of every collection,
|
|
15
|
-
* a given caller cannot read, because the document is one
|
|
16
|
-
* off the app rather than off the authenticated data
|
|
17
|
-
* here would protect nothing and would answer a
|
|
18
|
-
* "unknown field", sending them to look for a
|
|
19
|
-
* there.
|
|
13
|
+
* The refusal names a field restricted by role. That is deliberate and it is
|
|
14
|
+
* not a leak: the published OpenAPI lists every property of every collection,
|
|
15
|
+
* including the ones a given caller cannot read, because the document is one
|
|
16
|
+
* document and is served off the app rather than off the authenticated data
|
|
17
|
+
* router. Hiding the name here would protect nothing and would answer a
|
|
18
|
+
* caller's genuine typo with "unknown field", sending them to look for a
|
|
19
|
+
* spelling mistake that is not there. Those field *names* are public; their
|
|
20
|
+
* *values* are not.
|
|
21
|
+
*
|
|
22
|
+
* An `excludeFromApi` field is the exception, because its name is *not*
|
|
23
|
+
* public: OpenAPI, `list_collections` and the generated SDK all leave it out.
|
|
24
|
+
* "`email` is not readable on `authors`" would confirm the one thing those hide,
|
|
25
|
+
* so a filter, sort, projection, include, aggregate or vector search naming one
|
|
26
|
+
* is answered as one naming a field that does not exist — built by the functions the driver answers a
|
|
27
|
+
* typo with (`@rebasepro/common`'s `unknown-field`), so the two answers are the
|
|
28
|
+
* same bytes. See {@link assertNoHiddenNames}.
|
|
20
29
|
*
|
|
21
30
|
* @module
|
|
22
31
|
*/
|
|
@@ -67,4 +76,15 @@ export declare function assertQueryFieldsReadable(options: {
|
|
|
67
76
|
property: string;
|
|
68
77
|
};
|
|
69
78
|
}, collection: CollectionConfig, viewer: FieldViewer | undefined): void;
|
|
79
|
+
/**
|
|
80
|
+
* The aggregate route's `select` and `groupBy`, which it parses itself: an
|
|
81
|
+
* `excludeFromApi` name there is answered exactly as the driver answers a
|
|
82
|
+
* column that does not exist. Walked in the driver's order — each aggregate's
|
|
83
|
+
* field, then each group key — and the first hidden *or* unknown name gets the
|
|
84
|
+
* answer, so a typo before a hidden name is still the one named.
|
|
85
|
+
*/
|
|
86
|
+
export declare function assertNoHiddenAggregateNames(aggregates: readonly {
|
|
87
|
+
fn: string;
|
|
88
|
+
field?: string;
|
|
89
|
+
}[], groupBy: readonly string[], collection: CollectionConfig): void;
|
|
70
90
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CollectionConfig, FieldOperation, FIELD_OPERATORS, isFieldOperation } from "@rebasepro/types";
|
|
1
|
+
import { CollectionConfig, FieldOperation, FIELD_OPERATORS, isFieldOperation, Properties, Property } from "@rebasepro/types";
|
|
2
2
|
/**
|
|
3
3
|
* Writes expressed as a change to the column's current value.
|
|
4
4
|
*
|
|
@@ -38,23 +38,32 @@ export interface ParsedFieldOp {
|
|
|
38
38
|
* this package.
|
|
39
39
|
*/
|
|
40
40
|
export declare const looksLikeFieldOp: typeof isFieldOperation;
|
|
41
|
-
/**
|
|
42
|
-
|
|
41
|
+
/**
|
|
42
|
+
* True when any value in the payload is (or is attempting to be) an operation.
|
|
43
|
+
* With `properties`, a `map` value is one only when it is `{ <operator>: … }`;
|
|
44
|
+
* see `isFieldOperation`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function hasFieldOps(values: Record<string, unknown> | undefined, properties?: Properties): boolean;
|
|
43
47
|
/**
|
|
44
48
|
* Parse one value as an operation, or report why it is not a legal one.
|
|
45
49
|
*
|
|
46
50
|
* Returns `undefined` for an ordinary value. Throws for a value that reads as
|
|
47
|
-
* an operation and is malformed, so a typo cannot be written as data.
|
|
51
|
+
* an operation and is malformed, so a typo cannot be written as data. Given
|
|
52
|
+
* the field's `property`, a `map` value with `$` keys is data unless it is a
|
|
53
|
+
* single real operator.
|
|
48
54
|
*/
|
|
49
|
-
export declare function parseFieldOp(key: string, value: unknown): ParsedFieldOp | undefined;
|
|
55
|
+
export declare function parseFieldOp(key: string, value: unknown, property?: Property): ParsedFieldOp | undefined;
|
|
50
56
|
/**
|
|
51
57
|
* Split a write payload into ordinary values and field operations.
|
|
52
58
|
*
|
|
53
59
|
* The two travel together in one body — `{ title: "x", views: { $inc: 1 } }` is
|
|
54
60
|
* a single `PATCH` — and are applied in one statement, so a caller never has to
|
|
55
61
|
* choose between setting a column and incrementing another.
|
|
62
|
+
*
|
|
63
|
+
* Pass the collection's `properties`: without them a `map` value carrying `$`
|
|
64
|
+
* keys reads as a (malformed) operation rather than as the document it is.
|
|
56
65
|
*/
|
|
57
|
-
export declare function splitFieldOps(values: Record<string, unknown
|
|
66
|
+
export declare function splitFieldOps(values: Record<string, unknown>, properties?: Properties): {
|
|
58
67
|
values: Record<string, unknown>;
|
|
59
68
|
fieldOps: Record<string, ParsedFieldOp>;
|
|
60
69
|
};
|
|
@@ -79,8 +88,11 @@ export declare function assertFieldOpsValid(values: Record<string, unknown>, col
|
|
|
79
88
|
* are meaningless on an insert — `views + 1` over a row that does not exist yet
|
|
80
89
|
* is just `1`, and accepting it would make `$inc` mean two different things
|
|
81
90
|
* depending on a race the caller cannot observe.
|
|
91
|
+
*
|
|
92
|
+
* `collection` decides how a `map` value is read (see {@link splitFieldOps});
|
|
93
|
+
* every request boundary has it and passes it.
|
|
82
94
|
*/
|
|
83
|
-
export declare function assertNoFieldOpsOnCreate(values: Record<string, unknown>, context: string): void;
|
|
95
|
+
export declare function assertNoFieldOpsOnCreate(values: Record<string, unknown>, context: string, collection?: CollectionConfig): void;
|
|
84
96
|
/** Re-exported so callers type against one definition of the wire shape. */
|
|
85
97
|
export type { FieldOperation };
|
|
86
98
|
export { FIELD_OPERATORS };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The body of a `POST /<collection>/query`, as the dictionary the `GET`
|
|
3
|
+
* listing reads off its URL.
|
|
4
|
+
*
|
|
5
|
+
* @param body the parsed JSON body — already known to be an object
|
|
6
|
+
* @param urlParams the names of any parameters on the request's own URL, which
|
|
7
|
+
* are refused: two places to put one parameter is two answers to "which one
|
|
8
|
+
* applied", and the listing would silently answer one of them
|
|
9
|
+
*/
|
|
10
|
+
export declare function queryDictFromBody(body: Record<string, unknown>, urlParams?: readonly string[]): Record<string, string[]>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CollectionConfig } from "@rebasepro/types";
|
|
1
|
+
import { CollectionConfig, type IncludeSpec } from "@rebasepro/types";
|
|
2
2
|
import { type FieldViewer } from "@rebasepro/common";
|
|
3
3
|
/**
|
|
4
4
|
* Refuse a write naming a field this caller may not set.
|
|
@@ -91,11 +91,13 @@ export interface WriteViolation {
|
|
|
91
91
|
* from that spec believed the server was checking. Enum membership, `email` and
|
|
92
92
|
* `url` were published the same way and enforced the same amount.
|
|
93
93
|
*
|
|
94
|
-
* `required` is checked only when `options.status` says this
|
|
95
|
-
* row. A `PATCH` legitimately omits keys — this function sees
|
|
96
|
-
* values, not the row they land on — so demanding them there
|
|
97
|
-
* partial update. Callers that do not pass a status get the
|
|
98
|
-
* leave
|
|
94
|
+
* A missing `required` key is checked only when `options.status` says this
|
|
95
|
+
* write creates a row. A `PATCH` legitimately omits keys — this function sees
|
|
96
|
+
* one request's values, not the row they land on — so demanding them there
|
|
97
|
+
* would refuse every partial update. Callers that do not pass a status get the
|
|
98
|
+
* old behaviour and leave a missing key to the database's NOT NULL. A required
|
|
99
|
+
* string sent as `""` is refused on every write, whatever the status: NOT NULL
|
|
100
|
+
* would store it.
|
|
99
101
|
*
|
|
100
102
|
* A value of the wrong *type* is not this function's business — that reaches
|
|
101
103
|
* Postgres, which types every column and gives a 400 through the driver's
|
|
@@ -151,7 +153,7 @@ export declare function assertWriteValuesValid(values: Record<string, unknown>,
|
|
|
151
153
|
* assuming one.
|
|
152
154
|
*/
|
|
153
155
|
export declare function projectResponseFields<T extends Record<string, unknown>>(rows: T[], fields: readonly string[] | undefined, collection: CollectionConfig, options?: {
|
|
154
|
-
include?:
|
|
156
|
+
include?: IncludeSpec;
|
|
155
157
|
}): T[];
|
|
156
158
|
/**
|
|
157
159
|
* Both write checks, as one call, for a transport that is not the REST router.
|
|
@@ -1,3 +1,12 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
+
import { SchemaSourceEditor } from "../schema-edit/source-editor.js";
|
|
2
3
|
import { HonoEnv } from "./types.js";
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The editor for `collectionsDir`: one per process, shared by these routes
|
|
6
|
+
* and live schema editing's `/apply`. See {@link SchemaSourceEditor}.
|
|
7
|
+
*
|
|
8
|
+
* Here rather than beside the class because building it loads `ts-morph`, an
|
|
9
|
+
* optional peer dependency this module is imported to probe for.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createSchemaSourceEditor(collectionsDir: string): SchemaSourceEditor;
|
|
12
|
+
export declare function createSchemaEditorRoutes(source: SchemaSourceEditor): Hono<HonoEnv>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { type DatabaseAdmin } from "@rebasepro/types";
|
|
3
|
+
import type { HonoEnv } from "./types.js";
|
|
4
|
+
export interface SqlExportRoutesOptions {
|
|
5
|
+
/** The default data source's admin capabilities, read per request. */
|
|
6
|
+
getAdmin: () => DatabaseAdmin | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare function createSqlExportRoutes(options: SqlExportRoutesOptions): Hono<HonoEnv>;
|
package/dist/api/types.d.ts
CHANGED
|
@@ -31,10 +31,32 @@ export type HonoEnv = {
|
|
|
31
31
|
*/
|
|
32
32
|
recipientEmail?: string;
|
|
33
33
|
driver?: DataDriver;
|
|
34
|
-
/** Set when the request is authenticated
|
|
34
|
+
/** Set when the request is authenticated with an API key (`rk_`). */
|
|
35
35
|
apiKey?: ApiKeyMasked;
|
|
36
|
+
/**
|
|
37
|
+
* What the caller may do, when its credential is narrower than its
|
|
38
|
+
* person — an API key or an MCP token. Absent for a person's own
|
|
39
|
+
* session, who holds what their roles hold. Read it through
|
|
40
|
+
* `callerScopes` / `hasScope`, which fill in that second case.
|
|
41
|
+
*/
|
|
42
|
+
scopes?: string[];
|
|
43
|
+
/**
|
|
44
|
+
* Who asked, when an administrator's request runs as another user
|
|
45
|
+
* (`x-rebase-impersonate`). `user` and `driver` are then the
|
|
46
|
+
* impersonated user's; this is the only place the administrator
|
|
47
|
+
* remains. See `auth/impersonation.ts`.
|
|
48
|
+
*/
|
|
49
|
+
impersonator?: {
|
|
50
|
+
uid: string;
|
|
51
|
+
};
|
|
36
52
|
/** Unique request correlation ID (generated or propagated from X-Request-ID header). */
|
|
37
53
|
requestId?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Aborted when a custom function's request outlasts its timeout and is
|
|
56
|
+
* answered 504. Read it with `getSignal(c)` from
|
|
57
|
+
* `@rebasepro/server/functions`. See `functions/request-timeout.ts`.
|
|
58
|
+
*/
|
|
59
|
+
functionSignal?: AbortSignal;
|
|
38
60
|
/**
|
|
39
61
|
* The collection this request is about, when it is about one.
|
|
40
62
|
*
|
|
@@ -141,4 +163,9 @@ export interface QueryOptions {
|
|
|
141
163
|
* the point of the feature.
|
|
142
164
|
*/
|
|
143
165
|
withDeleted?: boolean | "only";
|
|
166
|
+
/**
|
|
167
|
+
* From `?count=exact|none` — whether a listing counts `meta.total`. See
|
|
168
|
+
* `FindParams.count`. Unset is `"exact"`.
|
|
169
|
+
*/
|
|
170
|
+
count?: "exact" | "none";
|
|
144
171
|
}
|