@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,527 @@
|
|
|
1
|
+
# Fixtures And Schemas
|
|
2
|
+
|
|
3
|
+
@async/db discovers fixture and schema sources recursively under the configured fixture folder, `./db` by default.
|
|
4
|
+
|
|
5
|
+
Supported built-in source formats:
|
|
6
|
+
|
|
7
|
+
```txt
|
|
8
|
+
.json
|
|
9
|
+
.jsonc
|
|
10
|
+
.csv
|
|
11
|
+
.schema.json
|
|
12
|
+
.schema.jsonc
|
|
13
|
+
.schema.js
|
|
14
|
+
.schema.mjs
|
|
15
|
+
db.schema.js
|
|
16
|
+
db.schema.mjs
|
|
17
|
+
index.schema.js
|
|
18
|
+
index.schema.mjs
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`.schema.js` and `db.schema.js` use normal Node.js ESM rules. If the project root `package.json` is already `"type": "module"`, no extra marker is needed. If the root is not ESM, @async/db creates `db/package.json` with `"type": "module"` before loading `.schema.js` files inside the configured fixture folder. Set `schema.autoModulePackageJson: false` to manage that file yourself.
|
|
22
|
+
|
|
23
|
+
TypeScript schema files are intentionally not loaded directly in v1 because Node.js does not execute TypeScript without an explicit loader or build step. See [TypeScript Schema Sources](./typescript-schema-sources.md) for the supported compile-to-JavaScript workflow.
|
|
24
|
+
|
|
25
|
+
## Data-First JSON Or JSONC
|
|
26
|
+
|
|
27
|
+
Use `db/users.json` or `db/users.jsonc` when you already have sample records and want @async/db to infer the collection schema.
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
[
|
|
31
|
+
{
|
|
32
|
+
"id": "u_1",
|
|
33
|
+
"name": "Ada Lovelace",
|
|
34
|
+
"active": true
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Collections always get an id field. If a JSON, JSONC, or CSV collection fixture omits `id`, @async/db adds counter ids in the selected runtime store:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
[
|
|
43
|
+
{ "id": "1", "name": "Ada Lovelace" },
|
|
44
|
+
{ "id": "2", "name": "Grace Hopper" }
|
|
45
|
+
]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
By default, source files stay unchanged and generated ids are written to the selected runtime store.
|
|
49
|
+
|
|
50
|
+
## CSV Fixtures
|
|
51
|
+
|
|
52
|
+
Use CSV when fixture data starts in a spreadsheet or export.
|
|
53
|
+
|
|
54
|
+
```txt
|
|
55
|
+
db/users.csv
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```csv
|
|
59
|
+
id,name,email,active
|
|
60
|
+
u_1,Ada Lovelace,ada@example.com,true
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`sync` parses the header row, infers a collection schema, and writes `.db/state/users.json` through the default JSON store. Source hashes are tracked so changed source fixtures refresh the selected runtime store, while unchanged source fixtures preserve runtime edits.
|
|
64
|
+
|
|
65
|
+
When a CSV is paired with a schema file, array fields stay arrays in runtime state. For example, a schema field like `"tags": { "type": "array", "items": { "type": "string" } }` accepts a CSV cell such as `renewal;priority` or a JSON array string such as `["renewal","priority"]`.
|
|
66
|
+
|
|
67
|
+
## Schema Files
|
|
68
|
+
|
|
69
|
+
Use schema files when data inference is too loose or when you need future intent that is not represented in the seed records.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"kind": "collection",
|
|
74
|
+
"idField": "id",
|
|
75
|
+
"fields": {
|
|
76
|
+
"id": { "type": "string", "required": true },
|
|
77
|
+
"name": { "type": "string", "required": true },
|
|
78
|
+
"email": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"required": true,
|
|
81
|
+
"unique": true,
|
|
82
|
+
"pattern": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
|
|
83
|
+
"description": "Email address used for local sign-in."
|
|
84
|
+
},
|
|
85
|
+
"role": {
|
|
86
|
+
"type": "enum",
|
|
87
|
+
"values": ["admin", "user"],
|
|
88
|
+
"default": "user"
|
|
89
|
+
},
|
|
90
|
+
"tags": {
|
|
91
|
+
"type": "array",
|
|
92
|
+
"maxLength": 5,
|
|
93
|
+
"items": { "type": "string" }
|
|
94
|
+
},
|
|
95
|
+
"score": {
|
|
96
|
+
"type": "number",
|
|
97
|
+
"min": 0,
|
|
98
|
+
"max": 100
|
|
99
|
+
},
|
|
100
|
+
"schemaSnapshot": {
|
|
101
|
+
"type": "object",
|
|
102
|
+
"additionalProperties": true,
|
|
103
|
+
"fields": {
|
|
104
|
+
"version": { "type": "number" }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Field constraints are checked during `sync`, schema validation, package API writes, REST writes, and GraphQL mutations. Use `nullable: true` when `null` is intentional. `datetime` fields validate as strings and generate TypeScript `string` types.
|
|
112
|
+
|
|
113
|
+
## JavaScript Schema Sources
|
|
114
|
+
|
|
115
|
+
Executable schema files can use `@async/db/schema` helpers:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
import { collection, field } from '@async/db/schema';
|
|
119
|
+
|
|
120
|
+
export default collection({
|
|
121
|
+
idField: 'id',
|
|
122
|
+
fields: {
|
|
123
|
+
id: field.string({ required: true }),
|
|
124
|
+
role: field.enum(['admin', 'user'], { default: 'user' }),
|
|
125
|
+
lastViewedAt: field.datetime(),
|
|
126
|
+
ownerPersonId: field.nullable(field.string()),
|
|
127
|
+
tags: field.array(field.string()),
|
|
128
|
+
schemaSnapshot: field.object({
|
|
129
|
+
version: field.number(),
|
|
130
|
+
}, { additionalProperties: true }),
|
|
131
|
+
},
|
|
132
|
+
seed: [],
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`.schema.js` files execute as trusted local project JavaScript. If the root package is not ESM, @async/db creates the fixture-folder package marker described at the top of this guide.
|
|
137
|
+
|
|
138
|
+
## Standard Schema Validators
|
|
139
|
+
|
|
140
|
+
Executable schema modules can also use any object that implements the Standard Schema
|
|
141
|
+
v1 contract. @async/db imports your trusted schema module, recognizes
|
|
142
|
+
`value['~standard'].version === 1`, and calls
|
|
143
|
+
`value['~standard'].validate(...)` during schema helpers and runtime writes.
|
|
144
|
+
The core package does not bundle a validator-library dependency. That means
|
|
145
|
+
Zod, Valibot, ArkType, or a local validator can own parsing and validation
|
|
146
|
+
while Async DB still applies its own lightweight metadata checks for defaults,
|
|
147
|
+
read-only/computed fields, uniqueness, relations, generated metadata, REST, and
|
|
148
|
+
GraphQL.
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
import { collection, field } from '@async/db/schema';
|
|
152
|
+
|
|
153
|
+
const UserSchema = {
|
|
154
|
+
'~standard': {
|
|
155
|
+
version: 1,
|
|
156
|
+
vendor: 'my-validator',
|
|
157
|
+
async validate(value) {
|
|
158
|
+
if (!value || typeof value !== 'object' || typeof value.email !== 'string') {
|
|
159
|
+
return { issues: [{ message: 'Email is required', path: ['email'] }] };
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
value: {
|
|
163
|
+
...value,
|
|
164
|
+
email: value.email.trim().toLowerCase(),
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
jsonSchema: {
|
|
169
|
+
output() {
|
|
170
|
+
return {
|
|
171
|
+
type: 'object',
|
|
172
|
+
required: ['email'],
|
|
173
|
+
properties: {
|
|
174
|
+
id: { type: 'string' },
|
|
175
|
+
email: { type: 'string' },
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export default collection({
|
|
184
|
+
idField: 'id',
|
|
185
|
+
validator: UserSchema,
|
|
186
|
+
fields: {
|
|
187
|
+
email: field.string({
|
|
188
|
+
required: true,
|
|
189
|
+
unique: true,
|
|
190
|
+
description: 'Normalized login email.',
|
|
191
|
+
}),
|
|
192
|
+
displayName: field.computed(field.string(), ({ record }) => record.email),
|
|
193
|
+
},
|
|
194
|
+
seed: [],
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
That object-first form keeps Async DB's simplified schema as the main shape and
|
|
199
|
+
mixes Standard Schema in as the parser/validator through `validator`. The equivalent
|
|
200
|
+
validator-first shorthand is useful when the external validator owns the field
|
|
201
|
+
shape:
|
|
202
|
+
|
|
203
|
+
```js
|
|
204
|
+
export default collection(UserSchema, {
|
|
205
|
+
fields: {
|
|
206
|
+
email: field.meta({ unique: true }),
|
|
207
|
+
displayName: field.computed(field.string(), ({ record }) => record.email),
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Set `schema.standardSchema: true` in `db.config.mjs` when generated
|
|
213
|
+
executable schema files should prefer that validator-first form for resources that
|
|
214
|
+
have a Standard Schema validator. Detection still works without the config flag;
|
|
215
|
+
the flag only changes generated authoring output.
|
|
216
|
+
|
|
217
|
+
If the validator exposes a Standard JSON Schema converter, @async/db uses that
|
|
218
|
+
for generated field metadata and TypeScript output. `field.meta(...)` overlays
|
|
219
|
+
Async DB metadata such as descriptions, defaults, uniqueness, relations,
|
|
220
|
+
constraints, and manifest hints. `field.computed(...)` remains the resolver
|
|
221
|
+
entrypoint.
|
|
222
|
+
|
|
223
|
+
Package API, REST, and GraphQL writes await async Standard Schema validators and
|
|
224
|
+
store the returned `value`. Synchronous helpers such as
|
|
225
|
+
`schema.validator('users').assert(...)` work for sync validators; if a validator
|
|
226
|
+
returns a Promise, use `validateAsync(...)` or `assertAsync(...)`. The sync path
|
|
227
|
+
throws `DB_SCHEMA_ASYNC_VALIDATOR_REQUIRED` with that hint.
|
|
228
|
+
|
|
229
|
+
Standard Schema issues become `STANDARD_SCHEMA_VALIDATION_FAILED` diagnostics
|
|
230
|
+
with a normalized field path, vendor, issue path, message, and recovery hint.
|
|
231
|
+
Validator and resolver functions stay in trusted local code and are never
|
|
232
|
+
serialized into generated schema, manifests, viewer metadata, or TypeScript
|
|
233
|
+
output.
|
|
234
|
+
|
|
235
|
+
When a Standard Schema validator has no JSON Schema converter and no
|
|
236
|
+
`field.meta(...)` overlays, generated TypeScript uses a conservative
|
|
237
|
+
`[key: string]: unknown` fallback and the project diagnostics include
|
|
238
|
+
`STANDARD_SCHEMA_FIELDS_UNKNOWN`. Add overlays or a converter when generated
|
|
239
|
+
metadata needs to be richer.
|
|
240
|
+
|
|
241
|
+
See [examples/standard-schema](../examples/standard-schema) for a runnable
|
|
242
|
+
dependency-free example.
|
|
243
|
+
|
|
244
|
+
## Root Schema Registry
|
|
245
|
+
|
|
246
|
+
Use `db.schema.js` at the project root when you want one canonical schema registry:
|
|
247
|
+
|
|
248
|
+
```js
|
|
249
|
+
import { collection, field } from '@async/db/schema';
|
|
250
|
+
|
|
251
|
+
export default {
|
|
252
|
+
users: collection({
|
|
253
|
+
idField: 'id',
|
|
254
|
+
fields: {
|
|
255
|
+
id: field.string({ required: true }),
|
|
256
|
+
firstName: field.string(),
|
|
257
|
+
lastName: field.string(),
|
|
258
|
+
fullName: field.computed(field.string(), function users_fullName_resolver({ record }) {
|
|
259
|
+
return `${record.firstName} ${record.lastName}`;
|
|
260
|
+
}),
|
|
261
|
+
},
|
|
262
|
+
}),
|
|
263
|
+
};
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
When `db.schema.js` or `db.schema.mjs` exists it is authoritative for explicit schemas. `db.schema.js` follows the root package `type` setting, so use root `"type": "module"` for that file. If both root files exist, `db.schema.mjs` wins and @async/db emits a duplicate-root warning. Per-resource `db/**/*.schema.*` files are not auto-discovered as live schemas, though the root schema may import them like normal JavaScript.
|
|
267
|
+
|
|
268
|
+
## Computed Fields
|
|
269
|
+
|
|
270
|
+
Use computed fields when an explicit schema needs trusted project code to derive
|
|
271
|
+
read-only values for REST and GraphQL projections:
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
import { collection, field } from '@async/db/schema';
|
|
275
|
+
|
|
276
|
+
export default collection({
|
|
277
|
+
idField: 'id',
|
|
278
|
+
fields: {
|
|
279
|
+
id: field.string({ required: true }),
|
|
280
|
+
firstName: field.string({ required: true }),
|
|
281
|
+
lastName: field.string({ required: true }),
|
|
282
|
+
fullName: field.computed(field.string(), function users_fullName_resolver({ record }) {
|
|
283
|
+
return `${record.firstName} ${record.lastName}`;
|
|
284
|
+
}),
|
|
285
|
+
displayName: field.computed(field.string(), {
|
|
286
|
+
resolve({ record }) {
|
|
287
|
+
return `${record.firstName} ${record.lastName}`;
|
|
288
|
+
},
|
|
289
|
+
resolveMany({ records }) {
|
|
290
|
+
return records.map((record) => `${record.firstName} ${record.lastName}`);
|
|
291
|
+
},
|
|
292
|
+
}),
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
`field.computed(type, fn)` is shorthand for `{ resolve: fn }`. Normal function
|
|
298
|
+
resolvers receive a runtime `this` context with delegated lookup through
|
|
299
|
+
`this.get(name)` and `this.has(name)`. Internal values include `db`, `resource`,
|
|
300
|
+
`field`, `fieldName`, `config`, `services`, `cache`, `value`, `record`,
|
|
301
|
+
`records`, and `args`. App-provided context can override those names, and
|
|
302
|
+
`this._internal` exposes the original internal values when a resolver needs them.
|
|
303
|
+
Arrow functions preserve JavaScript arrow semantics and cannot use runtime
|
|
304
|
+
`this`.
|
|
305
|
+
|
|
306
|
+
Computed fields are read-only and are rejected on package API, REST, GraphQL,
|
|
307
|
+
and registered operation writes. Generated schema, viewer manifest, and
|
|
308
|
+
TypeScript output include serializable metadata such as `computed` and
|
|
309
|
+
`readOnly`, but resolver functions stay in memory only.
|
|
310
|
+
|
|
311
|
+
REST resolves computed fields only when selected:
|
|
312
|
+
|
|
313
|
+
```txt
|
|
314
|
+
GET /db/users/u_1.json?select=id,fullName
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
GraphQL selections use the same projection/fanout layer. Collection reads prefer
|
|
318
|
+
`resolveMany` so one resolver can handle the selected page of records; single
|
|
319
|
+
reads and fields without `resolveMany` fall back to `resolve`.
|
|
320
|
+
|
|
321
|
+
Server code can call the same field resolvers without opening writable stores:
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
import { loadDbSchema } from '@async/db';
|
|
325
|
+
|
|
326
|
+
const schema = await loadDbSchema({ from: './db.schema.js' });
|
|
327
|
+
const userResolvers = schema.resolver('users', {
|
|
328
|
+
value: input,
|
|
329
|
+
context: {
|
|
330
|
+
locale: 'en-US',
|
|
331
|
+
nameFormatter,
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const fullName = await userResolvers.fullName();
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Use `schema.resolver('users.fullName')` when one field resolver is enough. The
|
|
339
|
+
call argument is plain JavaScript; schema authors can type and interpret it for
|
|
340
|
+
their own use case.
|
|
341
|
+
|
|
342
|
+
## Folder Content Collections
|
|
343
|
+
|
|
344
|
+
Use `index.schema.js` as an explicit folder-as-collection marker. The collection
|
|
345
|
+
name comes from the folder:
|
|
346
|
+
|
|
347
|
+
```txt
|
|
348
|
+
db/docs/index.schema.js
|
|
349
|
+
db/docs/intro.mdx
|
|
350
|
+
db/docs/getting-started.mdx
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Folder collections require an explicit `source` glob:
|
|
354
|
+
|
|
355
|
+
```js
|
|
356
|
+
import { collection, field, files } from '@async/db/schema';
|
|
357
|
+
|
|
358
|
+
export default collection({
|
|
359
|
+
source: files('./**/*.mdx', { read: 'frontmatter' }),
|
|
360
|
+
fields: {
|
|
361
|
+
id: field.string({ required: true }),
|
|
362
|
+
title: field.string({ required: true }),
|
|
363
|
+
body: field.string(),
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Runtime store behavior stays in `db.config.mjs`, not in the schema file:
|
|
369
|
+
|
|
370
|
+
```js
|
|
371
|
+
export default {
|
|
372
|
+
resources: {
|
|
373
|
+
docs: {
|
|
374
|
+
store: 'static',
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Core parses frontmatter and raw `.md` / `.mdx` body text. MDX compilation stays
|
|
381
|
+
app-owned JavaScript and is not a core dependency.
|
|
382
|
+
|
|
383
|
+
The built-in frontmatter parser is deliberately small and dependency-free. It
|
|
384
|
+
supports scalar `key: value` pairs plus the raw body string; keep arrays, nested
|
|
385
|
+
frontmatter, rich validation, and MDX compilation in app code when you need
|
|
386
|
+
them. The [content collections example](../examples/content-collections) shows
|
|
387
|
+
that pattern with docs and blog folders, static stores, relations to normal
|
|
388
|
+
fixture records, computed fields, and an example-owned preview renderer.
|
|
389
|
+
|
|
390
|
+
## Bundle And Unbundle
|
|
391
|
+
|
|
392
|
+
Single-resource bundle and unbundle commands keep their existing behavior:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
npm run db -- schema bundle users --out artifacts/users.bundle.schema.json
|
|
396
|
+
npm run db -- schema unbundle users
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
These single-resource JSON artifacts serialize Async DB metadata and seed data;
|
|
400
|
+
they do not serialize executable validator or resolver functions.
|
|
401
|
+
|
|
402
|
+
If you omit the resource in an interactive terminal, the CLI prompts for either
|
|
403
|
+
`All schemas` or a specific resource. Use `--all` in scripts to skip the prompt.
|
|
404
|
+
|
|
405
|
+
Aggregate root schema output is schema-only and never embeds seed/data fixtures:
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
npm run db -- schema bundle --all
|
|
409
|
+
npm run db -- schema unbundle --all --schema-dir db
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
If aggregate bundling finds non-empty `seed` embedded in a schema source and no
|
|
413
|
+
separate data fixture is loaded, it first writes that seed to
|
|
414
|
+
`db/<resource>.json`, then writes the root schema without seed. The default root
|
|
415
|
+
schema output is `db.schema.js` when the project root package is ESM, and
|
|
416
|
+
`db.schema.mjs` otherwise.
|
|
417
|
+
|
|
418
|
+
Folder collection source globs are rebased for the generated root file. For
|
|
419
|
+
example, `source: files('./**/*.mdx', { read: 'frontmatter' })` inside
|
|
420
|
+
`db/blog/index.schema.js` becomes
|
|
421
|
+
`source: files('./db/blog/**/*.mdx', { read: 'frontmatter' })` in
|
|
422
|
+
the root schema, so the registry can load the same content files.
|
|
423
|
+
|
|
424
|
+
When aggregate bundling sees computed resolvers or Standard Schema validators
|
|
425
|
+
from existing executable schema modules, the generated root schema imports the
|
|
426
|
+
original module, references its validator, and emits inline named resolver
|
|
427
|
+
wrappers to preserve behavior. Aggregate unbundle writes `.schema.js` files for
|
|
428
|
+
resources with executable validators or resolvers when the output folder can be
|
|
429
|
+
ESM. For the default `db/` folder, @async/db creates `db/package.json` with
|
|
430
|
+
`"type": "module"` when the root package is not already ESM and
|
|
431
|
+
`schema.autoModulePackageJson` is enabled. If `.js` would not load safely,
|
|
432
|
+
unbundle falls back to `.schema.mjs`. Schema, manifest, type,
|
|
433
|
+
doctor, bundle, unbundle, and generated starter commands import trusted schema
|
|
434
|
+
modules for metadata but do not call computed resolvers.
|
|
435
|
+
|
|
436
|
+
## Inference
|
|
437
|
+
|
|
438
|
+
Inspect inferred contracts:
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
npm run db -- schema infer
|
|
442
|
+
npm run db -- schema infer users
|
|
443
|
+
npm run db -- schema infer users --out db/users.schema.jsonc
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Use inference to move from fuzzy seed data toward explicit schema. If an explicit schema already exists, inference can still show what the current data implies.
|
|
447
|
+
|
|
448
|
+
## Source Readers
|
|
449
|
+
|
|
450
|
+
@async/db reads all source files through a reader pipeline. Built-in readers handle JSON, JSONC, CSV, and schema files. Add `sources.readers` when another file format should remain the source of truth.
|
|
451
|
+
|
|
452
|
+
```js
|
|
453
|
+
// db.config.mjs
|
|
454
|
+
// @ts-check
|
|
455
|
+
import { defineConfig } from '@async/db/config';
|
|
456
|
+
|
|
457
|
+
export default defineConfig({
|
|
458
|
+
sources: {
|
|
459
|
+
readers: [
|
|
460
|
+
{
|
|
461
|
+
name: 'pipe-data',
|
|
462
|
+
match({ file }) {
|
|
463
|
+
return file.endsWith('.pipe');
|
|
464
|
+
},
|
|
465
|
+
async read({ readText }) {
|
|
466
|
+
const rows = (await readText()).trim().split('\n');
|
|
467
|
+
return {
|
|
468
|
+
kind: 'data',
|
|
469
|
+
resourceName: 'users',
|
|
470
|
+
format: 'pipe',
|
|
471
|
+
data: rows.map((row) => {
|
|
472
|
+
const [id, name] = row.split('|');
|
|
473
|
+
return { id, name };
|
|
474
|
+
}),
|
|
475
|
+
};
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
],
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Custom readers run before built-in readers. The first reader that returns a result owns the file; returning `null` lets the next reader try. One file may return multiple sources, but every returned source must include `resourceName`.
|
|
484
|
+
|
|
485
|
+
## Nested Fixture Folders
|
|
486
|
+
|
|
487
|
+
Fixtures can be grouped under `db/` without changing resource names when basenames are unique:
|
|
488
|
+
|
|
489
|
+
```txt
|
|
490
|
+
db/
|
|
491
|
+
cms/
|
|
492
|
+
pages.schema.jsonc
|
|
493
|
+
pages.json
|
|
494
|
+
analytics/
|
|
495
|
+
charts.schema.jsonc
|
|
496
|
+
charts.json
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
That layout creates `pages` and `charts` resources. If nested folders contain repeated basenames, configure naming:
|
|
500
|
+
|
|
501
|
+
```js
|
|
502
|
+
import { defineConfig } from '@async/db/config';
|
|
503
|
+
|
|
504
|
+
export default defineConfig({
|
|
505
|
+
resources: {
|
|
506
|
+
naming: 'folder-prefixed',
|
|
507
|
+
},
|
|
508
|
+
});
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Naming options:
|
|
512
|
+
|
|
513
|
+
| Option | Example path | Resource name | Use when |
|
|
514
|
+
| --- | --- | --- | --- |
|
|
515
|
+
| `basename` | `db/cms/pages.json` | `pages` | Fixture basenames are unique. |
|
|
516
|
+
| `folder-prefixed` | `db/cms/pages.json` | `cmsPages` | Folders are domains and repeated filenames are common. |
|
|
517
|
+
| `path` | `db/cms/landing/pages.json` | `cmsLandingPages` | Deep folder structure should become part of the API name. |
|
|
518
|
+
| `customizeResource` | `db/marketing/pages.json` | `landingPages` | Public API names must be explicit and stable. |
|
|
519
|
+
|
|
520
|
+
Resource names affect state files, REST routes, GraphQL root fields, generated TypeScript, and relation targets.
|
|
521
|
+
|
|
522
|
+
## Related Examples
|
|
523
|
+
|
|
524
|
+
- [Data-first](../examples/data-first/README.md)
|
|
525
|
+
- [CSV](../examples/csv/README.md)
|
|
526
|
+
- [Schema-first](../examples/schema-first/README.md)
|
|
527
|
+
- [Advanced](../examples/advanced/README.md)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Fork And Branch Workflows
|
|
2
|
+
|
|
3
|
+
`@async/db` treats forks and branches as generic database lifecycle primitives. App code gives those primitives business meaning.
|
|
4
|
+
|
|
5
|
+
## Concepts
|
|
6
|
+
|
|
7
|
+
- `fork`: isolated logical database instance, useful for tenants, sandboxes, templates, demos, and prod-debug copies.
|
|
8
|
+
- `branch`: named data line inside one fork, useful for `main`, `draft`, `published`, previews, migrations, and debug work.
|
|
9
|
+
- `snapshot`: immutable captured state of a fork branch.
|
|
10
|
+
- `migration`: app-controlled move of one resource from one store to another.
|
|
11
|
+
|
|
12
|
+
Fork and branch records keep app labels in `metadata`. Core does not have a top-level `kind` field for lifecycle records because concepts like `tenant`, `debug`, `draft`, or `preview` are app-owned.
|
|
13
|
+
|
|
14
|
+
The default root database already points at `main`, so simple apps can call resources and operations directly:
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
await db.query('projects.list');
|
|
18
|
+
await db.collection('projects').all();
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
When an app has tenants or sandboxes, open or ensure the fork. A fork handle starts on its default `main` branch, so most app code does not need to mention `branch('main')`:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
const tenant = await db.forks.ensure('tenant_acme', {
|
|
25
|
+
from: 'main',
|
|
26
|
+
metadata: { purpose: 'tenant' },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await tenant.query('projects.list');
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Open named branches only when you intentionally leave `main`:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
const draft = await tenant.branches.open('draft');
|
|
36
|
+
|
|
37
|
+
await draft.query('projects.list');
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Free Plan To Paid Store
|
|
41
|
+
|
|
42
|
+
The app decides what "paid" means. `@async/db` only moves resources and switches routing:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
export async function upgradeTenantToPaid({ tenantId }) {
|
|
46
|
+
const tenant = await db.forks.open(tenantId);
|
|
47
|
+
|
|
48
|
+
const backup = await tenant.snapshots.create({
|
|
49
|
+
label: 'before-paid-upgrade',
|
|
50
|
+
resources: ['projects'],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await tenant.migrations.start('projects-to-postgres', {
|
|
54
|
+
resources: ['projects'],
|
|
55
|
+
mode: 'read-only',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await tenant.resources.migrate('projects', {
|
|
59
|
+
from: 'json',
|
|
60
|
+
to: 'postgres',
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await tenant.migrations.verify('projects-to-postgres', {
|
|
64
|
+
resources: ['projects'],
|
|
65
|
+
checks: ['count', 'checksum'],
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await tenant.routing.set({
|
|
69
|
+
projects: 'postgres',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await tenant.migrations.finish('projects-to-postgres');
|
|
73
|
+
return backup;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Before cutover, JSON is the live store. After cutover, Postgres is live and the JSON snapshot is backup/export data.
|
|
78
|
+
|
|
79
|
+
## Prod Debug Snapshot
|
|
80
|
+
|
|
81
|
+
Debugging a production issue should not mutate production data:
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
export async function createDebugForkFromSnapshot({ snapshotId, ticketId }) {
|
|
85
|
+
return db.forks.create(`debug_${ticketId}`, {
|
|
86
|
+
from: { snapshot: snapshotId },
|
|
87
|
+
metadata: {
|
|
88
|
+
purpose: 'debug',
|
|
89
|
+
ticketId,
|
|
90
|
+
ttl: '24h',
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The debug fork can run destructive reproductions, then be deleted.
|
|
97
|
+
|
|
98
|
+
## Other App-Owned Patterns
|
|
99
|
+
|
|
100
|
+
- Feature flag preview: branch flag resources, test rollout behavior, then promote.
|
|
101
|
+
- Settings rollback: snapshot settings before admin edits and restore if needed.
|
|
102
|
+
- Pricing plan staging: edit plan resources on a branch before publishing.
|
|
103
|
+
- Policy rule sandbox: branch permission rules and run access-decision tests.
|
|
104
|
+
- Prompt template experiment: compare generated outputs across prompt branches.
|
|
105
|
+
- Seed/demo template: create tenant forks from a template fork with `from: { fork: 'demo_template', branch: 'main' }`.
|
|
106
|
+
- Forked test environment: create a temporary fork, run destructive tests, delete it.
|
|
107
|
+
|
|
108
|
+
These helpers should live in app code. The package only owns the generic database lifecycle APIs.
|