@mandujs/core 0.54.31 → 0.55.0-beta.1
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 +654 -654
- package/package.json +106 -210
- package/scripts/postinstall-lock.ts +153 -153
- package/src/a11y/__tests__/run-audit.test.ts +333 -333
- package/src/a11y/fix-hints.ts +76 -76
- package/src/a11y/index.ts +18 -18
- package/src/a11y/run-audit.ts +15 -15
- package/src/a11y/types.ts +125 -125
- package/src/agent/__tests__/apply.test.ts +224 -0
- package/src/agent/__tests__/context.test.ts +97 -98
- package/src/agent/apply.ts +1031 -0
- package/src/agent/context.ts +552 -552
- package/src/agent/index.ts +4 -3
- package/src/agent/plan.ts +161 -243
- package/src/agent/repair.ts +239 -162
- package/src/agent/sync.ts +197 -198
- package/src/agent/types.ts +242 -94
- package/src/agent/verify.ts +55 -55
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +15 -5
- package/src/auth/__tests__/reset.test.ts +2 -2
- package/src/auth/__tests__/tokens.test.ts +274 -274
- package/src/auth/__tests__/verification.test.ts +274 -274
- package/src/auth/index.ts +76 -76
- package/src/auth/login.ts +225 -225
- package/src/auth/password.ts +14 -2
- package/src/auth/reset.ts +243 -243
- package/src/auth/tokens.ts +612 -612
- package/src/auth/verification.ts +253 -253
- package/src/brain/__tests__/redactor.test.ts +94 -94
- package/src/brain/adapters/__tests__/_helpers.ts +83 -83
- package/src/brain/adapters/__tests__/anthropic-oauth.test.ts +196 -196
- package/src/brain/adapters/__tests__/chatgpt-auth.test.ts +193 -193
- package/src/brain/adapters/__tests__/openai-oauth.test.ts +209 -209
- package/src/brain/adapters/__tests__/resolver.test.ts +143 -143
- package/src/brain/adapters/anthropic-oauth.ts +1 -1
- package/src/brain/adapters/chatgpt-auth.ts +300 -300
- package/src/brain/adapters/index.ts +319 -319
- package/src/brain/adapters/oauth-flow.ts +439 -439
- package/src/brain/consent.ts +240 -240
- package/src/brain/credentials.ts +396 -396
- package/src/brain/doctor/analyzer.ts +7 -7
- package/src/bundler/__snapshots__/build.test.ts.snap +5 -5
- package/src/bundler/__tests__/build-runner.ts +166 -166
- package/src/bundler/__tests__/client-boundary-transform.test.ts +524 -524
- package/src/bundler/__tests__/cold-start.test.ts +60 -60
- package/src/bundler/__tests__/css.test.ts +49 -20
- package/src/bundler/__tests__/dev-reliability.test.ts +619 -619
- package/src/bundler/__tests__/extended-watch.test.ts +711 -711
- package/src/bundler/__tests__/fast-refresh.test.ts +24 -24
- package/src/bundler/__tests__/generation.test.ts +447 -0
- package/src/bundler/__tests__/hdr.test.ts +24 -18
- package/src/bundler/__tests__/hmr-client.test.ts +62 -24
- package/src/bundler/__tests__/jsx-runtime-shim.test.ts +179 -140
- package/src/bundler/__tests__/manifest-schema.test.ts +305 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +42 -42
- package/src/bundler/__tests__/slot-dispatch.test.ts +573 -573
- package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -286
- package/src/bundler/__tests__/vendor-cache.test.ts +455 -455
- package/src/bundler/analyzer.ts +15 -15
- package/src/bundler/budget.ts +404 -404
- package/src/bundler/build.test.ts +898 -915
- package/src/bundler/build.ts +113 -24
- package/src/bundler/client-boundary-transform.ts +977 -977
- package/src/bundler/css.ts +65 -44
- package/src/bundler/dev.ts +105 -73
- package/src/bundler/fast-refresh-preamble.ts +47 -47
- package/src/bundler/generation.ts +602 -0
- package/src/bundler/index.ts +3 -3
- package/src/bundler/manifest-schema.ts +55 -40
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +13 -13
- package/src/bundler/plugins/__tests__/react-compiler-config.test.ts +83 -83
- package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -110
- package/src/bundler/plugins/block-generated-imports.ts +16 -15
- package/src/bundler/plugins/index.ts +83 -83
- package/src/bundler/plugins/react-compiler-config.ts +108 -108
- package/src/bundler/plugins/react-compiler-lint.ts +253 -253
- package/src/bundler/plugins/react-compiler.ts +162 -162
- package/src/bundler/prerender.ts +29 -9
- package/src/bundler/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +1 -1
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +58 -56
- package/src/bundler/vendor-cache-types.ts +130 -130
- package/src/bundler/vendor-cache.ts +526 -526
- package/src/change/snapshot.ts +18 -18
- package/src/client/Form.tsx +105 -105
- package/src/client/Link.tsx +9 -9
- package/src/client/__tests__/props-serialization.test.ts +37 -37
- package/src/client/__tests__/use-sse.test.ts +153 -153
- package/src/client/globals.ts +1 -1
- package/src/client/hooks.ts +362 -362
- package/src/client/hydrate.ts +2 -2
- package/src/client/index.ts +1 -1
- package/src/client/island.ts +79 -79
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/props-serialization.ts +233 -233
- package/src/client/runtime-entry.ts +598 -598
- package/src/client/runtime.ts +1 -1
- package/src/client/serialize.ts +50 -50
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-head.ts +197 -197
- package/src/client/use-sse.ts +378 -378
- package/src/client/window-state.ts +101 -101
- package/src/components/Image-compat.ts +3 -0
- package/src/components/Image.tsx +162 -162
- package/src/config/validate.ts +1 -1
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +9 -9
- package/src/content/content-layer.ts +7 -7
- package/src/content/data-store.ts +245 -245
- package/src/content/frontmatter.ts +189 -189
- package/src/content/generate-types.ts +1 -1
- package/src/content/index.ts +2 -2
- package/src/content/loader-context.ts +171 -171
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +172 -172
- package/src/content/loaders/glob.ts +253 -253
- package/src/content/loaders/index.ts +34 -34
- package/src/content/loaders/types.ts +137 -137
- package/src/content/meta-store.ts +209 -209
- package/src/content/prebuild.test.ts +571 -571
- package/src/content/prebuild.ts +636 -636
- package/src/content/schema.ts +20 -20
- package/src/content/sidebar.ts +630 -630
- package/src/content/slug.ts +110 -110
- package/src/content/types.ts +282 -282
- package/src/content/watcher.ts +135 -135
- package/src/contract/client-safe.test.ts +42 -42
- package/src/contract/client-safe.ts +114 -114
- package/src/contract/define.ts +11 -11
- package/src/contract/index.ts +1 -1
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +410 -410
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/route-helpers.ts +1 -1
- package/src/contract/rpc.ts +443 -443
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +9 -9
- package/src/db/index.ts +2 -2
- package/src/db/migrations/history-table.ts +345 -345
- package/src/db/migrations/index.ts +3 -3
- package/src/db/migrations/lock.ts +324 -324
- package/src/db/migrations/runner.ts +650 -650
- package/src/deploy/cache.ts +140 -140
- package/src/deploy/compile/vercel.ts +344 -344
- package/src/deploy/index.ts +87 -87
- package/src/deploy/inference/brain.ts +268 -268
- package/src/deploy/inference/context.ts +82 -82
- package/src/deploy/inference/filling-extract.ts +245 -245
- package/src/deploy/inference/heuristic.ts +182 -182
- package/src/deploy/intent.ts +173 -173
- package/src/deploy/plan.ts +178 -178
- package/src/design/__tests__/agents-link.test.ts +109 -109
- package/src/design/__tests__/extract-patch-diff.test.ts +265 -265
- package/src/design/__tests__/lint.test.ts +110 -110
- package/src/design/__tests__/parser.test.ts +195 -195
- package/src/design/__tests__/tailwind-theme.test.ts +229 -229
- package/src/design/agents-link.ts +165 -165
- package/src/design/diff.ts +138 -138
- package/src/design/extract.ts +285 -285
- package/src/design/index.ts +102 -102
- package/src/design/lint.ts +209 -209
- package/src/design/parser.ts +555 -555
- package/src/design/patch.ts +241 -241
- package/src/design/scaffold.ts +147 -147
- package/src/design/tailwind-theme.ts +441 -441
- package/src/design/types.ts +210 -210
- package/src/desktop/__tests__/smoke.test.ts +2 -2
- package/src/desktop/__tests__/webview-fallback.test.ts +12 -32
- package/src/desktop/__tests__/window.test.ts +20 -99
- package/src/desktop/__tests__/worker.test.ts +266 -266
- package/src/desktop/index.ts +43 -43
- package/src/desktop/types.ts +158 -158
- package/src/desktop/webview-fallback.ts +17 -4
- package/src/desktop/window.ts +17 -8
- package/src/desktop/worker.ts +180 -180
- package/src/dev-error-overlay/__tests__/overlay-injector.test.ts +241 -241
- package/src/dev-error-overlay/index.ts +30 -30
- package/src/dev-error-overlay/overlay-injector.ts +243 -243
- package/src/dev-error-overlay/overlay-styles.ts +52 -52
- package/src/dev-error-overlay/types.ts +66 -66
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +25 -25
- package/src/devtools/client/catchers/error-catcher.ts +344 -344
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/mandu-character.tsx +331 -331
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +30 -30
- package/src/devtools/client/components/panel/islands-panel.tsx +16 -16
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +1 -1
- package/src/devtools/client/components/panel/preview-panel.tsx +46 -46
- package/src/devtools/client/filters/context-filters.ts +282 -282
- package/src/devtools/client/filters/index.ts +16 -16
- package/src/devtools/client/index.ts +63 -63
- package/src/devtools/hook/create-hook.ts +207 -207
- package/src/devtools/hook/index.ts +13 -13
- package/src/devtools/index.ts +439 -439
- package/src/devtools/init.ts +265 -265
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/types.ts +35 -35
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +233 -233
- package/src/diagnose/__tests__/checks.test.ts +136 -136
- package/src/diagnose/checks.ts +185 -185
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +10 -10
- package/src/diagnose/types.ts +53 -53
- package/src/email/__tests__/email.test.ts +355 -355
- package/src/email/index.ts +282 -282
- package/src/email/smtp.ts +64 -64
- package/src/error/domains.ts +265 -265
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/__tests__/session-sqlite.test.ts +5 -1
- package/src/filling/auth.ts +308 -308
- package/src/filling/body-parse.test.ts +60 -60
- package/src/filling/cookie-codec.ts +5 -3
- package/src/filling/deps.ts +265 -265
- package/src/filling/head-method.test.ts +154 -154
- package/src/filling/session-sqlite.ts +617 -617
- package/src/filling/sse.ts +5 -5
- package/src/filling/ws.ts +78 -78
- package/src/generator/generate.ts +30 -30
- package/src/generator/index.ts +3 -3
- package/src/generator/templates.test.ts +48 -48
- package/src/generator/templates.ts +219 -219
- package/src/guard/__tests__/design-inline-class.test.ts +219 -219
- package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -347
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/check.ts +39 -24
- package/src/guard/config-guard.ts +13 -13
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/define-rule.ts +243 -243
- package/src/guard/design-inline-class.ts +393 -393
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/fs-routes-policy.ts +51 -51
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- package/src/guard/index.ts +11 -11
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/reporter.ts +442 -442
- package/src/guard/rule-presets.ts +379 -379
- package/src/guard/semantic-slots.ts +1 -1
- package/src/guard/suggestions.ts +358 -358
- package/src/guard/tsgolint-bridge.ts +512 -512
- package/src/guard/types.ts +348 -348
- package/src/guard/watcher.ts +405 -405
- package/src/i18n/define.ts +126 -126
- package/src/i18n/index.ts +52 -52
- package/src/i18n/message-registry.ts +173 -173
- package/src/i18n/types.ts +112 -112
- package/src/id/__tests__/id.test.ts +3 -3
- package/src/id/index.ts +105 -105
- package/src/index.ts +9 -48
- package/src/internal/client-boundary.ts +266 -266
- package/src/internal/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -779
- package/src/kitchen/api/bundle-inspector.ts +179 -0
- package/src/kitchen/api/errors-grouping.ts +126 -126
- package/src/kitchen/api/file-api.ts +11 -11
- package/src/kitchen/kitchen-handler.ts +36 -0
- package/src/kitchen/kitchen-ui.ts +456 -0
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/middleware/bridge.ts +147 -147
- package/src/middleware/compose.ts +134 -134
- package/src/middleware/compress.ts +62 -62
- package/src/middleware/cors.ts +47 -47
- package/src/middleware/csrf.ts +328 -328
- package/src/middleware/define.ts +132 -132
- package/src/middleware/jwt.ts +134 -134
- package/src/middleware/logger.ts +58 -58
- package/src/middleware/oauth/__tests__/oauth.test.ts +1 -1
- package/src/middleware/oauth/index.ts +505 -505
- package/src/middleware/oauth/providers.ts +115 -115
- package/src/middleware/rate-limit/index.ts +522 -522
- package/src/middleware/rate-limit/sqlite-store.ts +382 -382
- package/src/middleware/scheduler-cron.ts +96 -96
- package/src/middleware/secure/__tests__/secure.test.ts +360 -360
- package/src/middleware/secure/csp.ts +193 -193
- package/src/middleware/session.ts +174 -174
- package/src/middleware/timeout.ts +55 -55
- package/src/observability/logger-adapter.ts +36 -36
- package/src/observability/sqlite-store.ts +254 -254
- package/src/openapi/generator.ts +1 -1
- package/src/openapi/openapi.test.ts +43 -43
- package/src/perf/__tests__/user-marks.test.ts +354 -354
- package/src/perf/index.ts +133 -133
- package/src/perf/user-marks.ts +1 -1
- package/src/plugins/__tests__/lifecycle-integration.test.ts +272 -272
- package/src/plugins/__tests__/runner.test.ts +409 -409
- package/src/plugins/define.ts +124 -124
- package/src/plugins/examples/dep-check-plugin.ts +80 -80
- package/src/plugins/examples/prerender-cache-plugin.ts +111 -111
- package/src/plugins/examples/sitemap-plugin.ts +65 -65
- package/src/plugins/runner.ts +361 -361
- package/src/plugins/types.ts +368 -368
- package/src/report/index.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +7 -7
- package/src/resource/__tests__/schema.test.ts +14 -14
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +165 -165
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +146 -146
- package/src/resource/ddl/persistence-types.ts +218 -218
- package/src/resource/ddl/type-map.ts +223 -223
- package/src/resource/ddl/types.ts +232 -232
- package/src/resource/generator-repo.ts +630 -630
- package/src/resource/generator-schema.ts +11 -11
- package/src/resource/generators/slot.ts +72 -72
- package/src/resource/schema.ts +21 -21
- package/src/router/client-entry.test.ts +227 -227
- package/src/router/client-entry.ts +218 -218
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.test.ts +532 -532
- package/src/router/fs-routes.ts +53 -53
- package/src/router/fs-scanner.ts +216 -216
- package/src/router/fs-types.ts +19 -19
- package/src/router/route-source-analyzer.ts +521 -521
- package/src/routes/index.ts +74 -74
- package/src/routes/metadata-routes.ts +427 -427
- package/src/routes/types.ts +341 -341
- package/src/runtime/__tests__/devtools-adapter.test.ts +68 -68
- package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -141
- package/src/runtime/__tests__/hdr-client.test.ts +223 -223
- package/src/runtime/__tests__/http-errors.test.ts +117 -117
- package/src/runtime/__tests__/inline-client-hydration.test.ts +237 -237
- package/src/runtime/__tests__/not-found.test.ts +152 -152
- package/src/runtime/__tests__/observability-lifecycle.test.ts +103 -103
- package/src/runtime/__tests__/page-render-response.test.ts +517 -517
- package/src/runtime/__tests__/request-middleware.test.ts +70 -70
- package/src/runtime/__tests__/searchparams-page-props.test.ts +81 -81
- package/src/runtime/adapter.ts +47 -47
- package/src/runtime/boundary.tsx +252 -252
- package/src/runtime/cache.ts +494 -494
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/devtools-adapter.ts +68 -68
- package/src/runtime/escape.ts +34 -34
- package/src/runtime/fast-refresh-runtime.ts +322 -322
- package/src/runtime/fast-refresh-types.ts +1 -1
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +30 -11
- package/src/runtime/http-errors.ts +113 -113
- package/src/runtime/image-handler.ts +1 -1
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/middleware.ts +264 -264
- package/src/runtime/not-found.ts +93 -93
- package/src/runtime/observability-lifecycle.ts +290 -290
- package/src/runtime/openapi-endpoint.ts +236 -236
- package/src/runtime/page-render-response.ts +287 -274
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/rate-limit.ts +1 -1
- package/src/runtime/redirect.ts +1 -1
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/request-middleware.ts +31 -31
- package/src/runtime/router.test.ts +4 -4
- package/src/runtime/router.ts +10 -10
- package/src/runtime/server.ts +39 -20
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/ssr.ts +43 -17
- package/src/runtime/static-files.ts +369 -289
- package/src/runtime/streaming-ssr.ts +219 -180
- package/src/runtime/trace.ts +144 -144
- package/src/scheduler/__tests__/scheduler.test.ts +12 -11
- package/src/scheduler/index.ts +11 -5
- package/src/scheduler/validate.ts +169 -169
- package/src/seo/index.ts +219 -219
- package/src/seo/integration/ssr.ts +306 -306
- package/src/seo/render/basic.ts +435 -435
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +197 -197
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +127 -127
- package/src/seo/resolve/opengraph.ts +143 -143
- package/src/seo/resolve/robots.ts +73 -73
- package/src/seo/resolve/title.ts +94 -94
- package/src/seo/resolve/twitter.ts +73 -73
- package/src/seo/resolve/url.ts +104 -104
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +588 -588
- package/src/slot/validator.ts +39 -39
- package/src/spec/schema.ts +35 -35
- package/src/storage/s3/__tests__/s3.test.ts +479 -479
- package/src/storage/s3/index.ts +412 -412
- package/src/testing/__tests__/assertions.test.ts +632 -632
- package/src/testing/__tests__/reporter.test.ts +454 -454
- package/src/testing/assertions.ts +986 -986
- package/src/testing/db.ts +157 -157
- package/src/testing/index.ts +9 -3
- package/src/testing/lcov.ts +192 -0
- package/src/testing/mocks.ts +203 -203
- package/src/testing/session.ts +190 -190
- package/src/types/branded.ts +56 -56
- package/src/types/index.ts +1 -1
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/__tests__/watcher.test.ts +59 -59
- package/src/watcher/watcher.ts +61 -61
|
@@ -1,218 +1,218 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 4c — Extended persistence options for `ResourceDefinition`.
|
|
3
|
-
*
|
|
4
|
-
* # Why a separate file (not module augmentation)?
|
|
5
|
-
*
|
|
6
|
-
* Module-augmenting `ResourceOptions.persistence` would pollute the shape
|
|
7
|
-
* of `ResourceDefinition` for every consumer of `@mandujs/core` — including
|
|
8
|
-
* apps that never opt into persistence. The augmentation would also load
|
|
9
|
-
* DDL-specific symbols into the global type graph, which is exactly what
|
|
10
|
-
* the `ddl/` subdirectory isolation is trying to prevent.
|
|
11
|
-
*
|
|
12
|
-
* Instead we keep persistence options as an INDEPENDENT type and narrow at
|
|
13
|
-
* the use site (`snapshotFromResources`). `ResourceDefinition.options.persistence`
|
|
14
|
-
* is typed as `unknown` by the public schema; this module's `asPersistence()`
|
|
15
|
-
* is the single type-check gate. This preserves backward compatibility —
|
|
16
|
-
* existing resource files without `persistence` keep working — while
|
|
17
|
-
* giving the DDL layer a fully-typed view.
|
|
18
|
-
*
|
|
19
|
-
* # Identifier validation
|
|
20
|
-
*
|
|
21
|
-
* All identifier-shaped fields (`tableName`, `fieldOverrides[key].columnName`,
|
|
22
|
-
* `indexes[].name`) are validated against {@link SAFE_PERSISTENCE_IDENTIFIER_RE}
|
|
23
|
-
* at narrowing time. This is defense-in-depth in addition to `quoteIdent`:
|
|
24
|
-
* - `quoteIdent` catches SQL-injection characters (double quotes, backticks,
|
|
25
|
-
* NUL bytes) — but only when the value reaches DDL emission.
|
|
26
|
-
* - The same values ALSO feed `path.join` calls in `writeSchemaArtifacts`
|
|
27
|
-
* (`.mandu/generated/server/schema/{tableName}.sql`) and could otherwise
|
|
28
|
-
* allow path traversal via `..`, `/`, or `\` in the declared name.
|
|
29
|
-
* - Restricting to `[A-Za-z_][A-Za-z0-9_]*` closes both surfaces uniformly
|
|
30
|
-
* and matches the constraint already enforced on `definition.name` by
|
|
31
|
-
* `validateResourceDefinition` in schema.ts.
|
|
32
|
-
*
|
|
33
|
-
* References:
|
|
34
|
-
* - docs/rfcs/0001-db-resource-layer.md §4 D1 (opt-in persistence field)
|
|
35
|
-
* - docs/rfcs/0001-db-resource-layer.md Appendix D.1 (dialect divergence)
|
|
36
|
-
* - docs/security/phase-4c-audit.md §H-01 (path traversal remediation)
|
|
37
|
-
* - packages/core/src/resource/ddl/types.ts (canonical DDL contract)
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
import type { SqlProvider, DdlDefault, DdlIndex } from "./types";
|
|
41
|
-
|
|
42
|
-
// ============================================
|
|
43
|
-
// Identifier validation
|
|
44
|
-
// ============================================
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* The set of names allowed for DDL identifiers that originate from
|
|
48
|
-
* user-authored resource options (`tableName`, `columnName`, index
|
|
49
|
-
* `name`). Starts with a letter or underscore, followed by letters,
|
|
50
|
-
* digits, or underscores — i.e. the portable SQL identifier subset
|
|
51
|
-
* that is also safe to interpolate into a filesystem path segment.
|
|
52
|
-
*
|
|
53
|
-
* Rejects:
|
|
54
|
-
* - path separators (`/`, `\`) — prevents path traversal via
|
|
55
|
-
* `writeSchemaArtifacts` which writes `{tableName}.sql`.
|
|
56
|
-
* - `..`, `.` — parent/current directory markers.
|
|
57
|
-
* - whitespace and control characters — break both filesystems and
|
|
58
|
-
* terminal rendering in CLI output.
|
|
59
|
-
* - quote characters — redundant with `quoteIdent` but cheaper to
|
|
60
|
-
* reject early than per-dialect at emit time.
|
|
61
|
-
*
|
|
62
|
-
* Tightness rationale: we prefer a whitelist over a blacklist because
|
|
63
|
-
* OS + dialect quoting behavior varies; a whitelist aligns the two and
|
|
64
|
-
* stays simple to reason about.
|
|
65
|
-
*/
|
|
66
|
-
export const SAFE_PERSISTENCE_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Upper bound — mirrors `emit.ts:MAX_IDENT_LENGTH` so DDL identifiers
|
|
70
|
-
* never grow past the tighter of PG (63) / MySQL (64) limits. Enforcing
|
|
71
|
-
* it here gives a clearer error than the downstream emit-time throw.
|
|
72
|
-
*/
|
|
73
|
-
const MAX_PERSISTENCE_IDENTIFIER_LENGTH = 63;
|
|
74
|
-
|
|
75
|
-
function assertSafeIdentifier(kind: string, value: string): void {
|
|
76
|
-
if (value.length === 0) {
|
|
77
|
-
throw new TypeError(`options.persistence.${kind} must not be empty`);
|
|
78
|
-
}
|
|
79
|
-
if (value.length > MAX_PERSISTENCE_IDENTIFIER_LENGTH) {
|
|
80
|
-
throw new TypeError(
|
|
81
|
-
`options.persistence.${kind} too long (${value.length} > ${MAX_PERSISTENCE_IDENTIFIER_LENGTH}): ${value.slice(0, 32)}...`
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
if (!SAFE_PERSISTENCE_IDENTIFIER_RE.test(value)) {
|
|
85
|
-
throw new TypeError(
|
|
86
|
-
`options.persistence.${kind} ${JSON.stringify(value)} contains characters outside [A-Za-z0-9_] or does not start with a letter/underscore. ` +
|
|
87
|
-
`This restriction blocks SQL injection and path traversal uniformly; use ${SAFE_PERSISTENCE_IDENTIFIER_RE} to construct the name.`
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// ============================================
|
|
93
|
-
// Extended persistence options — opt-in
|
|
94
|
-
// ============================================
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Per-field override block. Merged on top of values derived from
|
|
98
|
-
* `ResourceField` by `snapshotFromResources`. All fields are optional.
|
|
99
|
-
*
|
|
100
|
-
* `columnName` beats the default `camelCase → snake_case` transform.
|
|
101
|
-
* `nullable` beats `!field.required`.
|
|
102
|
-
* `default` beats `field.default` (and bypasses its string-magic parsing).
|
|
103
|
-
* `maxLength` is used when the Mandu field type is `string`.
|
|
104
|
-
*/
|
|
105
|
-
export interface FieldOverride {
|
|
106
|
-
/** Explicit column name override. Must match `/^[a-z_][a-z0-9_]*$/i`. */
|
|
107
|
-
columnName?: string;
|
|
108
|
-
/** Explicit NULL allowance. */
|
|
109
|
-
nullable?: boolean;
|
|
110
|
-
/** Explicit DEFAULT clause (bypasses `field.default` string magic). */
|
|
111
|
-
default?: DdlDefault;
|
|
112
|
-
/** VARCHAR length for `string` fields. Ignored for other types. */
|
|
113
|
-
maxLength?: number;
|
|
114
|
-
/** Mark as indexed (single-column, non-unique). */
|
|
115
|
-
indexed?: boolean;
|
|
116
|
-
/** Mark as UNIQUE. */
|
|
117
|
-
unique?: boolean;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Persistence block on a resource definition — opt-in to DDL/migration.
|
|
122
|
-
* Resources without this field are ignored by the diff engine.
|
|
123
|
-
*/
|
|
124
|
-
export interface ExtendedResourcePersistence {
|
|
125
|
-
/** Which SQL provider this resource targets. Must be consistent across the project. */
|
|
126
|
-
provider: SqlProvider;
|
|
127
|
-
/** Explicit table name. Overrides auto-pluralization of `resource.name`. */
|
|
128
|
-
tableName?: string;
|
|
129
|
-
/**
|
|
130
|
-
* Primary key field key. Can be a single string (v1) or a 1-element array
|
|
131
|
-
* (future-compatible). Multi-element arrays are rejected — composite keys
|
|
132
|
-
* are v2+.
|
|
133
|
-
*/
|
|
134
|
-
primaryKey?: string | [string];
|
|
135
|
-
/** Multi-column indexes. Single-column indexes live on the field itself. */
|
|
136
|
-
indexes?: DdlIndex[];
|
|
137
|
-
/** Per-field overrides keyed by the Mandu field name. */
|
|
138
|
-
fieldOverrides?: Record<string, FieldOverride>;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ============================================
|
|
142
|
-
// Safe narrowing
|
|
143
|
-
// ============================================
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Narrow `unknown` (the public schema type for `options.persistence`) to
|
|
147
|
-
* `ExtendedResourcePersistence`. Returns `undefined` when the value is
|
|
148
|
-
* missing or empty. Throws `TypeError` on structurally-broken objects OR
|
|
149
|
-
* on identifier-shaped fields that contain SQL-injection / path-traversal
|
|
150
|
-
* characters (see {@link SAFE_PERSISTENCE_IDENTIFIER_RE}).
|
|
151
|
-
*
|
|
152
|
-
* This is the ONLY place the DDL layer trusts the shape of the persistence
|
|
153
|
-
* block; downstream code never sees `unknown`.
|
|
154
|
-
*/
|
|
155
|
-
export function asPersistence(raw: unknown): ExtendedResourcePersistence | undefined {
|
|
156
|
-
if (raw === undefined || raw === null) return undefined;
|
|
157
|
-
if (typeof raw !== "object") {
|
|
158
|
-
throw new TypeError(`options.persistence must be an object, got ${typeof raw}`);
|
|
159
|
-
}
|
|
160
|
-
const obj = raw as Record<string, unknown>;
|
|
161
|
-
const provider = obj.provider;
|
|
162
|
-
if (provider !== "postgres" && provider !== "mysql" && provider !== "sqlite") {
|
|
163
|
-
throw new TypeError(
|
|
164
|
-
`options.persistence.provider must be one of "postgres" | "mysql" | "sqlite", got ${JSON.stringify(provider)}`
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
if (obj.tableName !== undefined) {
|
|
168
|
-
if (typeof obj.tableName !== "string") {
|
|
169
|
-
throw new TypeError(`options.persistence.tableName must be a string`);
|
|
170
|
-
}
|
|
171
|
-
assertSafeIdentifier("tableName", obj.tableName);
|
|
172
|
-
}
|
|
173
|
-
if (obj.primaryKey !== undefined) {
|
|
174
|
-
const pk = obj.primaryKey;
|
|
175
|
-
if (
|
|
176
|
-
typeof pk !== "string" &&
|
|
177
|
-
!(Array.isArray(pk) && pk.length === 1 && typeof pk[0] === "string")
|
|
178
|
-
) {
|
|
179
|
-
throw new TypeError(
|
|
180
|
-
`options.persistence.primaryKey must be a string or single-element string array (composite keys are v2)`
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
if (obj.indexes !== undefined) {
|
|
185
|
-
if (!Array.isArray(obj.indexes)) {
|
|
186
|
-
throw new TypeError(`options.persistence.indexes must be an array`);
|
|
187
|
-
}
|
|
188
|
-
for (let i = 0; i < obj.indexes.length; i++) {
|
|
189
|
-
const idx = obj.indexes[i] as { name?: unknown } | undefined;
|
|
190
|
-
if (idx && typeof idx === "object" && typeof idx.name === "string") {
|
|
191
|
-
assertSafeIdentifier(`indexes[${i}].name`, idx.name);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
if (obj.fieldOverrides !== undefined) {
|
|
196
|
-
if (typeof obj.fieldOverrides !== "object" || obj.fieldOverrides === null || Array.isArray(obj.fieldOverrides)) {
|
|
197
|
-
throw new TypeError(`options.persistence.fieldOverrides must be an object`);
|
|
198
|
-
}
|
|
199
|
-
for (const [key, value] of Object.entries(obj.fieldOverrides as Record<string, unknown>)) {
|
|
200
|
-
if (value === undefined || value === null) continue;
|
|
201
|
-
if (typeof value !== "object" || Array.isArray(value)) {
|
|
202
|
-
throw new TypeError(
|
|
203
|
-
`options.persistence.fieldOverrides.${key} must be an object`
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
const col = (value as { columnName?: unknown }).columnName;
|
|
207
|
-
if (col !== undefined) {
|
|
208
|
-
if (typeof col !== "string") {
|
|
209
|
-
throw new TypeError(
|
|
210
|
-
`options.persistence.fieldOverrides.${key}.columnName must be a string`
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
assertSafeIdentifier(`fieldOverrides.${key}.columnName`, col);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
return obj as unknown as ExtendedResourcePersistence;
|
|
218
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Phase 4c — Extended persistence options for `ResourceDefinition`.
|
|
3
|
+
*
|
|
4
|
+
* # Why a separate file (not module augmentation)?
|
|
5
|
+
*
|
|
6
|
+
* Module-augmenting `ResourceOptions.persistence` would pollute the shape
|
|
7
|
+
* of `ResourceDefinition` for every consumer of `@mandujs/core` — including
|
|
8
|
+
* apps that never opt into persistence. The augmentation would also load
|
|
9
|
+
* DDL-specific symbols into the global type graph, which is exactly what
|
|
10
|
+
* the `ddl/` subdirectory isolation is trying to prevent.
|
|
11
|
+
*
|
|
12
|
+
* Instead we keep persistence options as an INDEPENDENT type and narrow at
|
|
13
|
+
* the use site (`snapshotFromResources`). `ResourceDefinition.options.persistence`
|
|
14
|
+
* is typed as `unknown` by the public schema; this module's `asPersistence()`
|
|
15
|
+
* is the single type-check gate. This preserves backward compatibility —
|
|
16
|
+
* existing resource files without `persistence` keep working — while
|
|
17
|
+
* giving the DDL layer a fully-typed view.
|
|
18
|
+
*
|
|
19
|
+
* # Identifier validation
|
|
20
|
+
*
|
|
21
|
+
* All identifier-shaped fields (`tableName`, `fieldOverrides[key].columnName`,
|
|
22
|
+
* `indexes[].name`) are validated against {@link SAFE_PERSISTENCE_IDENTIFIER_RE}
|
|
23
|
+
* at narrowing time. This is defense-in-depth in addition to `quoteIdent`:
|
|
24
|
+
* - `quoteIdent` catches SQL-injection characters (double quotes, backticks,
|
|
25
|
+
* NUL bytes) — but only when the value reaches DDL emission.
|
|
26
|
+
* - The same values ALSO feed `path.join` calls in `writeSchemaArtifacts`
|
|
27
|
+
* (`.mandu/generated/server/schema/{tableName}.sql`) and could otherwise
|
|
28
|
+
* allow path traversal via `..`, `/`, or `\` in the declared name.
|
|
29
|
+
* - Restricting to `[A-Za-z_][A-Za-z0-9_]*` closes both surfaces uniformly
|
|
30
|
+
* and matches the constraint already enforced on `definition.name` by
|
|
31
|
+
* `validateResourceDefinition` in schema.ts.
|
|
32
|
+
*
|
|
33
|
+
* References:
|
|
34
|
+
* - docs/rfcs/0001-db-resource-layer.md §4 D1 (opt-in persistence field)
|
|
35
|
+
* - docs/rfcs/0001-db-resource-layer.md Appendix D.1 (dialect divergence)
|
|
36
|
+
* - docs/security/phase-4c-audit.md §H-01 (path traversal remediation)
|
|
37
|
+
* - packages/core/src/resource/ddl/types.ts (canonical DDL contract)
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import type { SqlProvider, DdlDefault, DdlIndex } from "./types";
|
|
41
|
+
|
|
42
|
+
// ============================================
|
|
43
|
+
// Identifier validation
|
|
44
|
+
// ============================================
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The set of names allowed for DDL identifiers that originate from
|
|
48
|
+
* user-authored resource options (`tableName`, `columnName`, index
|
|
49
|
+
* `name`). Starts with a letter or underscore, followed by letters,
|
|
50
|
+
* digits, or underscores — i.e. the portable SQL identifier subset
|
|
51
|
+
* that is also safe to interpolate into a filesystem path segment.
|
|
52
|
+
*
|
|
53
|
+
* Rejects:
|
|
54
|
+
* - path separators (`/`, `\`) — prevents path traversal via
|
|
55
|
+
* `writeSchemaArtifacts` which writes `{tableName}.sql`.
|
|
56
|
+
* - `..`, `.` — parent/current directory markers.
|
|
57
|
+
* - whitespace and control characters — break both filesystems and
|
|
58
|
+
* terminal rendering in CLI output.
|
|
59
|
+
* - quote characters — redundant with `quoteIdent` but cheaper to
|
|
60
|
+
* reject early than per-dialect at emit time.
|
|
61
|
+
*
|
|
62
|
+
* Tightness rationale: we prefer a whitelist over a blacklist because
|
|
63
|
+
* OS + dialect quoting behavior varies; a whitelist aligns the two and
|
|
64
|
+
* stays simple to reason about.
|
|
65
|
+
*/
|
|
66
|
+
export const SAFE_PERSISTENCE_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Upper bound — mirrors `emit.ts:MAX_IDENT_LENGTH` so DDL identifiers
|
|
70
|
+
* never grow past the tighter of PG (63) / MySQL (64) limits. Enforcing
|
|
71
|
+
* it here gives a clearer error than the downstream emit-time throw.
|
|
72
|
+
*/
|
|
73
|
+
const MAX_PERSISTENCE_IDENTIFIER_LENGTH = 63;
|
|
74
|
+
|
|
75
|
+
function assertSafeIdentifier(kind: string, value: string): void {
|
|
76
|
+
if (value.length === 0) {
|
|
77
|
+
throw new TypeError(`options.persistence.${kind} must not be empty`);
|
|
78
|
+
}
|
|
79
|
+
if (value.length > MAX_PERSISTENCE_IDENTIFIER_LENGTH) {
|
|
80
|
+
throw new TypeError(
|
|
81
|
+
`options.persistence.${kind} too long (${value.length} > ${MAX_PERSISTENCE_IDENTIFIER_LENGTH}): ${value.slice(0, 32)}...`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (!SAFE_PERSISTENCE_IDENTIFIER_RE.test(value)) {
|
|
85
|
+
throw new TypeError(
|
|
86
|
+
`options.persistence.${kind} ${JSON.stringify(value)} contains characters outside [A-Za-z0-9_] or does not start with a letter/underscore. ` +
|
|
87
|
+
`This restriction blocks SQL injection and path traversal uniformly; use ${SAFE_PERSISTENCE_IDENTIFIER_RE} to construct the name.`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ============================================
|
|
93
|
+
// Extended persistence options — opt-in
|
|
94
|
+
// ============================================
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Per-field override block. Merged on top of values derived from
|
|
98
|
+
* `ResourceField` by `snapshotFromResources`. All fields are optional.
|
|
99
|
+
*
|
|
100
|
+
* `columnName` beats the default `camelCase → snake_case` transform.
|
|
101
|
+
* `nullable` beats `!field.required`.
|
|
102
|
+
* `default` beats `field.default` (and bypasses its string-magic parsing).
|
|
103
|
+
* `maxLength` is used when the Mandu field type is `string`.
|
|
104
|
+
*/
|
|
105
|
+
export interface FieldOverride {
|
|
106
|
+
/** Explicit column name override. Must match `/^[a-z_][a-z0-9_]*$/i`. */
|
|
107
|
+
columnName?: string;
|
|
108
|
+
/** Explicit NULL allowance. */
|
|
109
|
+
nullable?: boolean;
|
|
110
|
+
/** Explicit DEFAULT clause (bypasses `field.default` string magic). */
|
|
111
|
+
default?: DdlDefault;
|
|
112
|
+
/** VARCHAR length for `string` fields. Ignored for other types. */
|
|
113
|
+
maxLength?: number;
|
|
114
|
+
/** Mark as indexed (single-column, non-unique). */
|
|
115
|
+
indexed?: boolean;
|
|
116
|
+
/** Mark as UNIQUE. */
|
|
117
|
+
unique?: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Persistence block on a resource definition — opt-in to DDL/migration.
|
|
122
|
+
* Resources without this field are ignored by the diff engine.
|
|
123
|
+
*/
|
|
124
|
+
export interface ExtendedResourcePersistence {
|
|
125
|
+
/** Which SQL provider this resource targets. Must be consistent across the project. */
|
|
126
|
+
provider: SqlProvider;
|
|
127
|
+
/** Explicit table name. Overrides auto-pluralization of `resource.name`. */
|
|
128
|
+
tableName?: string;
|
|
129
|
+
/**
|
|
130
|
+
* Primary key field key. Can be a single string (v1) or a 1-element array
|
|
131
|
+
* (future-compatible). Multi-element arrays are rejected — composite keys
|
|
132
|
+
* are v2+.
|
|
133
|
+
*/
|
|
134
|
+
primaryKey?: string | [string];
|
|
135
|
+
/** Multi-column indexes. Single-column indexes live on the field itself. */
|
|
136
|
+
indexes?: DdlIndex[];
|
|
137
|
+
/** Per-field overrides keyed by the Mandu field name. */
|
|
138
|
+
fieldOverrides?: Record<string, FieldOverride>;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ============================================
|
|
142
|
+
// Safe narrowing
|
|
143
|
+
// ============================================
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Narrow `unknown` (the public schema type for `options.persistence`) to
|
|
147
|
+
* `ExtendedResourcePersistence`. Returns `undefined` when the value is
|
|
148
|
+
* missing or empty. Throws `TypeError` on structurally-broken objects OR
|
|
149
|
+
* on identifier-shaped fields that contain SQL-injection / path-traversal
|
|
150
|
+
* characters (see {@link SAFE_PERSISTENCE_IDENTIFIER_RE}).
|
|
151
|
+
*
|
|
152
|
+
* This is the ONLY place the DDL layer trusts the shape of the persistence
|
|
153
|
+
* block; downstream code never sees `unknown`.
|
|
154
|
+
*/
|
|
155
|
+
export function asPersistence(raw: unknown): ExtendedResourcePersistence | undefined {
|
|
156
|
+
if (raw === undefined || raw === null) return undefined;
|
|
157
|
+
if (typeof raw !== "object") {
|
|
158
|
+
throw new TypeError(`options.persistence must be an object, got ${typeof raw}`);
|
|
159
|
+
}
|
|
160
|
+
const obj = raw as Record<string, unknown>;
|
|
161
|
+
const provider = obj.provider;
|
|
162
|
+
if (provider !== "postgres" && provider !== "mysql" && provider !== "sqlite") {
|
|
163
|
+
throw new TypeError(
|
|
164
|
+
`options.persistence.provider must be one of "postgres" | "mysql" | "sqlite", got ${JSON.stringify(provider)}`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
if (obj.tableName !== undefined) {
|
|
168
|
+
if (typeof obj.tableName !== "string") {
|
|
169
|
+
throw new TypeError(`options.persistence.tableName must be a string`);
|
|
170
|
+
}
|
|
171
|
+
assertSafeIdentifier("tableName", obj.tableName);
|
|
172
|
+
}
|
|
173
|
+
if (obj.primaryKey !== undefined) {
|
|
174
|
+
const pk = obj.primaryKey;
|
|
175
|
+
if (
|
|
176
|
+
typeof pk !== "string" &&
|
|
177
|
+
!(Array.isArray(pk) && pk.length === 1 && typeof pk[0] === "string")
|
|
178
|
+
) {
|
|
179
|
+
throw new TypeError(
|
|
180
|
+
`options.persistence.primaryKey must be a string or single-element string array (composite keys are v2)`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (obj.indexes !== undefined) {
|
|
185
|
+
if (!Array.isArray(obj.indexes)) {
|
|
186
|
+
throw new TypeError(`options.persistence.indexes must be an array`);
|
|
187
|
+
}
|
|
188
|
+
for (let i = 0; i < obj.indexes.length; i++) {
|
|
189
|
+
const idx = obj.indexes[i] as { name?: unknown } | undefined;
|
|
190
|
+
if (idx && typeof idx === "object" && typeof idx.name === "string") {
|
|
191
|
+
assertSafeIdentifier(`indexes[${i}].name`, idx.name);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (obj.fieldOverrides !== undefined) {
|
|
196
|
+
if (typeof obj.fieldOverrides !== "object" || obj.fieldOverrides === null || Array.isArray(obj.fieldOverrides)) {
|
|
197
|
+
throw new TypeError(`options.persistence.fieldOverrides must be an object`);
|
|
198
|
+
}
|
|
199
|
+
for (const [key, value] of Object.entries(obj.fieldOverrides as Record<string, unknown>)) {
|
|
200
|
+
if (value === undefined || value === null) continue;
|
|
201
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
202
|
+
throw new TypeError(
|
|
203
|
+
`options.persistence.fieldOverrides.${key} must be an object`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
const col = (value as { columnName?: unknown }).columnName;
|
|
207
|
+
if (col !== undefined) {
|
|
208
|
+
if (typeof col !== "string") {
|
|
209
|
+
throw new TypeError(
|
|
210
|
+
`options.persistence.fieldOverrides.${key}.columnName must be a string`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
assertSafeIdentifier(`fieldOverrides.${key}.columnName`, col);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return obj as unknown as ExtendedResourcePersistence;
|
|
218
|
+
}
|