@async/db 0.2.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/CHANGELOG.md +167 -0
- package/README.md +431 -0
- package/SPEC.md +1429 -0
- package/db.config.example.mjs +128 -0
- package/dist/cli/args.d.ts +8 -0
- package/dist/cli/args.js +16 -0
- package/dist/cli/commands/create.d.ts +3 -0
- package/dist/cli/commands/create.js +13 -0
- package/dist/cli/commands/doctor.d.ts +3 -0
- package/dist/cli/commands/doctor.js +31 -0
- package/dist/cli/commands/generate.d.ts +6 -0
- package/dist/cli/commands/generate.js +24 -0
- package/dist/cli/commands/operations.d.ts +12 -0
- package/dist/cli/commands/operations.js +61 -0
- package/dist/cli/commands/schema.d.ts +11 -0
- package/dist/cli/commands/schema.js +1086 -0
- package/dist/cli/commands/serve.d.ts +9 -0
- package/dist/cli/commands/serve.js +18 -0
- package/dist/cli/commands/sync.d.ts +3 -0
- package/dist/cli/commands/sync.js +11 -0
- package/dist/cli/commands/types.d.ts +7 -0
- package/dist/cli/commands/types.js +37 -0
- package/dist/cli/commands/viewer.d.ts +6 -0
- package/dist/cli/commands/viewer.js +29 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +108 -0
- package/dist/cli/output.d.ts +25 -0
- package/dist/cli/output.js +149 -0
- package/dist/cli/schema-prompt.d.ts +20 -0
- package/dist/cli/schema-prompt.js +66 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +3 -0
- package/dist/client-cache.d.ts +105 -0
- package/dist/client-cache.js +916 -0
- package/dist/client.d.ts +64 -0
- package/dist/client.js +405 -0
- package/dist/config-public.d.ts +1 -0
- package/dist/config-public.js +1 -0
- package/dist/config.d.ts +54 -0
- package/dist/config.js +2 -0
- package/dist/csv.d.ts +1 -0
- package/dist/csv.js +1 -0
- package/dist/db.d.ts +3 -0
- package/dist/db.js +3 -0
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +1 -0
- package/dist/errors.d.ts +1 -0
- package/dist/errors.js +1 -0
- package/dist/features/config/defaults.d.ts +98 -0
- package/dist/features/config/defaults.js +95 -0
- package/dist/features/config/load.d.ts +11 -0
- package/dist/features/config/load.js +265 -0
- package/dist/features/config/public.d.ts +17 -0
- package/dist/features/config/public.js +75 -0
- package/dist/features/doctor/duplicate-ids.d.ts +18 -0
- package/dist/features/doctor/duplicate-ids.js +79 -0
- package/dist/features/doctor/field-consistency.d.ts +17 -0
- package/dist/features/doctor/field-consistency.js +48 -0
- package/dist/features/doctor/index.d.ts +39 -0
- package/dist/features/doctor/index.js +177 -0
- package/dist/features/doctor/relations.d.ts +22 -0
- package/dist/features/doctor/relations.js +90 -0
- package/dist/features/doctor/schema-guidance.d.ts +35 -0
- package/dist/features/doctor/schema-guidance.js +184 -0
- package/dist/features/generate/registry.d.ts +14 -0
- package/dist/features/generate/registry.js +37 -0
- package/dist/features/http/registry.d.ts +46 -0
- package/dist/features/http/registry.js +86 -0
- package/dist/features/operations/index.d.ts +49 -0
- package/dist/features/operations/index.js +199 -0
- package/dist/features/operations/maps.d.ts +1 -0
- package/dist/features/operations/maps.js +10 -0
- package/dist/features/operations/readiness.d.ts +30 -0
- package/dist/features/operations/readiness.js +228 -0
- package/dist/features/operations/runtime.d.ts +57 -0
- package/dist/features/operations/runtime.js +288 -0
- package/dist/features/runtime/collection.d.ts +51 -0
- package/dist/features/runtime/collection.js +198 -0
- package/dist/features/runtime/db.d.ts +152 -0
- package/dist/features/runtime/db.js +824 -0
- package/dist/features/runtime/document.d.ts +43 -0
- package/dist/features/runtime/document.js +111 -0
- package/dist/features/runtime/fanout.d.ts +24 -0
- package/dist/features/runtime/fanout.js +77 -0
- package/dist/features/runtime/json-pointer.d.ts +5 -0
- package/dist/features/runtime/json-pointer.js +49 -0
- package/dist/features/runtime/scope-state.d.ts +44 -0
- package/dist/features/runtime/scope-state.js +185 -0
- package/dist/features/runtime/state.d.ts +1 -0
- package/dist/features/runtime/state.js +1 -0
- package/dist/features/schema/api.d.ts +107 -0
- package/dist/features/schema/api.js +460 -0
- package/dist/features/schema/builders.d.ts +86 -0
- package/dist/features/schema/builders.js +110 -0
- package/dist/features/schema/fields.d.ts +38 -0
- package/dist/features/schema/fields.js +296 -0
- package/dist/features/schema/generated.d.ts +29 -0
- package/dist/features/schema/generated.js +32 -0
- package/dist/features/schema/locator.d.ts +16 -0
- package/dist/features/schema/locator.js +135 -0
- package/dist/features/schema/manifest.d.ts +91 -0
- package/dist/features/schema/manifest.js +384 -0
- package/dist/features/schema/metadata.d.ts +30 -0
- package/dist/features/schema/metadata.js +75 -0
- package/dist/features/schema/project.d.ts +46 -0
- package/dist/features/schema/project.js +442 -0
- package/dist/features/schema/relations.d.ts +38 -0
- package/dist/features/schema/relations.js +109 -0
- package/dist/features/schema/resolvers.d.ts +36 -0
- package/dist/features/schema/resolvers.js +111 -0
- package/dist/features/schema/resource.d.ts +75 -0
- package/dist/features/schema/resource.js +253 -0
- package/dist/features/schema/source-definitions.d.ts +21 -0
- package/dist/features/schema/source-definitions.js +29 -0
- package/dist/features/schema/sources.d.ts +83 -0
- package/dist/features/schema/sources.js +689 -0
- package/dist/features/schema/standard-schema.d.ts +57 -0
- package/dist/features/schema/standard-schema.js +232 -0
- package/dist/features/schema/validation.d.ts +69 -0
- package/dist/features/schema/validation.js +434 -0
- package/dist/features/storage/events.d.ts +12 -0
- package/dist/features/storage/events.js +30 -0
- package/dist/features/storage/json.d.ts +112 -0
- package/dist/features/storage/json.js +239 -0
- package/dist/features/storage/memory.d.ts +30 -0
- package/dist/features/storage/memory.js +44 -0
- package/dist/features/storage/resource-json.d.ts +31 -0
- package/dist/features/storage/resource-json.js +76 -0
- package/dist/features/storage/runtime.d.ts +37 -0
- package/dist/features/storage/runtime.js +184 -0
- package/dist/features/storage/source-metadata.d.ts +20 -0
- package/dist/features/storage/source-metadata.js +25 -0
- package/dist/features/storage/source.d.ts +37 -0
- package/dist/features/storage/source.js +60 -0
- package/dist/features/storage/static.d.ts +29 -0
- package/dist/features/storage/static.js +42 -0
- package/dist/features/sync/defaults.d.ts +21 -0
- package/dist/features/sync/defaults.js +21 -0
- package/dist/features/sync/index.d.ts +35 -0
- package/dist/features/sync/index.js +85 -0
- package/dist/features/sync/mirror-state.d.ts +14 -0
- package/dist/features/sync/mirror-state.js +4 -0
- package/dist/features/sync/runtime-dirs.d.ts +5 -0
- package/dist/features/sync/runtime-dirs.js +9 -0
- package/dist/features/sync/source-writes.d.ts +15 -0
- package/dist/features/sync/source-writes.js +27 -0
- package/dist/features/sync/synthetic-seed.d.ts +26 -0
- package/dist/features/sync/synthetic-seed.js +83 -0
- package/dist/features/viewer/manifest.d.ts +148 -0
- package/dist/features/viewer/manifest.js +165 -0
- package/dist/fs-utils.d.ts +1 -0
- package/dist/fs-utils.js +1 -0
- package/dist/generate/hono/app.d.ts +6 -0
- package/dist/generate/hono/app.js +51 -0
- package/dist/generate/hono/graphql.d.ts +7 -0
- package/dist/generate/hono/graphql.js +53 -0
- package/dist/generate/hono/index.d.ts +55 -0
- package/dist/generate/hono/index.js +140 -0
- package/dist/generate/hono/package.d.ts +6 -0
- package/dist/generate/hono/package.js +44 -0
- package/dist/generate/hono/readme.d.ts +13 -0
- package/dist/generate/hono/readme.js +28 -0
- package/dist/generate/hono/repository.d.ts +1 -0
- package/dist/generate/hono/repository.js +27 -0
- package/dist/generate/hono/rest.d.ts +1 -0
- package/dist/generate/hono/rest.js +38 -0
- package/dist/generate/hono/schema.d.ts +13 -0
- package/dist/generate/hono/schema.js +18 -0
- package/dist/generate/hono/sqlite.d.ts +20 -0
- package/dist/generate/hono/sqlite.js +266 -0
- package/dist/generate/hono/validators.d.ts +1 -0
- package/dist/generate/hono/validators.js +141 -0
- package/dist/generate/hono.d.ts +1 -0
- package/dist/generate/hono.js +1 -0
- package/dist/graphql/execute.d.ts +14 -0
- package/dist/graphql/execute.js +719 -0
- package/dist/graphql/http.d.ts +15 -0
- package/dist/graphql/http.js +29 -0
- package/dist/graphql/index.d.ts +3 -0
- package/dist/graphql/index.js +3 -0
- package/dist/graphql/parser.d.ts +54 -0
- package/dist/graphql/parser.js +433 -0
- package/dist/hono.d.ts +77 -0
- package/dist/hono.js +1 -0
- package/dist/index.d.ts +1065 -0
- package/dist/index.js +14 -0
- package/dist/integrations/hono.d.ts +136 -0
- package/dist/integrations/hono.js +508 -0
- package/dist/integrations/kv.d.ts +69 -0
- package/dist/integrations/kv.js +69 -0
- package/dist/integrations/postgres.d.ts +52 -0
- package/dist/integrations/postgres.js +113 -0
- package/dist/integrations/sqlite.d.ts +112 -0
- package/dist/integrations/sqlite.js +489 -0
- package/dist/integrations/vite.d.ts +45 -0
- package/dist/integrations/vite.js +111 -0
- package/dist/json.d.ts +48 -0
- package/dist/json.js +1 -0
- package/dist/jsonc.d.ts +1 -0
- package/dist/jsonc.js +1 -0
- package/dist/kv.d.ts +24 -0
- package/dist/kv.js +1 -0
- package/dist/mock.d.ts +1 -0
- package/dist/mock.js +1 -0
- package/dist/names.d.ts +1 -0
- package/dist/names.js +1 -0
- package/dist/operations.d.ts +3 -0
- package/dist/operations.js +3 -0
- package/dist/postgres.d.ts +24 -0
- package/dist/postgres.js +1 -0
- package/dist/redis.d.ts +14 -0
- package/dist/redis.js +1 -0
- package/dist/rest/formats.d.ts +80 -0
- package/dist/rest/formats.js +318 -0
- package/dist/rest/handler.d.ts +111 -0
- package/dist/rest/handler.js +833 -0
- package/dist/rest/shape.d.ts +33 -0
- package/dist/rest/shape.js +218 -0
- package/dist/schema-builders.d.ts +1 -0
- package/dist/schema-builders.js +1 -0
- package/dist/schema-manifest.d.ts +1 -0
- package/dist/schema-manifest.js +1 -0
- package/dist/schema.d.ts +193 -0
- package/dist/schema.js +6 -0
- package/dist/server.d.ts +116 -0
- package/dist/server.js +601 -0
- package/dist/shared/csv.d.ts +8 -0
- package/dist/shared/csv.js +149 -0
- package/dist/shared/errors.d.ts +40 -0
- package/dist/shared/errors.js +55 -0
- package/dist/shared/fs-utils.d.ts +4 -0
- package/dist/shared/fs-utils.js +30 -0
- package/dist/shared/jsonc.d.ts +2 -0
- package/dist/shared/jsonc.js +99 -0
- package/dist/shared/mock.d.ts +40 -0
- package/dist/shared/mock.js +83 -0
- package/dist/shared/names.d.ts +28 -0
- package/dist/shared/names.js +127 -0
- package/dist/shared/operations.d.ts +32 -0
- package/dist/shared/operations.js +302 -0
- package/dist/sqlite.d.ts +24 -0
- package/dist/sqlite.js +1 -0
- package/dist/state.d.ts +1 -0
- package/dist/state.js +1 -0
- package/dist/sync.d.ts +1 -0
- package/dist/sync.js +1 -0
- package/dist/tracing.d.ts +95 -0
- package/dist/tracing.js +260 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.js +285 -0
- package/dist/viewer-manifest.d.ts +1 -0
- package/dist/viewer-manifest.js +1 -0
- package/dist/vite.d.ts +59 -0
- package/dist/vite.js +1 -0
- package/dist/web/json-viewer.d.ts +5 -0
- package/dist/web/json-viewer.js +176 -0
- package/dist/web/viewer.d.ts +12 -0
- package/dist/web/viewer.js +1015 -0
- package/docs/README.md +42 -0
- package/docs/architecture.md +112 -0
- package/docs/ci-and-release.md +177 -0
- package/docs/cms-storage-patterns.md +108 -0
- package/docs/concepts.md +141 -0
- package/docs/configuration.md +552 -0
- package/docs/fixtures-and-schemas.md +527 -0
- package/docs/fork-branch-workflows.md +108 -0
- package/docs/generated-files.md +174 -0
- package/docs/getting-started.md +165 -0
- package/docs/integrations.md +206 -0
- package/docs/json-production.md +120 -0
- package/docs/package-api.md +418 -0
- package/docs/prototype-to-production.md +378 -0
- package/docs/server-and-viewer.md +466 -0
- package/docs/store-graduation.md +120 -0
- package/docs/typescript-schema-sources.md +79 -0
- package/examples/advanced/README.md +55 -0
- package/examples/advanced/db/projects.schema.jsonc +44 -0
- package/examples/advanced/db/settings.jsonc +9 -0
- package/examples/advanced/db/users.json +23 -0
- package/examples/advanced/db/users.schema.mjs +31 -0
- package/examples/advanced/db.config.mjs +18 -0
- package/examples/advanced/example.json +5 -0
- package/examples/advanced/src/generated/db.types.d.ts +64 -0
- package/examples/basic/README.md +95 -0
- package/examples/basic/db/operations/get-user.jsonc +8 -0
- package/examples/basic/db/settings.json +7 -0
- package/examples/basic/db/users.schema.jsonc +36 -0
- package/examples/basic/db.config.mjs +68 -0
- package/examples/basic/example.json +5 -0
- package/examples/basic/src/generated/db.types.d.ts +39 -0
- package/examples/cms-json-publish/README.md +21 -0
- package/examples/cms-json-publish/db/navigation.json +7 -0
- package/examples/cms-json-publish/db/pages.json +18 -0
- package/examples/cms-json-publish/example.json +5 -0
- package/examples/cms-json-publish/src/cms.mjs +104 -0
- package/examples/computed-fields/README.md +93 -0
- package/examples/computed-fields/db/orders.schema.mjs +62 -0
- package/examples/computed-fields/db/posts.schema.mjs +59 -0
- package/examples/computed-fields/db/products.schema.mjs +39 -0
- package/examples/computed-fields/db/users.schema.mjs +43 -0
- package/examples/computed-fields/db.config.mjs +15 -0
- package/examples/computed-fields/example.json +5 -0
- package/examples/computed-fields/src/generated/db.types.d.ts +81 -0
- package/examples/content-collections/README.md +91 -0
- package/examples/content-collections/db/authors.json +12 -0
- package/examples/content-collections/db/authors.schema.mjs +20 -0
- package/examples/content-collections/db/blog/draft-roadmap.mdx +12 -0
- package/examples/content-collections/db/blog/index.schema.mjs +61 -0
- package/examples/content-collections/db/blog/launch-notes.mdx +15 -0
- package/examples/content-collections/db/docs/index.schema.mjs +32 -0
- package/examples/content-collections/db/docs/intro.mdx +11 -0
- package/examples/content-collections/db/docs/schema-workflow.mdx +10 -0
- package/examples/content-collections/db/site.schema.jsonc +21 -0
- package/examples/content-collections/db.config.mjs +26 -0
- package/examples/content-collections/example.json +5 -0
- package/examples/content-collections/src/content-preview.mjs +66 -0
- package/examples/content-collections/src/generated/db.types.d.ts +81 -0
- package/examples/csv/README.md +52 -0
- package/examples/csv/db/customers.csv +4 -0
- package/examples/csv/db.config.mjs +13 -0
- package/examples/csv/example.json +5 -0
- package/examples/data-first/README.md +54 -0
- package/examples/data-first/db/posts.json +16 -0
- package/examples/data-first/db/settings.json +8 -0
- package/examples/data-first/db/users.json +14 -0
- package/examples/data-first/db.config.mjs +13 -0
- package/examples/data-first/example.json +5 -0
- package/examples/diagnostics/README.md +55 -0
- package/examples/diagnostics/db/projects.schema.jsonc +27 -0
- package/examples/diagnostics/db/users.json +9 -0
- package/examples/diagnostics/db/users.schema.jsonc +23 -0
- package/examples/diagnostics/db.config.mjs +16 -0
- package/examples/diagnostics/example.json +5 -0
- package/examples/free-plan-upgrade/README.md +22 -0
- package/examples/free-plan-upgrade/db/appSettings.json +4 -0
- package/examples/free-plan-upgrade/db/projects.json +7 -0
- package/examples/free-plan-upgrade/example.json +5 -0
- package/examples/free-plan-upgrade/src/upgrade-tenant-to-paid.mjs +105 -0
- package/examples/hono-auth/README.md +74 -0
- package/examples/hono-auth/db/pages.schema.jsonc +44 -0
- package/examples/hono-auth/db/users.schema.jsonc +42 -0
- package/examples/hono-auth/db.config.mjs +17 -0
- package/examples/hono-auth/example.json +5 -0
- package/examples/hono-auth/package.json +14 -0
- package/examples/hono-auth/src/app.mjs +79 -0
- package/examples/hono-auth/src/server.mjs +13 -0
- package/examples/production-json/README.md +102 -0
- package/examples/production-json/db/appSettings.schema.jsonc +41 -0
- package/examples/production-json/db/featureFlags.schema.jsonc +84 -0
- package/examples/production-json/db/operations/get-control-plane.jsonc +6 -0
- package/examples/production-json/db/operations/get-feature-flag.jsonc +9 -0
- package/examples/production-json/db/operations/list-feature-flags.jsonc +8 -0
- package/examples/production-json/db/operations/read-public-settings.jsonc +8 -0
- package/examples/production-json/db.config.mjs +33 -0
- package/examples/production-json/example.json +5 -0
- package/examples/production-json/src/client-demo.mjs +28 -0
- package/examples/production-json/src/generated/db.types.d.ts +60 -0
- package/examples/relations/README.md +56 -0
- package/examples/relations/db/posts.schema.jsonc +46 -0
- package/examples/relations/db/users.schema.jsonc +34 -0
- package/examples/relations/db.config.mjs +13 -0
- package/examples/relations/example.json +5 -0
- package/examples/rest-client/README.md +54 -0
- package/examples/rest-client/db/settings.json +5 -0
- package/examples/rest-client/db/users.schema.jsonc +42 -0
- package/examples/rest-client/db.config.mjs +13 -0
- package/examples/rest-client/example.json +5 -0
- package/examples/rest-client/src/client-demo.mjs +24 -0
- package/examples/schema-first/README.md +55 -0
- package/examples/schema-first/db/auditEvents.schema.jsonc +24 -0
- package/examples/schema-first/db/settings.schema.jsonc +29 -0
- package/examples/schema-first/db/users.schema.jsonc +36 -0
- package/examples/schema-first/db.config.mjs +15 -0
- package/examples/schema-first/example.json +5 -0
- package/examples/schema-first/src/generated/db.types.d.ts +47 -0
- package/examples/schema-manifest/README.md +50 -0
- package/examples/schema-manifest/db/projects.schema.jsonc +48 -0
- package/examples/schema-manifest/db/users.schema.jsonc +35 -0
- package/examples/schema-manifest/db.config.mjs +41 -0
- package/examples/schema-manifest/example.json +5 -0
- package/examples/schema-manifest/src/generated/db.schema.json +130 -0
- package/examples/schema-manifest/src/generated/db.types.d.ts +50 -0
- package/examples/schema-ui/README.md +103 -0
- package/examples/schema-ui/db/pages.schema.jsonc +53 -0
- package/examples/schema-ui/db/users.schema.jsonc +30 -0
- package/examples/schema-ui/db.config.mjs +55 -0
- package/examples/schema-ui/example.json +5 -0
- package/examples/schema-ui/src/cms-ssr.mjs +276 -0
- package/examples/schema-ui/src/generated/db.schema.json +133 -0
- package/examples/schema-ui/src/generated/db.types.d.ts +46 -0
- package/examples/schema-ui/src/render-admin.mjs +175 -0
- package/examples/schema-ui/src/schema-ui-ssr-handler.mjs +149 -0
- package/examples/schema-ui/src/start-schema-ui-server.mjs +140 -0
- package/examples/standard-schema/README.md +55 -0
- package/examples/standard-schema/db/settings.schema.mjs +22 -0
- package/examples/standard-schema/db/users.schema.mjs +72 -0
- package/examples/standard-schema/example.json +5 -0
- package/package.json +108 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
type RuntimeConfig = {
|
|
2
|
+
schema?: {
|
|
3
|
+
unknownFields?: string;
|
|
4
|
+
};
|
|
5
|
+
cwd?: string;
|
|
6
|
+
stateDir?: string;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
};
|
|
9
|
+
type RuntimeResource = {
|
|
10
|
+
name: string;
|
|
11
|
+
kind?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
};
|
|
14
|
+
type RuntimeAdapter = {
|
|
15
|
+
statePath?: (resource: RuntimeResource) => unknown;
|
|
16
|
+
readResource?: (resource: RuntimeResource, fallback: unknown) => Promise<unknown> | unknown;
|
|
17
|
+
writeResource?: (resource: RuntimeResource, value: unknown) => Promise<unknown> | unknown;
|
|
18
|
+
withResourceWrite?: <T>(resource: RuntimeResource, operation: () => T | Promise<T>) => Promise<T> | T;
|
|
19
|
+
};
|
|
20
|
+
type RuntimeFacade = {
|
|
21
|
+
adapterFor(resource: RuntimeResource): RuntimeAdapter;
|
|
22
|
+
emit(change: Record<string, unknown>): unknown;
|
|
23
|
+
};
|
|
24
|
+
type DbLike = {
|
|
25
|
+
config: RuntimeConfig;
|
|
26
|
+
runtime: RuntimeFacade;
|
|
27
|
+
assertResourceWritable?: (resourceName: string) => void;
|
|
28
|
+
};
|
|
29
|
+
export declare class DbDocument {
|
|
30
|
+
db: DbLike;
|
|
31
|
+
config: RuntimeConfig;
|
|
32
|
+
resource: RuntimeResource;
|
|
33
|
+
path: unknown;
|
|
34
|
+
constructor(db: DbLike | RuntimeConfig, resource: RuntimeResource);
|
|
35
|
+
all(): Promise<unknown>;
|
|
36
|
+
get(pointer?: string): Promise<unknown>;
|
|
37
|
+
put(value: unknown): Promise<unknown>;
|
|
38
|
+
set(pointer: string, value: unknown): Promise<unknown>;
|
|
39
|
+
update(patch: Record<string, unknown>): Promise<unknown>;
|
|
40
|
+
adapter(): RuntimeAdapter;
|
|
41
|
+
emit(op: string, details?: Record<string, unknown>): void;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { dbError } from '../../errors.js';
|
|
2
|
+
import { createSchemaValidator } from '../../schema.js';
|
|
3
|
+
import { createRuntime } from '../storage/runtime.js';
|
|
4
|
+
import { getPointer, setPointer } from './json-pointer.js';
|
|
5
|
+
export class DbDocument {
|
|
6
|
+
db;
|
|
7
|
+
config;
|
|
8
|
+
resource;
|
|
9
|
+
path;
|
|
10
|
+
constructor(db, resource) {
|
|
11
|
+
this.db = normalizeDb(db, resource);
|
|
12
|
+
this.config = this.db.config;
|
|
13
|
+
this.resource = resource;
|
|
14
|
+
this.path = this.db.runtime.adapterFor(resource).statePath?.(resource);
|
|
15
|
+
}
|
|
16
|
+
async all() {
|
|
17
|
+
return this.adapter().readResource?.(this.resource, {});
|
|
18
|
+
}
|
|
19
|
+
async get(pointer = '') {
|
|
20
|
+
const document = await this.all();
|
|
21
|
+
return pointer ? getPointer(document, pointer) : document;
|
|
22
|
+
}
|
|
23
|
+
async put(value) {
|
|
24
|
+
this.db.assertResourceWritable?.(this.resource.name);
|
|
25
|
+
return this.adapter().withResourceWrite(this.resource, async () => {
|
|
26
|
+
const document = await assertRuntimeDocument(value, this.resource, this.config, {
|
|
27
|
+
source: `${this.resource.name} document body`,
|
|
28
|
+
});
|
|
29
|
+
await this.adapter().writeResource?.(this.resource, document);
|
|
30
|
+
this.emit('put');
|
|
31
|
+
return document;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async set(pointer, value) {
|
|
35
|
+
this.db.assertResourceWritable?.(this.resource.name);
|
|
36
|
+
return this.adapter().withResourceWrite(this.resource, async () => {
|
|
37
|
+
const document = await this.all();
|
|
38
|
+
setPointer(document, pointer, value);
|
|
39
|
+
const nextDocument = await assertRuntimeDocument(document, this.resource, this.config, {
|
|
40
|
+
source: `${this.resource.name} document body`,
|
|
41
|
+
});
|
|
42
|
+
await this.adapter().writeResource?.(this.resource, nextDocument);
|
|
43
|
+
this.emit('set', { pointer });
|
|
44
|
+
return getPointer(nextDocument, pointer);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async update(patch) {
|
|
48
|
+
this.db.assertResourceWritable?.(this.resource.name);
|
|
49
|
+
return this.adapter().withResourceWrite(this.resource, async () => {
|
|
50
|
+
const document = await this.all();
|
|
51
|
+
const nextDocument = {
|
|
52
|
+
...document,
|
|
53
|
+
...patch,
|
|
54
|
+
};
|
|
55
|
+
const validatedDocument = await assertRuntimeDocument(nextDocument, this.resource, this.config, {
|
|
56
|
+
source: `${this.resource.name} document patch body`,
|
|
57
|
+
});
|
|
58
|
+
await this.adapter().writeResource?.(this.resource, validatedDocument);
|
|
59
|
+
this.emit('update');
|
|
60
|
+
return validatedDocument;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
adapter() {
|
|
64
|
+
return this.db.runtime.adapterFor(this.resource);
|
|
65
|
+
}
|
|
66
|
+
emit(op, details = {}) {
|
|
67
|
+
this.db.runtime.emit({
|
|
68
|
+
resource: this.resource.name,
|
|
69
|
+
kind: 'document',
|
|
70
|
+
op,
|
|
71
|
+
...details,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function assertRuntimeDocument(document, resource, config, options = {}) {
|
|
76
|
+
const validator = createSchemaValidator(resource, config, {
|
|
77
|
+
mode: 'replace',
|
|
78
|
+
unknownFields: config.schema?.unknownFields ?? 'warn',
|
|
79
|
+
applyDefaults: false,
|
|
80
|
+
});
|
|
81
|
+
return validator.validateAsync(document, {
|
|
82
|
+
source: options.source,
|
|
83
|
+
applyDefaults: false,
|
|
84
|
+
}).then((result) => {
|
|
85
|
+
if (result.ok) {
|
|
86
|
+
return result.value;
|
|
87
|
+
}
|
|
88
|
+
throw dbError('DB_SCHEMA_VALIDATION_FAILED', `${resource.name} record does not match its schema: ${result.errors[0].message}`, {
|
|
89
|
+
status: 400,
|
|
90
|
+
hint: 'Update the record to match the schema field types, required fields, enum values, and constraints.',
|
|
91
|
+
details: {
|
|
92
|
+
resource: resource.name,
|
|
93
|
+
diagnostics: result.errors,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function normalizeDb(db, resource) {
|
|
99
|
+
if (isDbLike(db)) {
|
|
100
|
+
return db;
|
|
101
|
+
}
|
|
102
|
+
const config = db;
|
|
103
|
+
const runtime = createRuntime(config, [resource]);
|
|
104
|
+
return {
|
|
105
|
+
config,
|
|
106
|
+
runtime,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function isDbLike(value) {
|
|
110
|
+
return Boolean(value?.runtime && value?.config);
|
|
111
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type RuntimeRecord = Record<string, unknown>;
|
|
2
|
+
type RuntimeDb = {
|
|
3
|
+
config?: unknown;
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
};
|
|
6
|
+
type ComputedFieldResolver = {
|
|
7
|
+
resolve?: (...args: unknown[]) => unknown;
|
|
8
|
+
resolveMany?: (...args: unknown[]) => unknown;
|
|
9
|
+
};
|
|
10
|
+
type RuntimeResource = {
|
|
11
|
+
fields?: Record<string, {
|
|
12
|
+
computed?: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
resolvers?: {
|
|
15
|
+
fields?: Record<string, ComputedFieldResolver>;
|
|
16
|
+
};
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
};
|
|
19
|
+
type ComputedFanoutOptions = {
|
|
20
|
+
cache?: Map<unknown, unknown>;
|
|
21
|
+
context?: unknown;
|
|
22
|
+
};
|
|
23
|
+
export declare function resolveSelectedComputedFields(db: RuntimeDb, resource: RuntimeResource, records: RuntimeRecord[], fieldNames: string[], options?: ComputedFanoutOptions): Promise<RuntimeRecord[]>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { callFieldResolver, valueFromResolveManyResult } from '../schema/resolvers.js';
|
|
2
|
+
export async function resolveSelectedComputedFields(db, resource, records, fieldNames, options = {}) {
|
|
3
|
+
const selected = [...new Set(fieldNames)]
|
|
4
|
+
.filter((fieldName) => resource.fields?.[fieldName]?.computed)
|
|
5
|
+
.filter((fieldName) => resource.resolvers?.fields?.[fieldName]);
|
|
6
|
+
if (selected.length === 0 || records.length === 0) {
|
|
7
|
+
return records;
|
|
8
|
+
}
|
|
9
|
+
const nextRecords = records.map((record) => record && typeof record === 'object' && !Array.isArray(record)
|
|
10
|
+
? { ...record }
|
|
11
|
+
: record);
|
|
12
|
+
const cache = options.cache ?? new Map();
|
|
13
|
+
for (const fieldName of selected) {
|
|
14
|
+
const resolver = resource.resolvers?.fields?.[fieldName];
|
|
15
|
+
if (!resolver) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (typeof resolver.resolveMany === 'function') {
|
|
19
|
+
const args = {
|
|
20
|
+
records: nextRecords,
|
|
21
|
+
db,
|
|
22
|
+
resource,
|
|
23
|
+
cache,
|
|
24
|
+
};
|
|
25
|
+
const values = await callFieldResolver(resolver.resolveMany, args, {
|
|
26
|
+
db,
|
|
27
|
+
config: db.config,
|
|
28
|
+
resource,
|
|
29
|
+
fieldName,
|
|
30
|
+
cache,
|
|
31
|
+
context: options.context,
|
|
32
|
+
value: nextRecords,
|
|
33
|
+
});
|
|
34
|
+
applyManyResolvedValues(nextRecords, resource, fieldName, values);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (typeof resolver.resolve === 'function') {
|
|
38
|
+
for (const record of nextRecords) {
|
|
39
|
+
const args = {
|
|
40
|
+
record,
|
|
41
|
+
db,
|
|
42
|
+
resource,
|
|
43
|
+
cache,
|
|
44
|
+
};
|
|
45
|
+
record[fieldName] = await callFieldResolver(resolver.resolve, args, {
|
|
46
|
+
db,
|
|
47
|
+
config: db.config,
|
|
48
|
+
resource,
|
|
49
|
+
fieldName,
|
|
50
|
+
cache,
|
|
51
|
+
context: options.context,
|
|
52
|
+
value: record,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return nextRecords;
|
|
58
|
+
}
|
|
59
|
+
function applyManyResolvedValues(records, resource, fieldName, values) {
|
|
60
|
+
if (Array.isArray(values)) {
|
|
61
|
+
for (const [index, record] of records.entries()) {
|
|
62
|
+
record[fieldName] = valueFromResolveManyResult(values, resource, record, index);
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (values instanceof Map) {
|
|
67
|
+
for (const [index, record] of records.entries()) {
|
|
68
|
+
record[fieldName] = valueFromResolveManyResult(values, resource, record, index);
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (values && typeof values === 'object') {
|
|
73
|
+
for (const [index, record] of records.entries()) {
|
|
74
|
+
record[fieldName] = valueFromResolveManyResult(values, resource, record, index);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type JsonContainer = Record<string, unknown> | unknown[];
|
|
2
|
+
export declare function getPointer(document: unknown, pointer: string): unknown;
|
|
3
|
+
export declare function setPointer(document: JsonContainer, pointer: string, value: unknown): void;
|
|
4
|
+
export declare function parsePointer(pointer: string): string[];
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { dbError } from '../../errors.js';
|
|
2
|
+
export function getPointer(document, pointer) {
|
|
3
|
+
const parts = parsePointer(pointer);
|
|
4
|
+
let value = document;
|
|
5
|
+
for (const part of parts) {
|
|
6
|
+
if (value === null || value === undefined) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
value = value[part];
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
export function setPointer(document, pointer, value) {
|
|
14
|
+
const parts = parsePointer(pointer);
|
|
15
|
+
if (parts.length === 0) {
|
|
16
|
+
throw dbError('DB_DOCUMENT_SET_ROOT', 'Cannot set the root document with set().', {
|
|
17
|
+
status: 400,
|
|
18
|
+
hint: 'Use document.put(value) to replace the whole document, or pass a JSON pointer like "/theme" to set a nested value.',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
let current = document;
|
|
22
|
+
for (const part of parts.slice(0, -1)) {
|
|
23
|
+
if (!current[part] || typeof current[part] !== 'object') {
|
|
24
|
+
current[part] = {};
|
|
25
|
+
}
|
|
26
|
+
current = current[part];
|
|
27
|
+
}
|
|
28
|
+
const last = parts.at(-1);
|
|
29
|
+
if (last !== undefined) {
|
|
30
|
+
current[last] = value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function parsePointer(pointer) {
|
|
34
|
+
if (!pointer || pointer === '/') {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
if (!pointer.startsWith('/')) {
|
|
38
|
+
throw dbError('DB_INVALID_JSON_POINTER', `Invalid JSON pointer "${pointer}".`, {
|
|
39
|
+
status: 400,
|
|
40
|
+
hint: 'JSON pointers must start with "/". For example: "/theme" or "/features/billing".',
|
|
41
|
+
details: { pointer },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return pointer
|
|
45
|
+
.slice(1)
|
|
46
|
+
.split('/')
|
|
47
|
+
.filter(Boolean)
|
|
48
|
+
.map((part) => part.replaceAll('~1', '/').replaceAll('~0', '~'));
|
|
49
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type DbScope = {
|
|
2
|
+
fork: string | null;
|
|
3
|
+
branch: string;
|
|
4
|
+
rootStateDir: string;
|
|
5
|
+
};
|
|
6
|
+
export type RuntimeScopeConfig = {
|
|
7
|
+
cwd: string;
|
|
8
|
+
stateDir: string;
|
|
9
|
+
resources?: Record<string, unknown>;
|
|
10
|
+
__asyncDbScope?: DbScope;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
};
|
|
13
|
+
export type MigrationLock = {
|
|
14
|
+
name: string;
|
|
15
|
+
resources: string[];
|
|
16
|
+
mode: 'read-only';
|
|
17
|
+
startedAt: string;
|
|
18
|
+
copies?: Record<string, MigrationResourceCopy>;
|
|
19
|
+
};
|
|
20
|
+
export type MigrationResourceCopy = {
|
|
21
|
+
resource: string;
|
|
22
|
+
from: string;
|
|
23
|
+
to: string;
|
|
24
|
+
copiedAt: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function normalizeScopedConfig<TConfig extends RuntimeScopeConfig>(config: TConfig, scope?: DbScope): {
|
|
27
|
+
config: TConfig;
|
|
28
|
+
scope: DbScope;
|
|
29
|
+
};
|
|
30
|
+
export declare function scopedConfig(config: RuntimeScopeConfig, scope: DbScope): RuntimeScopeConfig;
|
|
31
|
+
export declare function forkRegistryPath(rootStateDir: string): string;
|
|
32
|
+
export declare function branchRegistryPath(rootStateDir: string, fork: string): string;
|
|
33
|
+
export declare function snapshotDirForScope(scope: DbScope, snapshotId: string): string;
|
|
34
|
+
export declare function routingPathForScope(scope: DbScope): string;
|
|
35
|
+
export declare function migrationLocksPathForScope(scope: DbScope): string;
|
|
36
|
+
export declare function loadPersistedMigrationLocks(scope: DbScope): Map<string, MigrationLock>;
|
|
37
|
+
export declare function readJsonFile<T>(filePath: string, fallback: T): Promise<T>;
|
|
38
|
+
export declare function writeJsonFile(filePath: string, value: unknown): Promise<void>;
|
|
39
|
+
export declare function snapshotId(label?: string): string;
|
|
40
|
+
export declare function assertValidSnapshotId(id: string): void;
|
|
41
|
+
export declare function assertValidScopedName(name: string, kind: string): void;
|
|
42
|
+
export declare function configRecord(value: unknown): Record<string, unknown>;
|
|
43
|
+
export declare function cloneConfigResources(value: unknown): Record<string, unknown> | undefined;
|
|
44
|
+
export declare function readJsonFileSync<T>(filePath: string, fallback: T): T;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { dbError } from '../../errors.js';
|
|
4
|
+
import { readJsonState, writeJsonState } from '../storage/json.js';
|
|
5
|
+
export function normalizeScopedConfig(config, scope) {
|
|
6
|
+
const next = {
|
|
7
|
+
...config,
|
|
8
|
+
resources: cloneConfigResources(config.resources),
|
|
9
|
+
};
|
|
10
|
+
const nextScope = scope ?? next.__asyncDbScope ?? {
|
|
11
|
+
fork: null,
|
|
12
|
+
branch: 'main',
|
|
13
|
+
rootStateDir: config.stateDir,
|
|
14
|
+
};
|
|
15
|
+
next.__asyncDbScope = nextScope;
|
|
16
|
+
applyPersistedRouting(next, nextScope);
|
|
17
|
+
return {
|
|
18
|
+
config: next,
|
|
19
|
+
scope: nextScope,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function scopedConfig(config, scope) {
|
|
23
|
+
const stateDir = scope.fork
|
|
24
|
+
? path.join(scope.rootStateDir, 'forks', scope.fork, 'branches', scope.branch)
|
|
25
|
+
: scope.rootStateDir;
|
|
26
|
+
return {
|
|
27
|
+
...config,
|
|
28
|
+
stateDir,
|
|
29
|
+
__asyncDbScope: scope,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function forkRegistryPath(rootStateDir) {
|
|
33
|
+
return path.join(rootStateDir, 'forks', 'registry.json');
|
|
34
|
+
}
|
|
35
|
+
export function branchRegistryPath(rootStateDir, fork) {
|
|
36
|
+
return path.join(rootStateDir, 'forks', fork, 'branches', 'registry.json');
|
|
37
|
+
}
|
|
38
|
+
export function snapshotDirForScope(scope, snapshotId) {
|
|
39
|
+
return scope.fork
|
|
40
|
+
? path.join(scope.rootStateDir, 'forks', scope.fork, 'snapshots', snapshotId)
|
|
41
|
+
: path.join(scope.rootStateDir, 'snapshots', snapshotId);
|
|
42
|
+
}
|
|
43
|
+
export function routingPathForScope(scope) {
|
|
44
|
+
return path.join(branchMetaDirForScope(scope), 'routing.json');
|
|
45
|
+
}
|
|
46
|
+
export function migrationLocksPathForScope(scope) {
|
|
47
|
+
return path.join(branchMetaDirForScope(scope), 'migration-locks.json');
|
|
48
|
+
}
|
|
49
|
+
export function loadPersistedMigrationLocks(scope) {
|
|
50
|
+
const state = readJsonFileSync(migrationLocksPathForScope(scope), { locks: {} });
|
|
51
|
+
const locks = new Map();
|
|
52
|
+
for (const [name, value] of Object.entries(configRecord(state.locks))) {
|
|
53
|
+
const lock = configRecord(value);
|
|
54
|
+
if (!Array.isArray(lock.resources)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
locks.set(name, {
|
|
58
|
+
name,
|
|
59
|
+
resources: lock.resources.map(String),
|
|
60
|
+
mode: 'read-only',
|
|
61
|
+
startedAt: typeof lock.startedAt === 'string' ? lock.startedAt : '',
|
|
62
|
+
copies: migrationCopiesRecord(lock.copies),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return locks;
|
|
66
|
+
}
|
|
67
|
+
export async function readJsonFile(filePath, fallback) {
|
|
68
|
+
return readJsonState(filePath, fallback);
|
|
69
|
+
}
|
|
70
|
+
export async function writeJsonFile(filePath, value) {
|
|
71
|
+
await writeJsonState(filePath, value);
|
|
72
|
+
}
|
|
73
|
+
export function snapshotId(label) {
|
|
74
|
+
const safeLabel = label
|
|
75
|
+
? `_${slugPart(label)}`
|
|
76
|
+
: '';
|
|
77
|
+
return `snap_${new Date().toISOString().replace(/[-:.TZ]/g, '')}${safeLabel}_${Math.random().toString(36).slice(2, 8)}`;
|
|
78
|
+
}
|
|
79
|
+
export function assertValidSnapshotId(id) {
|
|
80
|
+
if (/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(id)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
throw dbError('DB_SNAPSHOT_ID_INVALID', `Invalid snapshot id "${id}".`, {
|
|
84
|
+
status: 400,
|
|
85
|
+
hint: 'Use a snapshot id with letters, numbers, dots, underscores, or hyphens.',
|
|
86
|
+
details: {
|
|
87
|
+
snapshot: id,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
export function assertValidScopedName(name, kind) {
|
|
92
|
+
if (/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(String(name ?? ''))) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
throw dbError('DB_SCOPE_NAME_INVALID', `Invalid ${kind} name "${name}".`, {
|
|
96
|
+
status: 400,
|
|
97
|
+
hint: 'Use a folder-style name with letters, numbers, underscores, or hyphens.',
|
|
98
|
+
details: {
|
|
99
|
+
kind,
|
|
100
|
+
name,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
export function configRecord(value) {
|
|
105
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
106
|
+
? value
|
|
107
|
+
: {};
|
|
108
|
+
}
|
|
109
|
+
export function cloneConfigResources(value) {
|
|
110
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
return Object.fromEntries(Object.entries(value)
|
|
114
|
+
.map(([name, config]) => [name, cloneConfigValue(config)]));
|
|
115
|
+
}
|
|
116
|
+
function branchMetaDirForScope(scope) {
|
|
117
|
+
return scope.fork
|
|
118
|
+
? path.join(scope.rootStateDir, 'forks', scope.fork, 'branches', scope.branch, 'meta')
|
|
119
|
+
: path.join(scope.rootStateDir, 'meta');
|
|
120
|
+
}
|
|
121
|
+
function applyPersistedRouting(config, scope) {
|
|
122
|
+
const routes = readJsonFileSync(routingPathForScope(scope), {});
|
|
123
|
+
if (Object.keys(routes).length === 0) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const resourcesConfig = cloneConfigResources(config.resources) ?? {};
|
|
127
|
+
for (const [resourceName, storeName] of Object.entries(routes)) {
|
|
128
|
+
const existing = configRecord(resourcesConfig[resourceName]);
|
|
129
|
+
resourcesConfig[resourceName] = {
|
|
130
|
+
...existing,
|
|
131
|
+
store: storeName,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
config.resources = resourcesConfig;
|
|
135
|
+
}
|
|
136
|
+
export function readJsonFileSync(filePath, fallback) {
|
|
137
|
+
try {
|
|
138
|
+
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (error.code === 'ENOENT') {
|
|
142
|
+
return fallback;
|
|
143
|
+
}
|
|
144
|
+
if (error instanceof SyntaxError) {
|
|
145
|
+
throw dbError('JSON_STATE_INVALID', `JSON state file is not valid JSON: ${filePath}`, {
|
|
146
|
+
status: 500,
|
|
147
|
+
hint: 'Restore this file from a known-good snapshot, delete it to rehydrate from seed data when safe, or fix the JSON syntax before restarting.',
|
|
148
|
+
details: {
|
|
149
|
+
filePath,
|
|
150
|
+
parserMessage: error.message,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function migrationCopiesRecord(value) {
|
|
158
|
+
const copies = configRecord(value);
|
|
159
|
+
const normalized = {};
|
|
160
|
+
for (const [resource, copyValue] of Object.entries(copies)) {
|
|
161
|
+
const copy = configRecord(copyValue);
|
|
162
|
+
if (typeof copy.from !== 'string' || typeof copy.to !== 'string') {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
normalized[resource] = {
|
|
166
|
+
resource,
|
|
167
|
+
from: copy.from,
|
|
168
|
+
to: copy.to,
|
|
169
|
+
copiedAt: typeof copy.copiedAt === 'string' ? copy.copiedAt : '',
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
173
|
+
}
|
|
174
|
+
function slugPart(value) {
|
|
175
|
+
return value
|
|
176
|
+
.toLowerCase()
|
|
177
|
+
.replace(/[^a-z0-9_-]+/g, '-')
|
|
178
|
+
.replace(/^-+|-+$/g, '')
|
|
179
|
+
.slice(0, 48);
|
|
180
|
+
}
|
|
181
|
+
function cloneConfigValue(value) {
|
|
182
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
183
|
+
? { ...value }
|
|
184
|
+
: value;
|
|
185
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { atomicWriteJson, readJsonState, statePathForResource, withJsonStateWrite, writeJsonState, } from '../storage/json.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { atomicWriteJson, readJsonState, statePathForResource, withJsonStateWrite, writeJsonState, } from '../storage/json.js';
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { SchemaField } from './fields.js';
|
|
2
|
+
import type { SchemaLoadMode } from './locator.js';
|
|
3
|
+
import { callFieldResolver } from './resolvers.js';
|
|
4
|
+
import { type SchemaDiagnostic, type StandardSchemaV1 } from './standard-schema.js';
|
|
5
|
+
type ValidationMode = 'create' | 'replace' | 'patch';
|
|
6
|
+
type ValidatorUnknownFields = 'allow' | 'warn' | 'error' | 'strip';
|
|
7
|
+
type DbConfig = Record<string, unknown> & {
|
|
8
|
+
schemaLoadMode?: SchemaLoadMode;
|
|
9
|
+
schema?: {
|
|
10
|
+
unknownFields?: ValidatorUnknownFields | string;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
};
|
|
13
|
+
services?: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
type LoadDbSchemaOptions = string | (DbConfig & {
|
|
16
|
+
from?: string;
|
|
17
|
+
load?: SchemaLoadMode;
|
|
18
|
+
});
|
|
19
|
+
type SchemaFieldResolverDefinition = {
|
|
20
|
+
resolve?: Parameters<typeof callFieldResolver>[0];
|
|
21
|
+
resolveMany?: Parameters<typeof callFieldResolver>[0];
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
};
|
|
24
|
+
type SchemaResource = {
|
|
25
|
+
name: string;
|
|
26
|
+
kind?: 'collection' | 'document' | string;
|
|
27
|
+
idField?: string;
|
|
28
|
+
fields?: Record<string, SchemaField>;
|
|
29
|
+
fieldsAuthoritative?: boolean;
|
|
30
|
+
resolvers?: {
|
|
31
|
+
fields?: Record<string, SchemaFieldResolverDefinition>;
|
|
32
|
+
[key: string]: unknown;
|
|
33
|
+
};
|
|
34
|
+
validators?: {
|
|
35
|
+
standard?: StandardSchemaV1;
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
};
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
};
|
|
40
|
+
type SchemaProject = {
|
|
41
|
+
resources: SchemaResource[];
|
|
42
|
+
diagnostics: SchemaDiagnostic[];
|
|
43
|
+
schema: unknown;
|
|
44
|
+
loadMode?: SchemaLoadMode;
|
|
45
|
+
locator?: unknown;
|
|
46
|
+
rootSchema?: unknown;
|
|
47
|
+
};
|
|
48
|
+
type LoadedDbSchema = {
|
|
49
|
+
kind: 'DbSchema';
|
|
50
|
+
config: DbConfig;
|
|
51
|
+
loadMode?: SchemaLoadMode;
|
|
52
|
+
locator?: unknown;
|
|
53
|
+
rootSchema?: unknown;
|
|
54
|
+
resources: Map<string, SchemaResource>;
|
|
55
|
+
diagnostics: SchemaDiagnostic[];
|
|
56
|
+
schema: unknown;
|
|
57
|
+
resource(name: string): SchemaResource;
|
|
58
|
+
resourceNames(): string[];
|
|
59
|
+
validator(name: string, options?: SchemaValidatorOptions): SchemaValidator;
|
|
60
|
+
resolver(selector: string, options?: SchemaResolverOptions): SchemaFieldResolver | Record<string, SchemaFieldResolver>;
|
|
61
|
+
validate(name: string, value: unknown, options?: SchemaValidatorOptions): ValidationResult;
|
|
62
|
+
assert(name: string, value: unknown, options?: SchemaValidatorOptions): unknown;
|
|
63
|
+
validateAsync(name: string, value: unknown, options?: SchemaValidatorOptions): Promise<ValidationResult>;
|
|
64
|
+
assertAsync(name: string, value: unknown, options?: SchemaValidatorOptions): Promise<unknown>;
|
|
65
|
+
toJSON(): unknown;
|
|
66
|
+
};
|
|
67
|
+
type SchemaResolverOptions = {
|
|
68
|
+
db?: unknown;
|
|
69
|
+
cache?: Map<unknown, unknown>;
|
|
70
|
+
context?: unknown;
|
|
71
|
+
services?: Record<string, unknown>;
|
|
72
|
+
value?: unknown;
|
|
73
|
+
[key: string]: unknown;
|
|
74
|
+
};
|
|
75
|
+
type SchemaFieldResolver = ((args?: unknown) => Promise<unknown>) & {
|
|
76
|
+
resolve?: (args?: unknown) => Promise<unknown>;
|
|
77
|
+
resolveMany?: (args?: unknown) => Promise<unknown>;
|
|
78
|
+
};
|
|
79
|
+
type SchemaValidatorOptions = {
|
|
80
|
+
mode?: ValidationMode | string;
|
|
81
|
+
unknownFields?: ValidatorUnknownFields | 'ignore' | string;
|
|
82
|
+
source?: string;
|
|
83
|
+
applyDefaults?: boolean;
|
|
84
|
+
};
|
|
85
|
+
type ValidationResult<TValue = Record<string, unknown>> = {
|
|
86
|
+
ok: boolean;
|
|
87
|
+
value: TValue;
|
|
88
|
+
diagnostics: SchemaDiagnostic[];
|
|
89
|
+
errors: SchemaDiagnostic[];
|
|
90
|
+
resource: string;
|
|
91
|
+
mode: ValidationMode;
|
|
92
|
+
};
|
|
93
|
+
type SchemaValidator<TValue = Record<string, unknown>> = {
|
|
94
|
+
resource: string;
|
|
95
|
+
mode: ValidationMode;
|
|
96
|
+
unknownFields: ValidatorUnknownFields;
|
|
97
|
+
validate(value: unknown, validateOptions?: SchemaValidatorOptions): ValidationResult<TValue>;
|
|
98
|
+
validateAsync(value: unknown, validateOptions?: SchemaValidatorOptions): Promise<ValidationResult<TValue>>;
|
|
99
|
+
assert(value: unknown, assertOptions?: SchemaValidatorOptions): TValue;
|
|
100
|
+
assertAsync(value: unknown, assertOptions?: SchemaValidatorOptions): Promise<TValue>;
|
|
101
|
+
};
|
|
102
|
+
export declare function loadDbSchema(options?: LoadDbSchemaOptions): Promise<LoadedDbSchema>;
|
|
103
|
+
export declare function createDbSchema(project: SchemaProject, config: DbConfig): LoadedDbSchema;
|
|
104
|
+
export declare function createSchemaResourceResolverMap(resource: SchemaResource, config: DbConfig, options?: SchemaResolverOptions): Record<string, SchemaFieldResolver>;
|
|
105
|
+
export declare function createSchemaFieldResolver(resource: SchemaResource, fieldName: string, config: DbConfig, options?: SchemaResolverOptions): SchemaFieldResolver;
|
|
106
|
+
export declare function createSchemaValidator(resource: SchemaResource, config: DbConfig, options?: SchemaValidatorOptions): SchemaValidator;
|
|
107
|
+
export {};
|