@rebasepro/server 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/rebase-server.js +4 -2
- package/dist/{GCSStorageController-CLIJXwGS.js → GCSStorageController-BSiP1c-f.js} +57 -29
- package/dist/GCSStorageController-BSiP1c-f.js.map +1 -0
- package/dist/{S3StorageController-Dcuf8lMA.js → S3StorageController-CAwFRgjV.js} +19 -7
- package/dist/S3StorageController-CAwFRgjV.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +127 -1
- package/dist/api/errors.d.ts +9 -0
- package/dist/api/live-schema-routes.d.ts +52 -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 +158 -42
- package/dist/api/rest/auth-collection-writes.d.ts +85 -0
- package/dist/api/rest/field-access-query.d.ts +6 -2
- package/dist/api/rest/idempotency.d.ts +7 -1
- package/dist/api/rest/nested-write-access.d.ts +46 -0
- package/dist/api/rest/write-validation.d.ts +34 -2
- package/dist/api/types.d.ts +17 -1
- package/dist/{ast-schema-editor-CslO8Oje.js → ast-schema-editor-CWqS_sLJ.js} +411 -13
- package/dist/ast-schema-editor-CWqS_sLJ.js.map +1 -0
- package/dist/auth/access.d.ts +105 -0
- package/dist/auth/adapter-middleware.d.ts +2 -1
- package/dist/auth/address-ownership.d.ts +68 -0
- 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 +35 -2
- package/dist/auth/admin-users-route.d.ts +1 -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 +50 -7
- package/dist/auth/builtin-auth-adapter.d.ts +8 -0
- package/dist/auth/captcha.d.ts +5 -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 +67 -0
- package/dist/auth/impersonation.d.ts +110 -0
- package/dist/auth/index.d.ts +4 -2
- package/dist/auth/interfaces.d.ts +146 -65
- package/dist/auth/jwt.d.ts +66 -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/oauth-signin-policy.d.ts +25 -8
- 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 +72 -1
- package/dist/auth/routes.d.ts +45 -0
- package/dist/auth/scope-routes.d.ts +22 -0
- package/dist/auth/session-routes.d.ts +18 -6
- package/dist/auth/token-revocation.d.ts +53 -1
- package/dist/auth/verify-credential.d.ts +28 -0
- package/dist/{auth-CCDpk2rn.js → auth-DMLngxn_.js} +2712 -711
- package/dist/auth-DMLngxn_.js.map +1 -0
- package/dist/backend-DTAOsLQc.js +30 -0
- package/dist/backend-DTAOsLQc.js.map +1 -0
- package/dist/backup/backup-common.d.ts +29 -0
- package/dist/backup/backup-routes.d.ts +24 -4
- package/dist/backup/backup-schedule.d.ts +33 -0
- package/dist/backup/backup-storage.d.ts +14 -0
- package/dist/backup/index.d.ts +2 -0
- package/dist/backup-CN0s50D2.js +444 -0
- package/dist/backup-CN0s50D2.js.map +1 -0
- package/dist/boot/bundle.d.ts +19 -0
- package/dist/boot/driver.d.ts +10 -0
- package/dist/boot/env.d.ts +51 -6
- package/dist/boot/fetch-bundle.d.ts +18 -1
- package/dist/boot/rls-audit-option.d.ts +26 -0
- package/dist/boot/security-headers.d.ts +26 -0
- package/dist/boot/sources.d.ts +1 -0
- package/dist/boot/static-routing.d.ts +56 -0
- package/dist/collection_patch-BRu-BvDv.js +472 -0
- package/dist/collection_patch-BRu-BvDv.js.map +1 -0
- package/dist/{contract-routes-eLxV0le1.js → contract-routes-fz8i4pxs.js} +17 -4
- package/dist/contract-routes-fz8i4pxs.js.map +1 -0
- package/dist/cron/cron-routes.d.ts +7 -2
- package/dist/cron/cron-scheduler.d.ts +146 -21
- package/dist/cron/cron-store.d.ts +76 -8
- package/dist/cron/index.d.ts +1 -1
- package/dist/{cron-loader-CQjvjpEw.js → cron-loader-CwaANlOG.js} +4 -4
- package/dist/cron-loader-CwaANlOG.js.map +1 -0
- package/dist/cron-routes-Bc-SB0Se.js +96 -0
- package/dist/cron-routes-Bc-SB0Se.js.map +1 -0
- package/dist/{cron-scheduler-COPQxlEq.js → cron-scheduler-CYQgco86.js} +427 -83
- package/dist/cron-scheduler-CYQgco86.js.map +1 -0
- package/dist/{cron-store-BYGZFNWk.js → cron-store-D2Q9-Aco.js} +139 -23
- package/dist/cron-store-D2Q9-Aco.js.map +1 -0
- package/dist/{ddl-bootstrap-CfNvxMuK.js → ddl-bootstrap-BaqMSa4Y.js} +3 -26
- package/dist/ddl-bootstrap-BaqMSa4Y.js.map +1 -0
- package/dist/email/index.d.ts +2 -2
- package/dist/email/link-base.d.ts +5 -4
- package/dist/email/smtp-email-service.d.ts +13 -1
- package/dist/email/templates.d.ts +31 -0
- package/dist/email/types.d.ts +29 -2
- package/dist/env.d.ts +25 -7
- package/dist/{errors-DWsX4yTd.js → errors-D6_y86c5.js} +102 -8
- package/dist/errors-D6_y86c5.js.map +1 -0
- package/dist/{function-loader-xnbDAPfa.js → function-loader-D7o5Epjj.js} +2 -2
- package/dist/{function-loader-xnbDAPfa.js.map → function-loader-D7o5Epjj.js.map} +1 -1
- package/dist/{function-routes-Chet4-lB.js → function-routes-CaNG4waN.js} +24 -12
- package/dist/function-routes-CaNG4waN.js.map +1 -0
- package/dist/functions/context.d.ts +17 -6
- package/dist/functions/guards.d.ts +22 -5
- package/dist/functions/index.d.ts +2 -2
- package/dist/functions/index.js +90 -36
- package/dist/functions/index.js.map +1 -1
- package/dist/{history-recorder-BQmB0P_j.js → history-recorder-Nr8zLvoU.js} +9 -7
- package/dist/history-recorder-Nr8zLvoU.js.map +1 -0
- package/dist/{history-store-CetkrBBD.js → history-store-rcAm_xFR.js} +2 -2
- package/dist/{history-store-CetkrBBD.js.map → history-store-rcAm_xFR.js.map} +1 -1
- package/dist/index.d.ts +14 -4
- package/dist/index.es.js +5733 -1551
- package/dist/index.es.js.map +1 -1
- package/dist/init/docs.d.ts +5 -2
- package/dist/init/health.d.ts +17 -2
- package/dist/init/shutdown.d.ts +18 -3
- package/dist/init.d.ts +54 -0
- package/dist/jobs/index.d.ts +2 -2
- package/dist/jobs/job-queue.d.ts +23 -2
- package/dist/jobs/job-store.d.ts +37 -5
- package/dist/jobs/types.d.ts +8 -6
- package/dist/{jobs-Bjr8DZAi.js → jobs-DqYNfquG.js} +306 -167
- package/dist/jobs-DqYNfquG.js.map +1 -0
- package/dist/{jwt-C4OW-DNq.js → jwt-R6bSPMjk.js} +114 -38
- package/dist/{jwt-C4OW-DNq.js.map → jwt-R6bSPMjk.js.map} +1 -1
- package/dist/{keys-Qfc4XieN.js → keys-GAVZqbqx.js} +18 -17
- package/dist/{keys-Qfc4XieN.js.map → keys-GAVZqbqx.js.map} +1 -1
- 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-3EEzPjhl.js → logs-routes-DAdv37GI.js} +54 -11
- 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 +45 -2
- package/dist/mcp/mcp-tools.d.ts +22 -10
- package/dist/mcp/oauth-metadata.d.ts +21 -16
- package/dist/mcp/oauth-routes.d.ts +34 -1
- package/dist/mcp/oauth-store.d.ts +29 -13
- package/dist/metrics/history-recorder.d.ts +1 -1
- package/dist/{openapi-generator-D8uFz-LW.js → openapi-generator-DAq_XVDu.js} +135 -22
- package/dist/openapi-generator-DAq_XVDu.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-BleZmY18.js → query-parser-BgiKJKvc.js} +41 -82
- package/dist/query-parser-BgiKJKvc.js.map +1 -0
- package/dist/{request-timeout-C_4C2BeR.js → request-timeout-DgH7j8qO.js} +3 -3
- package/dist/{request-timeout-C_4C2BeR.js.map → request-timeout-DgH7j8qO.js.map} +1 -1
- package/dist/rls-audit/index.d.ts +4 -0
- package/dist/schema-edit/apply-schema-change.d.ts +63 -3
- package/dist/schema-edit/project-root.d.ts +3 -2
- package/dist/schema-edit/remote-source.d.ts +9 -4
- package/dist/{schema-editor-routes-DdLihzp0.js → schema-editor-routes-oIyuWl3L.js} +12 -7
- package/dist/schema-editor-routes-oIyuWl3L.js.map +1 -0
- package/dist/serve-spa.d.ts +58 -0
- package/dist/services/routed-realtime-service.d.ts +11 -0
- package/dist/soft-delete-params-BWPilMPF.js +59 -0
- package/dist/soft-delete-params-BWPilMPF.js.map +1 -0
- package/dist/{src-Br6ARbs6.js → src-CatHFUym.js} +439 -45
- package/dist/src-CatHFUym.js.map +1 -0
- package/dist/{src-1vL-I1Po.js → src-I3aG1PcY.js} +371 -81
- package/dist/src-I3aG1PcY.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +13 -1
- package/dist/storage/LocalStorageController.d.ts +2 -0
- package/dist/storage/S3StorageController.d.ts +2 -0
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/keys.d.ts +12 -0
- package/dist/storage/property-limits.d.ts +41 -6
- package/dist/storage/rendition-cache.d.ts +11 -1
- package/dist/storage/request-keys.d.ts +82 -0
- package/dist/storage/requested-object.d.ts +74 -0
- package/dist/storage/routes.d.ts +36 -18
- package/dist/storage/tus-handler.d.ts +30 -5
- package/dist/storage/types.d.ts +36 -1
- package/dist/types-BfKcm9do.js.map +1 -1
- package/dist/utils/logger.d.ts +12 -0
- package/package.json +9 -9
- package/dist/GCSStorageController-CLIJXwGS.js.map +0 -1
- package/dist/S3StorageController-Dcuf8lMA.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-CslO8Oje.js.map +0 -1
- package/dist/auth/api-keys/api-key-permission-guard.d.ts +0 -65
- package/dist/auth-CCDpk2rn.js.map +0 -1
- package/dist/backup-DzI9jLwc.js +0 -192
- package/dist/backup-DzI9jLwc.js.map +0 -1
- package/dist/contract-routes-eLxV0le1.js.map +0 -1
- package/dist/cron-loader-CQjvjpEw.js.map +0 -1
- package/dist/cron-routes-B7CRGfiq.js +0 -72
- package/dist/cron-routes-B7CRGfiq.js.map +0 -1
- package/dist/cron-scheduler-COPQxlEq.js.map +0 -1
- package/dist/cron-store-BYGZFNWk.js.map +0 -1
- package/dist/ddl-bootstrap-CfNvxMuK.js.map +0 -1
- package/dist/errors-DWsX4yTd.js.map +0 -1
- package/dist/function-routes-Chet4-lB.js.map +0 -1
- package/dist/history-recorder-BQmB0P_j.js.map +0 -1
- package/dist/jobs-Bjr8DZAi.js.map +0 -1
- package/dist/logger-DO2PZc4i.js.map +0 -1
- package/dist/logs-routes-3EEzPjhl.js.map +0 -1
- package/dist/openapi-generator-D8uFz-LW.js.map +0 -1
- package/dist/query-parser-BleZmY18.js.map +0 -1
- package/dist/schema-editor-routes-DdLihzp0.js.map +0 -1
- package/dist/src-1vL-I1Po.js.map +0 -1
- package/dist/src-Br6ARbs6.js.map +0 -1
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { createRequire as __rebaseCreateRequire } from "module";
|
|
2
|
+
import __rebaseProcess from "process";
|
|
3
|
+
globalThis.process ??= __rebaseProcess;
|
|
4
|
+
__rebaseCreateRequire(import.meta.url);
|
|
5
|
+
//#region ../types/src/types/admin_block.ts
|
|
6
|
+
/**
|
|
7
|
+
* The keys of a collection's admin block, as data.
|
|
8
|
+
*
|
|
9
|
+
* There is no *type* for the block in this package any more, and that is the point:
|
|
10
|
+
* `admin` is not declared on `BaseCollectionConfig` or on any property here, so a
|
|
11
|
+
* BaaS install cannot even write one. `@rebasepro/cms-types` adds the field back by
|
|
12
|
+
* declaration merging, which is why installing it is what makes the admin surface
|
|
13
|
+
* appear.
|
|
14
|
+
*
|
|
15
|
+
* The *list* still has to live here, because three runtime consumers need it and two
|
|
16
|
+
* of them are core — see below.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Every key that belongs inside a collection's `admin` block, as data.
|
|
20
|
+
*
|
|
21
|
+
* The type that describes these fields is `AdminCollectionOptions` in
|
|
22
|
+
* `@rebasepro/cms-types`, and it is erased at build time — but three runtime
|
|
23
|
+
* consumers need the list, and two of them are core:
|
|
24
|
+
*
|
|
25
|
+
* - `serializeCollections`, to drop the block from the contract
|
|
26
|
+
* - the ts-morph schema editor in `@rebasepro/server`, which rewrites collection
|
|
27
|
+
* files on disk from the admin panel and has to know where each key goes. A key
|
|
28
|
+
* missing from this list gets written to the *top level* of the file, where the
|
|
29
|
+
* backend ignores it and the panel never finds it again.
|
|
30
|
+
* - the `collections-admin-block` codemod
|
|
31
|
+
*
|
|
32
|
+
* `@rebasepro/cms-types` re-exports this and asserts it names only real option
|
|
33
|
+
* keys; the count is pinned by a test there.
|
|
34
|
+
*
|
|
35
|
+
* @group Models
|
|
36
|
+
*/
|
|
37
|
+
var ADMIN_COLLECTION_KEYS = [
|
|
38
|
+
"Actions",
|
|
39
|
+
"additionalFields",
|
|
40
|
+
"alwaysApplyDefaultValues",
|
|
41
|
+
"browserCallbacks",
|
|
42
|
+
"components",
|
|
43
|
+
"customViews",
|
|
44
|
+
"defaultEntityAction",
|
|
45
|
+
"defaultFilter",
|
|
46
|
+
"defaultSelectedView",
|
|
47
|
+
"defaultSize",
|
|
48
|
+
"defaultViewMode",
|
|
49
|
+
"disableDefaultActions",
|
|
50
|
+
"display",
|
|
51
|
+
"enabledViews",
|
|
52
|
+
"entityActions",
|
|
53
|
+
"entityViews",
|
|
54
|
+
"exportable",
|
|
55
|
+
"filterPresets",
|
|
56
|
+
"fixedFilter",
|
|
57
|
+
"form",
|
|
58
|
+
"formAutoSave",
|
|
59
|
+
"formView",
|
|
60
|
+
"group",
|
|
61
|
+
"hideFromEntityViews",
|
|
62
|
+
"hideFromNavigation",
|
|
63
|
+
"hideIdFromCollection",
|
|
64
|
+
"hideIdFromForm",
|
|
65
|
+
"icon",
|
|
66
|
+
"includeJsonView",
|
|
67
|
+
"inlineEditing",
|
|
68
|
+
"kanban",
|
|
69
|
+
"listProperties",
|
|
70
|
+
"localChangesBackup",
|
|
71
|
+
"openEntityMode",
|
|
72
|
+
"orderProperty",
|
|
73
|
+
"pagination",
|
|
74
|
+
"previewProperties",
|
|
75
|
+
"propertiesOrder",
|
|
76
|
+
"selectionController",
|
|
77
|
+
"selectionEnabled",
|
|
78
|
+
"sideDialogWidth",
|
|
79
|
+
"sort"
|
|
80
|
+
];
|
|
81
|
+
/**
|
|
82
|
+
* Every key that belongs inside a *property's* `admin` block, as data.
|
|
83
|
+
*
|
|
84
|
+
* The union of `AdminPropertyOptions` and its per-type extensions
|
|
85
|
+
* (`AdminStringOptions`, `AdminArrayOptions`, …) in `@rebasepro/cms-types`.
|
|
86
|
+
* It lives here for the same reason {@link ADMIN_COLLECTION_KEYS} does: the
|
|
87
|
+
* runtime consumers are core packages that the BaaS guard forbids from
|
|
88
|
+
* importing `@rebasepro/cms-types`. Here it is the boot-time collection
|
|
89
|
+
* validator in `@rebasepro/server`, which has to tell "you left `readOnly` at
|
|
90
|
+
* the top of the property, where nothing reads it" apart from "you invented a
|
|
91
|
+
* key we have never heard of".
|
|
92
|
+
*
|
|
93
|
+
* `@rebasepro/cms-types` re-exports this and asserts it names only real
|
|
94
|
+
* option keys.
|
|
95
|
+
*
|
|
96
|
+
* @group Models
|
|
97
|
+
*/
|
|
98
|
+
var ADMIN_PROPERTY_KEYS = [
|
|
99
|
+
"canAddElements",
|
|
100
|
+
"clearable",
|
|
101
|
+
"columnWidth",
|
|
102
|
+
"customProps",
|
|
103
|
+
"disabled",
|
|
104
|
+
"expanded",
|
|
105
|
+
"Field",
|
|
106
|
+
"filledByServer",
|
|
107
|
+
"Filter",
|
|
108
|
+
"filterOperators",
|
|
109
|
+
"fixedFilter",
|
|
110
|
+
"format",
|
|
111
|
+
"hideFromCollection",
|
|
112
|
+
"includeEntityLink",
|
|
113
|
+
"includeId",
|
|
114
|
+
"markdown",
|
|
115
|
+
"minimalistView",
|
|
116
|
+
"multiline",
|
|
117
|
+
"Preview",
|
|
118
|
+
"previewAsTag",
|
|
119
|
+
"previewProperties",
|
|
120
|
+
"readOnly",
|
|
121
|
+
"renderInForm",
|
|
122
|
+
"sortable",
|
|
123
|
+
"span",
|
|
124
|
+
"spreadChildren",
|
|
125
|
+
"urlPreview",
|
|
126
|
+
"widget"
|
|
127
|
+
];
|
|
128
|
+
/**
|
|
129
|
+
* Move flattened admin keys back down into the `admin` block.
|
|
130
|
+
*
|
|
131
|
+
* The admin panel works with a *flat* view model — the block merged onto the
|
|
132
|
+
* collection — so what comes back from a form has `icon` and `defaultViewMode`
|
|
133
|
+
* at the top level while `admin` still holds whatever the file was loaded with.
|
|
134
|
+
* This is the way back.
|
|
135
|
+
*
|
|
136
|
+
* **The top-level value wins.** It is the one the form just wrote; the block is
|
|
137
|
+
* the copy the collection was loaded with, and preferring it resolves every edit
|
|
138
|
+
* in favour of the value the user changed away from.
|
|
139
|
+
*
|
|
140
|
+
* This lives here, next to the key lists, because it had two implementations —
|
|
141
|
+
* `toAdminCollectionConfig` in `@rebasepro/cms-types` and `nestAdminKeys` in
|
|
142
|
+
* `@rebasepro/server`'s schema editor — that agreed on everything except that
|
|
143
|
+
* precedence, which is the only part that decides whether a save is visible.
|
|
144
|
+
*
|
|
145
|
+
* @group Models
|
|
146
|
+
*/
|
|
147
|
+
function nestAdminKeysOf(source, adminKeys) {
|
|
148
|
+
const keys = new Set(adminKeys);
|
|
149
|
+
const top = {};
|
|
150
|
+
const block = { ...source.admin ?? {} };
|
|
151
|
+
for (const [key, value] of Object.entries(source)) {
|
|
152
|
+
if (key === "admin") continue;
|
|
153
|
+
if (keys.has(key)) block[key] = value;
|
|
154
|
+
else top[key] = value;
|
|
155
|
+
}
|
|
156
|
+
if (Object.keys(block).length > 0) top.admin = block;
|
|
157
|
+
return top;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* {@link nestAdminKeysOf} for a collection.
|
|
161
|
+
*
|
|
162
|
+
* @group Models
|
|
163
|
+
*/
|
|
164
|
+
function nestAdminCollectionKeys(collection) {
|
|
165
|
+
return nestAdminKeysOf(collection, ADMIN_COLLECTION_KEYS);
|
|
166
|
+
}
|
|
167
|
+
/** A record of properties, keyed by name — a map's `properties`, or a `oneOf` block's. */
|
|
168
|
+
function nestEachProperty(properties) {
|
|
169
|
+
return Object.fromEntries(Object.entries(properties).map(([key, child]) => [key, isNestable(child) ? nestAdminPropertyKeys(child) : child]));
|
|
170
|
+
}
|
|
171
|
+
/** Anything the walk can descend into: a plain object, not an array. */
|
|
172
|
+
function isNestable(value) {
|
|
173
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* {@link nestAdminKeysOf} for a property, applied to its children too.
|
|
177
|
+
*
|
|
178
|
+
* A map property carries `properties`, an array property carries `of`, and an
|
|
179
|
+
* array of typed blocks carries `oneOf.properties` — a record of properties like
|
|
180
|
+
* a map's. All of them hold properties with `admin` blocks of their own. A flat
|
|
181
|
+
* `readOnly` left on a child is as dead — and as fatal at the next boot — as one
|
|
182
|
+
* left on the parent, so the walk goes all the way down.
|
|
183
|
+
*
|
|
184
|
+
* `oneOf` was the container this walk did not know about, and it is the one the
|
|
185
|
+
* block-based collection templates are built out of: every block inside them
|
|
186
|
+
* kept its flat `markdown`, and the collection they created would not boot.
|
|
187
|
+
*
|
|
188
|
+
* @group Models
|
|
189
|
+
*/
|
|
190
|
+
function nestAdminPropertyKeys(property) {
|
|
191
|
+
const nested = nestAdminKeysOf(property, ADMIN_PROPERTY_KEYS);
|
|
192
|
+
const children = nested.properties;
|
|
193
|
+
if (isNestable(children)) nested.properties = nestEachProperty(children);
|
|
194
|
+
const oneOf = nested.oneOf;
|
|
195
|
+
if (isNestable(oneOf) && isNestable(oneOf.properties)) nested.oneOf = {
|
|
196
|
+
...oneOf,
|
|
197
|
+
properties: nestEachProperty(oneOf.properties)
|
|
198
|
+
};
|
|
199
|
+
const of = nested.of;
|
|
200
|
+
if (Array.isArray(of)) nested.of = of.map((entry) => isNestable(entry) ? nestAdminPropertyKeys(entry) : entry);
|
|
201
|
+
else if (isNestable(of)) nested.of = nestAdminPropertyKeys(of);
|
|
202
|
+
return nested;
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region ../types/src/types/collection_patch.ts
|
|
206
|
+
/**
|
|
207
|
+
* What changed about a collection, as opposed to what the collection is.
|
|
208
|
+
*
|
|
209
|
+
* ## Why a save is a patch
|
|
210
|
+
*
|
|
211
|
+
* The admin panel edits a collection as JSON — the only form a browser and a
|
|
212
|
+
* server can exchange it in — and JSON cannot carry most of what a collection
|
|
213
|
+
* file holds: an entity action's `onClick`, a computed field's `value`, a
|
|
214
|
+
* property shared from another module (`status: statusProperty`), an enum
|
|
215
|
+
* imported from elsewhere (`enum: LOCALE_ENUM`), the comments around all of
|
|
216
|
+
* them. A save that posted the whole collection and had it written back
|
|
217
|
+
* rewrote every key it carried and deleted everything JSON had dropped, so
|
|
218
|
+
* renaming a collection removed its actions' handlers, inlined its shared
|
|
219
|
+
* property without the custom `Field`, and froze a copy of the enum.
|
|
220
|
+
*
|
|
221
|
+
* So a save sends what the person changed: the difference between the JSON
|
|
222
|
+
* the editor loaded and the JSON it is saving. Anything JSON could not carry
|
|
223
|
+
* is identical on both sides of that difference — dropped from both — and
|
|
224
|
+
* cannot appear in it. The server applies the patch to the collection as it
|
|
225
|
+
* is (for the plan) and writes only the patched keys into the file.
|
|
226
|
+
*
|
|
227
|
+
* ## The shape
|
|
228
|
+
*
|
|
229
|
+
* Two operations on paths of object keys. Arrays are values: an array that
|
|
230
|
+
* changed is set whole. Positions in an array are not stable identities —
|
|
231
|
+
* element 2 of an enum is not "the third value" once a value is inserted —
|
|
232
|
+
* and the writer is the one place that can match an old element to its new
|
|
233
|
+
* self (by `key`/`id`/`name`) and keep what JSON dropped from it.
|
|
234
|
+
*
|
|
235
|
+
* Paths are in the AUTHORING shape — presentation keys under `admin`, the
|
|
236
|
+
* shape the file has — see {@link nestCollectionPatchPaths}.
|
|
237
|
+
*/
|
|
238
|
+
/**
|
|
239
|
+
* Keys the runtime adds to a collection that must never be written into its
|
|
240
|
+
* source. `resolvedRelation` is the registry's resolved form of a relation; a
|
|
241
|
+
* file holding one does not type-check (`target` is missing from it).
|
|
242
|
+
*/
|
|
243
|
+
var RUNTIME_ONLY_COLLECTION_KEYS = ["resolvedRelation"];
|
|
244
|
+
/** Path segments that reach an object's prototype rather than a key of it. */
|
|
245
|
+
var UNSAFE_SEGMENTS = /* @__PURE__ */ new Set([
|
|
246
|
+
"__proto__",
|
|
247
|
+
"constructor",
|
|
248
|
+
"prototype"
|
|
249
|
+
]);
|
|
250
|
+
var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
251
|
+
/** A JSON value as JSON would carry it: functions, `undefined` and symbols dropped. */
|
|
252
|
+
function toCollectionJson(value) {
|
|
253
|
+
if (value === void 0) return void 0;
|
|
254
|
+
const text = JSON.stringify(value);
|
|
255
|
+
return text === void 0 ? void 0 : JSON.parse(text);
|
|
256
|
+
}
|
|
257
|
+
function jsonEqual(a, b) {
|
|
258
|
+
if (a === b) return true;
|
|
259
|
+
if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((item, index) => jsonEqual(item, b[index]));
|
|
260
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
261
|
+
const keys = Object.keys(a);
|
|
262
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
263
|
+
return keys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && jsonEqual(a[key], b[key]));
|
|
264
|
+
}
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The operations that take `before` to `after`, both JSON values.
|
|
269
|
+
*
|
|
270
|
+
* Objects are compared key by key; anything else that differs — a primitive,
|
|
271
|
+
* an array, a change of kind — is set whole.
|
|
272
|
+
*/
|
|
273
|
+
function diffJsonValues(before, after, path = []) {
|
|
274
|
+
if (jsonEqual(before, after)) return [];
|
|
275
|
+
if (isPlainObject(before) && isPlainObject(after)) {
|
|
276
|
+
const ops = [];
|
|
277
|
+
for (const key of Object.keys(after)) if (!Object.prototype.hasOwnProperty.call(before, key)) ops.push({
|
|
278
|
+
op: "set",
|
|
279
|
+
path: [...path, key],
|
|
280
|
+
value: after[key]
|
|
281
|
+
});
|
|
282
|
+
else ops.push(...diffJsonValues(before[key], after[key], [...path, key]));
|
|
283
|
+
for (const key of Object.keys(before)) if (!Object.prototype.hasOwnProperty.call(after, key)) ops.push({
|
|
284
|
+
op: "remove",
|
|
285
|
+
path: [...path, key]
|
|
286
|
+
});
|
|
287
|
+
return ops;
|
|
288
|
+
}
|
|
289
|
+
return [{
|
|
290
|
+
op: "set",
|
|
291
|
+
path,
|
|
292
|
+
value: after
|
|
293
|
+
}];
|
|
294
|
+
}
|
|
295
|
+
function step(position, segment) {
|
|
296
|
+
switch (position) {
|
|
297
|
+
case "collection": return segment === "properties" ? "properties" : "other";
|
|
298
|
+
case "properties": return "property";
|
|
299
|
+
case "property":
|
|
300
|
+
if (segment === "properties") return "properties";
|
|
301
|
+
if (segment === "of") return "property";
|
|
302
|
+
if (segment === "oneOf") return "oneOf";
|
|
303
|
+
return "other";
|
|
304
|
+
case "oneOf": return segment === "properties" ? "properties" : "other";
|
|
305
|
+
default: return "other";
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function nestPath(path) {
|
|
309
|
+
const nested = [];
|
|
310
|
+
let position = "collection";
|
|
311
|
+
for (const segment of path) {
|
|
312
|
+
const moved = position === "collection" && ADMIN_COLLECTION_KEYS.includes(segment) || position === "property" && ADMIN_PROPERTY_KEYS.includes(segment);
|
|
313
|
+
if (moved) nested.push("admin");
|
|
314
|
+
nested.push(segment);
|
|
315
|
+
position = moved ? "other" : step(position, segment);
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
path: nested,
|
|
319
|
+
position
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function nestValue(position, value) {
|
|
323
|
+
if (position === "property") {
|
|
324
|
+
if (Array.isArray(value)) return value.map((entry) => isPlainObject(entry) ? nestAdminPropertyKeys(entry) : entry);
|
|
325
|
+
return isPlainObject(value) ? nestAdminPropertyKeys(value) : value;
|
|
326
|
+
}
|
|
327
|
+
if (position === "properties" && isPlainObject(value)) return Object.fromEntries(Object.entries(value).map(([key, property]) => [key, isPlainObject(property) ? nestAdminPropertyKeys(property) : property]));
|
|
328
|
+
if (position === "oneOf" && isPlainObject(value)) return isPlainObject(value.properties) ? {
|
|
329
|
+
...value,
|
|
330
|
+
properties: nestValue("properties", value.properties)
|
|
331
|
+
} : value;
|
|
332
|
+
if (position === "collection" && isPlainObject(value)) {
|
|
333
|
+
const nested = nestAdminCollectionKeys(value);
|
|
334
|
+
return isPlainObject(nested.properties) ? {
|
|
335
|
+
...nested,
|
|
336
|
+
properties: nestValue("properties", nested.properties)
|
|
337
|
+
} : nested;
|
|
338
|
+
}
|
|
339
|
+
return value;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* The patch, with every flat presentation key moved into its `admin` block.
|
|
343
|
+
*
|
|
344
|
+
* The panel edits a flat view model — `icon` beside `name`, a property's
|
|
345
|
+
* `readOnly` beside its `type` — while the file keeps them under `admin`. The
|
|
346
|
+
* difference is taken on the flat model, because that is where a cleared field
|
|
347
|
+
* shows up as a removed key (the `admin` copy the model was loaded with still
|
|
348
|
+
* holds the old value), and moved here.
|
|
349
|
+
*
|
|
350
|
+
* The flat value wins over the `admin` copy, as in {@link nestAdminKeysOf}:
|
|
351
|
+
* operations that were already under `admin` are ordered first, so the flat
|
|
352
|
+
* one — what the form just wrote — is applied last.
|
|
353
|
+
*/
|
|
354
|
+
function nestCollectionPatchPaths(patch) {
|
|
355
|
+
return patch.map((op, index) => {
|
|
356
|
+
const { path, position } = nestPath(op.path);
|
|
357
|
+
const wasNested = op.path.includes("admin");
|
|
358
|
+
return {
|
|
359
|
+
moved: op.op === "remove" ? {
|
|
360
|
+
op: "remove",
|
|
361
|
+
path
|
|
362
|
+
} : {
|
|
363
|
+
op: "set",
|
|
364
|
+
path,
|
|
365
|
+
value: nestValue(position, op.value)
|
|
366
|
+
},
|
|
367
|
+
wasNested,
|
|
368
|
+
index
|
|
369
|
+
};
|
|
370
|
+
}).sort((a, b) => a.wasNested === b.wasNested ? a.index - b.index : a.wasNested ? -1 : 1).map((entry) => entry.moved);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* The patch that takes the collection the editor loaded to the one it is
|
|
374
|
+
* saving, in the authoring shape. Empty when nothing changed.
|
|
375
|
+
*
|
|
376
|
+
* Both sides go through JSON first, so whatever JSON cannot carry — a
|
|
377
|
+
* function, a value it drops — is equally absent from both and never appears
|
|
378
|
+
* as a change.
|
|
379
|
+
*/
|
|
380
|
+
function diffCollections(loaded, saving) {
|
|
381
|
+
return nestCollectionPatchPaths(diffJsonValues(toCollectionJson(loaded), toCollectionJson(saving)));
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Why a patch may not be applied, or an empty list.
|
|
385
|
+
*
|
|
386
|
+
* Checked by every door that accepts one: a path is a list of non-empty object
|
|
387
|
+
* keys that cannot reach a prototype, and no runtime-only key may be written.
|
|
388
|
+
*/
|
|
389
|
+
function collectionPatchProblems(patch) {
|
|
390
|
+
if (!Array.isArray(patch)) return ["`patch` must be a list of operations."];
|
|
391
|
+
const problems = [];
|
|
392
|
+
patch.forEach((op, index) => {
|
|
393
|
+
const where = `patch[${index}]`;
|
|
394
|
+
if (!isPlainObject(op) || op.op !== "set" && op.op !== "remove") {
|
|
395
|
+
problems.push(`${where} must be { op: "set", path, value } or { op: "remove", path }.`);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const path = op.path;
|
|
399
|
+
if (!Array.isArray(path) || path.length === 0 || !path.every((segment) => typeof segment === "string" && segment.length > 0)) {
|
|
400
|
+
problems.push(`${where}.path must be a non-empty list of object keys.`);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const unsafe = path.find((segment) => UNSAFE_SEGMENTS.has(segment));
|
|
404
|
+
if (unsafe) problems.push(`${where}.path may not contain "${unsafe}".`);
|
|
405
|
+
const runtime = path.find((segment) => RUNTIME_ONLY_COLLECTION_KEYS.includes(segment));
|
|
406
|
+
if (runtime) problems.push(`${where} writes "${runtime}", which the runtime adds and a collection file must not hold.`);
|
|
407
|
+
if (op.op === "set") {
|
|
408
|
+
const key = keyIn(op.value, (key) => UNSAFE_SEGMENTS.has(key) || RUNTIME_ONLY_COLLECTION_KEYS.includes(key));
|
|
409
|
+
if (key) problems.push(`${where}.value holds "${key}", which a collection file must not hold.`);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
return problems;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* The first runtime-only key (see {@link RUNTIME_ONLY_COLLECTION_KEYS}) found
|
|
416
|
+
* anywhere in a value, or `undefined`. For the doors that take a whole
|
|
417
|
+
* collection rather than a patch.
|
|
418
|
+
*/
|
|
419
|
+
function runtimeOnlyKeyIn(value) {
|
|
420
|
+
return keyIn(value, (key) => RUNTIME_ONLY_COLLECTION_KEYS.includes(key));
|
|
421
|
+
}
|
|
422
|
+
/** A patch every door may apply: {@link collectionPatchProblems} found nothing. */
|
|
423
|
+
function isCollectionPatch(patch) {
|
|
424
|
+
return collectionPatchProblems(patch).length === 0;
|
|
425
|
+
}
|
|
426
|
+
function keyIn(value, matches) {
|
|
427
|
+
if (Array.isArray(value)) {
|
|
428
|
+
for (const item of value) {
|
|
429
|
+
const found = keyIn(item, matches);
|
|
430
|
+
if (found) return found;
|
|
431
|
+
}
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (!isPlainObject(value)) return void 0;
|
|
435
|
+
for (const key of Object.keys(value)) {
|
|
436
|
+
if (matches(key)) return key;
|
|
437
|
+
const found = keyIn(value[key], matches);
|
|
438
|
+
if (found) return found;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* `target` with the patch applied, copying only what it changes.
|
|
443
|
+
*
|
|
444
|
+
* Everything the patch does not reach is shared with `target` by reference —
|
|
445
|
+
* functions, thunks and all — which is what lets a planner see a relation's
|
|
446
|
+
* `target()` on the patched collection exactly as it was. Intermediate objects
|
|
447
|
+
* a `set` needs are created; a `remove` of a key that is not there is nothing
|
|
448
|
+
* to do.
|
|
449
|
+
*/
|
|
450
|
+
function applyCollectionPatch(target, patch) {
|
|
451
|
+
let root = target;
|
|
452
|
+
for (const op of patch) root = applyOp(root, op.path, op);
|
|
453
|
+
return root;
|
|
454
|
+
}
|
|
455
|
+
function applyOp(node, path, op) {
|
|
456
|
+
const [head, ...rest] = path;
|
|
457
|
+
if (UNSAFE_SEGMENTS.has(head)) throw new Error(`A collection patch may not reach "${head}".`);
|
|
458
|
+
const base = isPlainObject(node) ? { ...node } : {};
|
|
459
|
+
if (rest.length === 0) {
|
|
460
|
+
if (op.op === "remove") delete base[head];
|
|
461
|
+
else base[head] = op.value;
|
|
462
|
+
return base;
|
|
463
|
+
}
|
|
464
|
+
const child = Object.prototype.hasOwnProperty.call(base, head) ? base[head] : void 0;
|
|
465
|
+
if (op.op === "remove" && !isPlainObject(child)) return node;
|
|
466
|
+
base[head] = applyOp(child, rest, op);
|
|
467
|
+
return base;
|
|
468
|
+
}
|
|
469
|
+
//#endregion
|
|
470
|
+
export { nestCollectionPatchPaths as a, ADMIN_PROPERTY_KEYS as c, isCollectionPatch as i, nestAdminCollectionKeys as l, collectionPatchProblems as n, runtimeOnlyKeyIn as o, diffCollections as r, ADMIN_COLLECTION_KEYS as s, applyCollectionPatch as t, nestAdminPropertyKeys as u };
|
|
471
|
+
|
|
472
|
+
//# sourceMappingURL=collection_patch-BRu-BvDv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection_patch-BRu-BvDv.js","names":[],"sources":["../../types/src/types/admin_block.ts","../../types/src/types/collection_patch.ts"],"sourcesContent":["/**\n * The keys of a collection's admin block, as data.\n *\n * There is no *type* for the block in this package any more, and that is the point:\n * `admin` is not declared on `BaseCollectionConfig` or on any property here, so a\n * BaaS install cannot even write one. `@rebasepro/cms-types` adds the field back by\n * declaration merging, which is why installing it is what makes the admin surface\n * appear.\n *\n * The *list* still has to live here, because three runtime consumers need it and two\n * of them are core — see below.\n */\n\n/**\n * Every key that belongs inside a collection's `admin` block, as data.\n *\n * The type that describes these fields is `AdminCollectionOptions` in\n * `@rebasepro/cms-types`, and it is erased at build time — but three runtime\n * consumers need the list, and two of them are core:\n *\n * - `serializeCollections`, to drop the block from the contract\n * - the ts-morph schema editor in `@rebasepro/server`, which rewrites collection\n * files on disk from the admin panel and has to know where each key goes. A key\n * missing from this list gets written to the *top level* of the file, where the\n * backend ignores it and the panel never finds it again.\n * - the `collections-admin-block` codemod\n *\n * `@rebasepro/cms-types` re-exports this and asserts it names only real option\n * keys; the count is pinned by a test there.\n *\n * @group Models\n */\nexport const ADMIN_COLLECTION_KEYS = [\n \"Actions\",\n \"additionalFields\",\n \"alwaysApplyDefaultValues\",\n \"browserCallbacks\",\n \"components\",\n \"customViews\",\n \"defaultEntityAction\",\n \"defaultFilter\",\n \"defaultSelectedView\",\n \"defaultSize\",\n \"defaultViewMode\",\n \"disableDefaultActions\",\n \"display\",\n \"enabledViews\",\n \"entityActions\",\n \"entityViews\",\n \"exportable\",\n \"filterPresets\",\n \"fixedFilter\",\n \"form\",\n \"formAutoSave\",\n \"formView\",\n \"group\",\n \"hideFromEntityViews\",\n \"hideFromNavigation\",\n \"hideIdFromCollection\",\n \"hideIdFromForm\",\n \"icon\",\n \"includeJsonView\",\n \"inlineEditing\",\n \"kanban\",\n \"listProperties\",\n \"localChangesBackup\",\n \"openEntityMode\",\n \"orderProperty\",\n \"pagination\",\n \"previewProperties\",\n \"propertiesOrder\",\n \"selectionController\",\n \"selectionEnabled\",\n \"sideDialogWidth\",\n \"sort\"\n] as const;\n\n/** A key of a collection's `admin` block. @group Models */\nexport type AdminCollectionKey = typeof ADMIN_COLLECTION_KEYS[number];\n\n/**\n * Every key that belongs inside a *property's* `admin` block, as data.\n *\n * The union of `AdminPropertyOptions` and its per-type extensions\n * (`AdminStringOptions`, `AdminArrayOptions`, …) in `@rebasepro/cms-types`.\n * It lives here for the same reason {@link ADMIN_COLLECTION_KEYS} does: the\n * runtime consumers are core packages that the BaaS guard forbids from\n * importing `@rebasepro/cms-types`. Here it is the boot-time collection\n * validator in `@rebasepro/server`, which has to tell \"you left `readOnly` at\n * the top of the property, where nothing reads it\" apart from \"you invented a\n * key we have never heard of\".\n *\n * `@rebasepro/cms-types` re-exports this and asserts it names only real\n * option keys.\n *\n * @group Models\n */\nexport const ADMIN_PROPERTY_KEYS = [\n \"canAddElements\",\n \"clearable\",\n \"columnWidth\",\n \"customProps\",\n \"disabled\",\n \"expanded\",\n \"Field\",\n \"filledByServer\",\n \"Filter\",\n \"filterOperators\",\n \"fixedFilter\",\n \"format\",\n \"hideFromCollection\",\n \"includeEntityLink\",\n \"includeId\",\n \"markdown\",\n \"minimalistView\",\n \"multiline\",\n \"Preview\",\n \"previewAsTag\",\n \"previewProperties\",\n \"readOnly\",\n \"renderInForm\",\n \"sortable\",\n \"span\",\n \"spreadChildren\",\n \"urlPreview\",\n \"widget\",\n] as const;\n\n/** A key of a property's `admin` block. @group Models */\nexport type AdminPropertyKey = typeof ADMIN_PROPERTY_KEYS[number];\n\n/**\n * Move flattened admin keys back down into the `admin` block.\n *\n * The admin panel works with a *flat* view model — the block merged onto the\n * collection — so what comes back from a form has `icon` and `defaultViewMode`\n * at the top level while `admin` still holds whatever the file was loaded with.\n * This is the way back.\n *\n * **The top-level value wins.** It is the one the form just wrote; the block is\n * the copy the collection was loaded with, and preferring it resolves every edit\n * in favour of the value the user changed away from.\n *\n * This lives here, next to the key lists, because it had two implementations —\n * `toAdminCollectionConfig` in `@rebasepro/cms-types` and `nestAdminKeys` in\n * `@rebasepro/server`'s schema editor — that agreed on everything except that\n * precedence, which is the only part that decides whether a save is visible.\n *\n * @group Models\n */\nexport function nestAdminKeysOf(\n source: Record<string, unknown>,\n adminKeys: readonly string[]\n): Record<string, unknown> {\n const keys = new Set<string>(adminKeys);\n const top: Record<string, unknown> = {};\n const block: Record<string, unknown> = { ...((source.admin as Record<string, unknown> | undefined) ?? {}) };\n\n for (const [key, value] of Object.entries(source)) {\n if (key === \"admin\") continue;\n if (keys.has(key)) block[key] = value;\n else top[key] = value;\n }\n\n if (Object.keys(block).length > 0) top.admin = block;\n return top;\n}\n\n/**\n * {@link nestAdminKeysOf} for a collection.\n *\n * @group Models\n */\nexport function nestAdminCollectionKeys(collection: Record<string, unknown>): Record<string, unknown> {\n return nestAdminKeysOf(collection, ADMIN_COLLECTION_KEYS);\n}\n\n/** A record of properties, keyed by name — a map's `properties`, or a `oneOf` block's. */\nfunction nestEachProperty(properties: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(properties).map(([key, child]) => [\n key,\n isNestable(child) ? nestAdminPropertyKeys(child) : child\n ])\n );\n}\n\n/** Anything the walk can descend into: a plain object, not an array. */\nfunction isNestable(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/**\n * {@link nestAdminKeysOf} for a property, applied to its children too.\n *\n * A map property carries `properties`, an array property carries `of`, and an\n * array of typed blocks carries `oneOf.properties` — a record of properties like\n * a map's. All of them hold properties with `admin` blocks of their own. A flat\n * `readOnly` left on a child is as dead — and as fatal at the next boot — as one\n * left on the parent, so the walk goes all the way down.\n *\n * `oneOf` was the container this walk did not know about, and it is the one the\n * block-based collection templates are built out of: every block inside them\n * kept its flat `markdown`, and the collection they created would not boot.\n *\n * @group Models\n */\nexport function nestAdminPropertyKeys(property: Record<string, unknown>): Record<string, unknown> {\n const nested = nestAdminKeysOf(property, ADMIN_PROPERTY_KEYS);\n\n const children = nested.properties;\n if (isNestable(children)) {\n nested.properties = nestEachProperty(children);\n }\n\n // `oneOf` is not itself a property — it is a block holding `properties`\n // alongside `typeField`, `valueField` and `propertiesOrder`, none of which\n // may be walked as one.\n const oneOf = nested.oneOf;\n if (isNestable(oneOf) && isNestable(oneOf.properties)) {\n nested.oneOf = { ...oneOf, properties: nestEachProperty(oneOf.properties) };\n }\n\n const of = nested.of;\n if (Array.isArray(of)) {\n nested.of = of.map(entry => isNestable(entry) ? nestAdminPropertyKeys(entry) : entry);\n } else if (isNestable(of)) {\n nested.of = nestAdminPropertyKeys(of);\n }\n\n return nested;\n}\n","/**\n * What changed about a collection, as opposed to what the collection is.\n *\n * ## Why a save is a patch\n *\n * The admin panel edits a collection as JSON — the only form a browser and a\n * server can exchange it in — and JSON cannot carry most of what a collection\n * file holds: an entity action's `onClick`, a computed field's `value`, a\n * property shared from another module (`status: statusProperty`), an enum\n * imported from elsewhere (`enum: LOCALE_ENUM`), the comments around all of\n * them. A save that posted the whole collection and had it written back\n * rewrote every key it carried and deleted everything JSON had dropped, so\n * renaming a collection removed its actions' handlers, inlined its shared\n * property without the custom `Field`, and froze a copy of the enum.\n *\n * So a save sends what the person changed: the difference between the JSON\n * the editor loaded and the JSON it is saving. Anything JSON could not carry\n * is identical on both sides of that difference — dropped from both — and\n * cannot appear in it. The server applies the patch to the collection as it\n * is (for the plan) and writes only the patched keys into the file.\n *\n * ## The shape\n *\n * Two operations on paths of object keys. Arrays are values: an array that\n * changed is set whole. Positions in an array are not stable identities —\n * element 2 of an enum is not \"the third value\" once a value is inserted —\n * and the writer is the one place that can match an old element to its new\n * self (by `key`/`id`/`name`) and keep what JSON dropped from it.\n *\n * Paths are in the AUTHORING shape — presentation keys under `admin`, the\n * shape the file has — see {@link nestCollectionPatchPaths}.\n */\nimport { ADMIN_COLLECTION_KEYS, ADMIN_PROPERTY_KEYS, nestAdminCollectionKeys, nestAdminPropertyKeys } from \"./admin_block\";\n\n/** One change to a collection. `path` is a list of object keys. */\nexport type CollectionPatchOp =\n | { op: \"set\"; path: string[]; value: unknown }\n | { op: \"remove\"; path: string[] };\n\n/** @group Models */\nexport type CollectionPatch = CollectionPatchOp[];\n\n/**\n * Keys the runtime adds to a collection that must never be written into its\n * source. `resolvedRelation` is the registry's resolved form of a relation; a\n * file holding one does not type-check (`target` is missing from it).\n */\nexport const RUNTIME_ONLY_COLLECTION_KEYS: readonly string[] = [\"resolvedRelation\"];\n\n/** Path segments that reach an object's prototype rather than a key of it. */\nconst UNSAFE_SEGMENTS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** A JSON value as JSON would carry it: functions, `undefined` and symbols dropped. */\nexport function toCollectionJson(value: unknown): unknown {\n if (value === undefined) return undefined;\n const text = JSON.stringify(value);\n return text === undefined ? undefined : JSON.parse(text);\n}\n\nfunction jsonEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((item, index) => jsonEqual(item, b[index]));\n }\n if (isPlainObject(a) && isPlainObject(b)) {\n const keys = Object.keys(a);\n if (keys.length !== Object.keys(b).length) return false;\n return keys.every(key => Object.prototype.hasOwnProperty.call(b, key) && jsonEqual(a[key], b[key]));\n }\n return false;\n}\n\n/**\n * The operations that take `before` to `after`, both JSON values.\n *\n * Objects are compared key by key; anything else that differs — a primitive,\n * an array, a change of kind — is set whole.\n */\nexport function diffJsonValues(before: unknown, after: unknown, path: string[] = []): CollectionPatch {\n if (jsonEqual(before, after)) return [];\n if (isPlainObject(before) && isPlainObject(after)) {\n const ops: CollectionPatch = [];\n for (const key of Object.keys(after)) {\n if (!Object.prototype.hasOwnProperty.call(before, key)) {\n ops.push({ op: \"set\", path: [...path, key], value: after[key] });\n } else {\n ops.push(...diffJsonValues(before[key], after[key], [...path, key]));\n }\n }\n for (const key of Object.keys(before)) {\n if (!Object.prototype.hasOwnProperty.call(after, key)) ops.push({ op: \"remove\", path: [...path, key] });\n }\n return ops;\n }\n return [{ op: \"set\", path, value: after }];\n}\n\n/**\n * Where a path lands in a collection, so a flat presentation key can be moved\n * into the `admin` block it belongs to.\n *\n * A collection holds `properties`, a map of properties; a property holds\n * `properties` (a map's children), `of` (an array's element) and\n * `oneOf.properties` (typed blocks). Every other key leaves the structure.\n */\ntype Position = \"collection\" | \"properties\" | \"property\" | \"oneOf\" | \"other\";\n\nfunction step(position: Position, segment: string): Position {\n switch (position) {\n case \"collection\":\n return segment === \"properties\" ? \"properties\" : \"other\";\n case \"properties\":\n return \"property\";\n case \"property\":\n if (segment === \"properties\") return \"properties\";\n if (segment === \"of\") return \"property\";\n if (segment === \"oneOf\") return \"oneOf\";\n return \"other\";\n case \"oneOf\":\n return segment === \"properties\" ? \"properties\" : \"other\";\n default:\n return \"other\";\n }\n}\n\nfunction nestPath(path: string[]): { path: string[]; position: Position } {\n const nested: string[] = [];\n let position: Position = \"collection\";\n for (const segment of path) {\n const moved: boolean = (position === \"collection\" && (ADMIN_COLLECTION_KEYS as readonly string[]).includes(segment))\n || (position === \"property\" && (ADMIN_PROPERTY_KEYS as readonly string[]).includes(segment));\n if (moved) nested.push(\"admin\");\n nested.push(segment);\n // Inside `admin` nothing is moved any further.\n position = moved ? \"other\" : step(position, segment);\n }\n return { path: nested, position };\n}\n\nfunction nestValue(position: Position, value: unknown): unknown {\n if (position === \"property\") {\n if (Array.isArray(value)) return value.map(entry => isPlainObject(entry) ? nestAdminPropertyKeys(entry) : entry);\n return isPlainObject(value) ? nestAdminPropertyKeys(value) : value;\n }\n if (position === \"properties\" && isPlainObject(value)) {\n return Object.fromEntries(Object.entries(value).map(([key, property]) =>\n [key, isPlainObject(property) ? nestAdminPropertyKeys(property) : property]));\n }\n if (position === \"oneOf\" && isPlainObject(value)) {\n return isPlainObject(value.properties)\n ? { ...value, properties: nestValue(\"properties\", value.properties) }\n : value;\n }\n if (position === \"collection\" && isPlainObject(value)) {\n const nested = nestAdminCollectionKeys(value);\n return isPlainObject(nested.properties)\n ? { ...nested, properties: nestValue(\"properties\", nested.properties) }\n : nested;\n }\n return value;\n}\n\n/**\n * The patch, with every flat presentation key moved into its `admin` block.\n *\n * The panel edits a flat view model — `icon` beside `name`, a property's\n * `readOnly` beside its `type` — while the file keeps them under `admin`. The\n * difference is taken on the flat model, because that is where a cleared field\n * shows up as a removed key (the `admin` copy the model was loaded with still\n * holds the old value), and moved here.\n *\n * The flat value wins over the `admin` copy, as in {@link nestAdminKeysOf}:\n * operations that were already under `admin` are ordered first, so the flat\n * one — what the form just wrote — is applied last.\n */\nexport function nestCollectionPatchPaths(patch: CollectionPatch): CollectionPatch {\n const nested = patch.map((op, index) => {\n const { path, position } = nestPath(op.path);\n const wasNested = op.path.includes(\"admin\");\n const moved: CollectionPatchOp = op.op === \"remove\"\n ? { op: \"remove\", path }\n : { op: \"set\", path, value: nestValue(position, op.value) };\n return { moved, wasNested, index };\n });\n return nested\n .sort((a, b) => (a.wasNested === b.wasNested ? a.index - b.index : a.wasNested ? -1 : 1))\n .map(entry => entry.moved);\n}\n\n/**\n * The patch that takes the collection the editor loaded to the one it is\n * saving, in the authoring shape. Empty when nothing changed.\n *\n * Both sides go through JSON first, so whatever JSON cannot carry — a\n * function, a value it drops — is equally absent from both and never appears\n * as a change.\n */\nexport function diffCollections(loaded: unknown, saving: unknown): CollectionPatch {\n return nestCollectionPatchPaths(diffJsonValues(toCollectionJson(loaded), toCollectionJson(saving)));\n}\n\n/**\n * Why a patch may not be applied, or an empty list.\n *\n * Checked by every door that accepts one: a path is a list of non-empty object\n * keys that cannot reach a prototype, and no runtime-only key may be written.\n */\nexport function collectionPatchProblems(patch: unknown): string[] {\n if (!Array.isArray(patch)) return [\"`patch` must be a list of operations.\"];\n const problems: string[] = [];\n patch.forEach((op: unknown, index) => {\n const where = `patch[${index}]`;\n if (!isPlainObject(op) || (op.op !== \"set\" && op.op !== \"remove\")) {\n problems.push(`${where} must be { op: \"set\", path, value } or { op: \"remove\", path }.`);\n return;\n }\n const path = op.path;\n if (!Array.isArray(path) || path.length === 0 || !path.every(segment => typeof segment === \"string\" && segment.length > 0)) {\n problems.push(`${where}.path must be a non-empty list of object keys.`);\n return;\n }\n const unsafe = path.find(segment => UNSAFE_SEGMENTS.has(segment as string));\n if (unsafe) problems.push(`${where}.path may not contain \"${unsafe}\".`);\n const runtime = path.find(segment => RUNTIME_ONLY_COLLECTION_KEYS.includes(segment as string));\n if (runtime) problems.push(`${where} writes \"${runtime}\", which the runtime adds and a collection file must not hold.`);\n if (op.op === \"set\") {\n const key = keyIn(op.value, key => UNSAFE_SEGMENTS.has(key) || RUNTIME_ONLY_COLLECTION_KEYS.includes(key));\n if (key) problems.push(`${where}.value holds \"${key}\", which a collection file must not hold.`);\n }\n });\n return problems;\n}\n\n/**\n * The first runtime-only key (see {@link RUNTIME_ONLY_COLLECTION_KEYS}) found\n * anywhere in a value, or `undefined`. For the doors that take a whole\n * collection rather than a patch.\n */\nexport function runtimeOnlyKeyIn(value: unknown): string | undefined {\n return keyIn(value, key => RUNTIME_ONLY_COLLECTION_KEYS.includes(key));\n}\n\n/** A patch every door may apply: {@link collectionPatchProblems} found nothing. */\nexport function isCollectionPatch(patch: unknown): patch is CollectionPatch {\n return collectionPatchProblems(patch).length === 0;\n}\n\nfunction keyIn(value: unknown, matches: (key: string) => boolean): string | undefined {\n if (Array.isArray(value)) {\n for (const item of value) {\n const found = keyIn(item, matches);\n if (found) return found;\n }\n return undefined;\n }\n if (!isPlainObject(value)) return undefined;\n for (const key of Object.keys(value)) {\n if (matches(key)) return key;\n const found = keyIn(value[key], matches);\n if (found) return found;\n }\n return undefined;\n}\n\n/**\n * `target` with the patch applied, copying only what it changes.\n *\n * Everything the patch does not reach is shared with `target` by reference —\n * functions, thunks and all — which is what lets a planner see a relation's\n * `target()` on the patched collection exactly as it was. Intermediate objects\n * a `set` needs are created; a `remove` of a key that is not there is nothing\n * to do.\n */\nexport function applyCollectionPatch<T>(target: T, patch: CollectionPatch): T {\n let root: unknown = target;\n for (const op of patch) {\n root = applyOp(root, op.path, op);\n }\n return root as T;\n}\n\nfunction applyOp(node: unknown, path: string[], op: CollectionPatchOp): unknown {\n const [head, ...rest] = path;\n if (UNSAFE_SEGMENTS.has(head)) throw new Error(`A collection patch may not reach \"${head}\".`);\n const base: Record<string, unknown> = isPlainObject(node) ? { ...node } : {};\n if (rest.length === 0) {\n if (op.op === \"remove\") delete base[head];\n else base[head] = op.value;\n return base;\n }\n const child = Object.prototype.hasOwnProperty.call(base, head) ? base[head] : undefined;\n if (op.op === \"remove\" && !isPlainObject(child)) return node;\n base[head] = applyOp(child, rest, op);\n return base;\n}\n"],"mappings":"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"}
|
|
@@ -3,10 +3,23 @@ import __rebaseProcess from "process";
|
|
|
3
3
|
globalThis.process ??= __rebaseProcess;
|
|
4
4
|
__rebaseCreateRequire(import.meta.url);
|
|
5
5
|
import { n as __exportAll } from "./rolldown-runtime-dW7B1o5h.js";
|
|
6
|
-
import "./src-
|
|
7
|
-
import { r as logger } from "./logger-
|
|
6
|
+
import "./src-CatHFUym.js";
|
|
7
|
+
import { r as logger } from "./logger-D-S-hO5e.js";
|
|
8
8
|
import { Hono } from "hono";
|
|
9
9
|
//#region ../types/src/types/project_manifest.ts
|
|
10
|
+
/**
|
|
11
|
+
* Whether `host` is a public DNS name an app can be given: lowercase labels of
|
|
12
|
+
* letters, digits and hyphens, at least two of them, not an IP address and not
|
|
13
|
+
* `localhost`. Internationalised names arrive here already in punycode.
|
|
14
|
+
*/
|
|
15
|
+
function isValidAppHost(host) {
|
|
16
|
+
if (host.length === 0 || host.length > 253 || host !== host.toLowerCase()) return false;
|
|
17
|
+
const labels = host.split(".");
|
|
18
|
+
if (labels.length < 2) return false;
|
|
19
|
+
const label = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
20
|
+
if (!labels.every((part) => label.test(part))) return false;
|
|
21
|
+
return !/^[0-9]+$/.test(labels[labels.length - 1]);
|
|
22
|
+
}
|
|
10
23
|
/** Header carrying the schema version an SDK was generated from. */
|
|
11
24
|
var SCHEMA_VERSION_HEADER = "x-rebase-schema";
|
|
12
25
|
//#endregion
|
|
@@ -275,6 +288,6 @@ function createContractRoutes(config) {
|
|
|
275
288
|
return router;
|
|
276
289
|
}
|
|
277
290
|
//#endregion
|
|
278
|
-
export { SCHEMA_VERSION_HEADER as i, createContractRoutes as n, computeSchemaVersion as r, contract_routes_exports as t };
|
|
291
|
+
export { isValidAppHost as a, SCHEMA_VERSION_HEADER as i, createContractRoutes as n, computeSchemaVersion as r, contract_routes_exports as t };
|
|
279
292
|
|
|
280
|
-
//# sourceMappingURL=contract-routes-
|
|
293
|
+
//# sourceMappingURL=contract-routes-fz8i4pxs.js.map
|