@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,105 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { openDb } from '@async/db';
|
|
4
|
+
|
|
5
|
+
const paidStoreData = new Map();
|
|
6
|
+
|
|
7
|
+
export async function openUpgradeDemoDb() {
|
|
8
|
+
return openDb({
|
|
9
|
+
cwd: new URL('../', import.meta.url).pathname,
|
|
10
|
+
stores: {
|
|
11
|
+
default: 'json',
|
|
12
|
+
paidPostgres: {
|
|
13
|
+
read(resource, fallback) {
|
|
14
|
+
return paidStoreData.has(resource.name) ? paidStoreData.get(resource.name) : fallback;
|
|
15
|
+
},
|
|
16
|
+
write(resource, value) {
|
|
17
|
+
paidStoreData.set(resource.name, value);
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function upgradeTenantToPaid({ db, tenantId }) {
|
|
25
|
+
const tenant = await db.forks.ensure(tenantId, {
|
|
26
|
+
from: 'main',
|
|
27
|
+
metadata: {
|
|
28
|
+
purpose: 'tenant',
|
|
29
|
+
plan: 'free',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const backup = await tenant.snapshots.create({
|
|
34
|
+
label: 'before-paid-upgrade',
|
|
35
|
+
resources: ['projects'],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await tenant.migrations.start('projects-to-paid-postgres', {
|
|
39
|
+
resources: ['projects'],
|
|
40
|
+
mode: 'read-only',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
await tenant.resources.migrate('projects', {
|
|
44
|
+
from: 'json',
|
|
45
|
+
to: 'paidPostgres',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await tenant.migrations.verify('projects-to-paid-postgres', {
|
|
49
|
+
resources: ['projects'],
|
|
50
|
+
checks: ['count', 'checksum'],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await tenant.routing.set({
|
|
54
|
+
projects: 'paidPostgres',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await tenant.migrations.finish('projects-to-paid-postgres');
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
tenant,
|
|
61
|
+
backup,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function main() {
|
|
66
|
+
const db = await openUpgradeDemoDb();
|
|
67
|
+
const { tenant, backup } = await upgradeTenantToPaid({
|
|
68
|
+
db,
|
|
69
|
+
tenantId: 'tenant_acme',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
assert.equal(backup.resources.includes('projects'), true);
|
|
73
|
+
assert.deepEqual(await tenant.collection('projects').all(), [
|
|
74
|
+
{
|
|
75
|
+
id: 'p_1',
|
|
76
|
+
name: 'Launch checklist',
|
|
77
|
+
status: 'active',
|
|
78
|
+
},
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
await tenant.collection('projects').create({
|
|
82
|
+
id: 'p_2',
|
|
83
|
+
name: 'Paid workspace project',
|
|
84
|
+
status: 'active',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
assert.deepEqual(paidStoreData.get('projects'), [
|
|
88
|
+
{
|
|
89
|
+
id: 'p_1',
|
|
90
|
+
name: 'Launch checklist',
|
|
91
|
+
status: 'active',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 'p_2',
|
|
95
|
+
name: 'Paid workspace project',
|
|
96
|
+
status: 'active',
|
|
97
|
+
},
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
await db.close();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
104
|
+
await main();
|
|
105
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Hono Auth Example
|
|
2
|
+
|
|
3
|
+
## What This Teaches
|
|
4
|
+
|
|
5
|
+
Use this when your app already has a Hono server and you want db to own the CRUD routes while your app owns auth, permissions, and write normalization.
|
|
6
|
+
|
|
7
|
+
## Files To Inspect
|
|
8
|
+
|
|
9
|
+
- [src/app.mjs](./src/app.mjs): registers db REST routes with `beforeRequest`, `beforeWrite`, and a pages-specific create hook.
|
|
10
|
+
- [src/server.mjs](./src/server.mjs): starts the Hono app locally.
|
|
11
|
+
- [db/pages.schema.jsonc](./db/pages.schema.jsonc): schema-backed page collection with timestamps set by hooks.
|
|
12
|
+
- [db/users.schema.jsonc](./db/users.schema.jsonc): demo users used by the bearer-token sessions.
|
|
13
|
+
|
|
14
|
+
## Run It
|
|
15
|
+
|
|
16
|
+
From this example directory:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install
|
|
20
|
+
npm run sync
|
|
21
|
+
npm run dev
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Requests To Try
|
|
25
|
+
|
|
26
|
+
Leave `npm run dev` running and run these from another terminal.
|
|
27
|
+
|
|
28
|
+
Missing tokens are rejected:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
curl -i 'http://127.0.0.1:8787/api/pages'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Reader tokens can read:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
curl -H 'Authorization: Bearer user-token' 'http://127.0.0.1:8787/api/pages'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Reader tokens cannot write:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
curl -i -X PATCH 'http://127.0.0.1:8787/api/pages/home' \
|
|
44
|
+
-H 'Authorization: Bearer user-token' \
|
|
45
|
+
-H 'content-type: application/json' \
|
|
46
|
+
-d '{"title":"Draft"}'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Admin tokens can write. The shared write hook trims strings and sets `updatedAt`:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
curl -X PATCH 'http://127.0.0.1:8787/api/pages/home' \
|
|
53
|
+
-H 'Authorization: Bearer admin-token' \
|
|
54
|
+
-H 'content-type: application/json' \
|
|
55
|
+
-d '{"title":" Homepage "}'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Token Map
|
|
59
|
+
|
|
60
|
+
- `Bearer admin-token`: read and write.
|
|
61
|
+
- `Bearer user-token`: read only.
|
|
62
|
+
|
|
63
|
+
This is intentionally tiny demo auth. In a real app, `beforeRequest` would read your session or token source, and `beforeWrite` would call your permission policy.
|
|
64
|
+
|
|
65
|
+
## Features To Notice
|
|
66
|
+
|
|
67
|
+
- [Hono route registration](../../docs/integrations.md#hono-route-registration)
|
|
68
|
+
- [Lifecycle hooks](../../docs/integrations.md#hono-route-registration)
|
|
69
|
+
- [Schema-backed fixtures](../../docs/fixtures-and-schemas.md#schema-files)
|
|
70
|
+
|
|
71
|
+
## More Docs
|
|
72
|
+
|
|
73
|
+
- [Integrations](../../docs/integrations.md)
|
|
74
|
+
- [Server And Viewer](../../docs/server-and-viewer.md)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "collection",
|
|
3
|
+
"idField": "id",
|
|
4
|
+
"fields": {
|
|
5
|
+
"id": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"required": true,
|
|
8
|
+
"description": "Stable page id used in routes and links."
|
|
9
|
+
},
|
|
10
|
+
"title": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"required": true,
|
|
13
|
+
"description": "Human-readable page title."
|
|
14
|
+
},
|
|
15
|
+
"visibility": {
|
|
16
|
+
"type": "enum",
|
|
17
|
+
"values": ["public", "private"],
|
|
18
|
+
"default": "private",
|
|
19
|
+
"description": "Whether the page can be shown publicly."
|
|
20
|
+
},
|
|
21
|
+
"body": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Short markdown-like page body for the demo."
|
|
24
|
+
},
|
|
25
|
+
"createdAt": {
|
|
26
|
+
"type": "datetime",
|
|
27
|
+
"description": "Timestamp assigned by the pages create hook."
|
|
28
|
+
},
|
|
29
|
+
"updatedAt": {
|
|
30
|
+
"type": "datetime",
|
|
31
|
+
"description": "Timestamp assigned by the shared write hook."
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"seed": [
|
|
35
|
+
{
|
|
36
|
+
"id": "home",
|
|
37
|
+
"title": "Home",
|
|
38
|
+
"visibility": "public",
|
|
39
|
+
"body": "Welcome to the db Hono auth example.",
|
|
40
|
+
"createdAt": "2026-05-14T00:00:00.000Z",
|
|
41
|
+
"updatedAt": "2026-05-14T00:00:00.000Z"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "collection",
|
|
3
|
+
"idField": "id",
|
|
4
|
+
"fields": {
|
|
5
|
+
"id": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"required": true,
|
|
8
|
+
"description": "Stable user id."
|
|
9
|
+
},
|
|
10
|
+
"name": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"required": true,
|
|
13
|
+
"description": "Display name shown in the app."
|
|
14
|
+
},
|
|
15
|
+
"email": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"required": true,
|
|
18
|
+
"unique": true,
|
|
19
|
+
"description": "Email address used for local sign-in."
|
|
20
|
+
},
|
|
21
|
+
"role": {
|
|
22
|
+
"type": "enum",
|
|
23
|
+
"values": ["admin", "user"],
|
|
24
|
+
"default": "user",
|
|
25
|
+
"description": "Local authorization role."
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"seed": [
|
|
29
|
+
{
|
|
30
|
+
"id": "u_admin",
|
|
31
|
+
"name": "Admin Example",
|
|
32
|
+
"email": "admin@example.com",
|
|
33
|
+
"role": "admin"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "u_user",
|
|
37
|
+
"name": "Reader Example",
|
|
38
|
+
"email": "reader@example.com",
|
|
39
|
+
"role": "user"
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { defineConfig } from '@async/db/config';
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
dbDir: './db',
|
|
6
|
+
outputs: {
|
|
7
|
+
stateDir: './.db',
|
|
8
|
+
types: './.db/types/index.d.ts',
|
|
9
|
+
},
|
|
10
|
+
types: {
|
|
11
|
+
enabled: true,
|
|
12
|
+
emitComments: true,
|
|
13
|
+
},
|
|
14
|
+
schema: {
|
|
15
|
+
unknownFields: 'warn',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "db-hono-auth-example",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "node ./src/server.mjs",
|
|
7
|
+
"sync": "async-db sync --cwd ."
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@hono/node-server": "^1.13.8",
|
|
11
|
+
"hono": "^4.6.0",
|
|
12
|
+
"@async/db": "file:../.."
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { openDb } from '@async/db';
|
|
4
|
+
import { registerDbRoutes } from '@async/db/hono';
|
|
5
|
+
|
|
6
|
+
const exampleRoot = fileURLToPath(new URL('..', import.meta.url));
|
|
7
|
+
|
|
8
|
+
export const demoAuthorizationHeaders = {
|
|
9
|
+
admin: 'Bearer admin-token',
|
|
10
|
+
user: 'Bearer user-token',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const demoSessions = new Map([
|
|
14
|
+
[authorizationToken(demoAuthorizationHeaders.admin), { userId: 'u_admin', role: 'admin' }],
|
|
15
|
+
[authorizationToken(demoAuthorizationHeaders.user), { userId: 'u_user', role: 'user' }],
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export async function createApp(options = {}) {
|
|
19
|
+
const { Hono } = await import('hono');
|
|
20
|
+
const app = new Hono();
|
|
21
|
+
const db = options.db ?? await openDb({
|
|
22
|
+
cwd: options.cwd ?? path.resolve(exampleRoot),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
registerDbRoutes(app, db, {
|
|
26
|
+
prefix: '/api',
|
|
27
|
+
resources: ['pages', 'users'],
|
|
28
|
+
lifecycleHooks: {
|
|
29
|
+
beforeRequest(ctx) {
|
|
30
|
+
const session = sessionFromAuthorizationHeader(ctx.c.req.header('authorization'));
|
|
31
|
+
if (!session) {
|
|
32
|
+
return ctx.c.json({ error: 'Unauthorized' }, 401);
|
|
33
|
+
}
|
|
34
|
+
ctx.c.set('session', session);
|
|
35
|
+
},
|
|
36
|
+
beforeWrite(ctx) {
|
|
37
|
+
const session = ctx.c.get('session');
|
|
38
|
+
if (session?.role !== 'admin') {
|
|
39
|
+
return ctx.c.json({ error: 'Forbidden' }, 403);
|
|
40
|
+
}
|
|
41
|
+
normalizeWriteBody(ctx.body);
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
resourceOptions: {
|
|
45
|
+
pages: {
|
|
46
|
+
hooks: {
|
|
47
|
+
beforeCreate(ctx) {
|
|
48
|
+
ctx.body.createdAt ??= ctx.body.updatedAt;
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return app;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function sessionFromAuthorizationHeader(header) {
|
|
59
|
+
return demoSessions.get(authorizationToken(header)) ?? null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function authorizationToken(header) {
|
|
63
|
+
const match = /^Bearer\s+(.+)$/i.exec(String(header ?? ''));
|
|
64
|
+
return match?.[1] ?? null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizeWriteBody(body) {
|
|
68
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const [key, value] of Object.entries(body)) {
|
|
73
|
+
if (typeof value === 'string') {
|
|
74
|
+
body[key] = value.trim();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
body.updatedAt = new Date().toISOString();
|
|
79
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { serve } from '@hono/node-server';
|
|
2
|
+
import { createApp } from './app.mjs';
|
|
3
|
+
|
|
4
|
+
const port = Number(process.env.PORT ?? 8787);
|
|
5
|
+
const app = await createApp();
|
|
6
|
+
|
|
7
|
+
serve({
|
|
8
|
+
fetch: app.fetch,
|
|
9
|
+
hostname: '127.0.0.1',
|
|
10
|
+
port,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
console.log(`db Hono auth example: http://127.0.0.1:${port}`);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Production JSON Example
|
|
2
|
+
|
|
3
|
+
## What This Teaches
|
|
4
|
+
|
|
5
|
+
Use this when an app has small production control-plane data that should stay in reviewed JSON while browser traffic goes through registered operations. It models feature flags and public app settings as file-backed JSON resources, then exposes only operation refs to client code.
|
|
6
|
+
|
|
7
|
+
## Files To Inspect
|
|
8
|
+
|
|
9
|
+
- [db/featureFlags.schema.jsonc](./db/featureFlags.schema.jsonc): low-write feature flag records with explicit schema.
|
|
10
|
+
- [db/appSettings.schema.jsonc](./db/appSettings.schema.jsonc): singleton production settings document.
|
|
11
|
+
- [db/operations](./db/operations): registered operation templates for browser-facing reads.
|
|
12
|
+
- [db.config.mjs](./db.config.mjs): enables operation-only REST exposure and ref-only operation calls.
|
|
13
|
+
- [src/client-demo.mjs](./src/client-demo.mjs): tiny client script that calls generated operation refs.
|
|
14
|
+
- [src/generated/db.types.d.ts](./src/generated/db.types.d.ts): committed generated types.
|
|
15
|
+
|
|
16
|
+
## Run It
|
|
17
|
+
|
|
18
|
+
From the repository root, use the repo-internal CLI path:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm run db -- sync --cwd ./examples/production-json
|
|
22
|
+
npm run db -- operations build --cwd ./examples/production-json
|
|
23
|
+
npm run db -- serve --cwd ./examples/production-json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
In another terminal:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
ASYNC_DB_URL=http://127.0.0.1:7331 node ./examples/production-json/src/client-demo.mjs
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Expected Result
|
|
33
|
+
|
|
34
|
+
`sync` writes generated schema, types, and runtime state under `examples/production-json/.db/`, plus the committed type copy in `src/generated/`.
|
|
35
|
+
|
|
36
|
+
`operations build` writes client-safe operation refs under `examples/production-json/src/generated/`. The server reads templates from `db/operations`, while the demo script reads generated refs and calls `GetControlPlane` and `GetFeatureFlag` through `client.query()`.
|
|
37
|
+
|
|
38
|
+
To review the browser-facing operation contract without volatile timestamps:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm run db -- operations contract --cwd ./examples/production-json
|
|
42
|
+
npm run db -- operations contract --cwd ./examples/production-json --check
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Operation Requests To Try
|
|
46
|
+
|
|
47
|
+
Build operations, then use the generated ref from `examples/production-json/src/generated/db.operation-refs.json`:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
curl -X POST http://127.0.0.1:7331/__db/operations/REF \
|
|
51
|
+
-H 'content-type: application/json' \
|
|
52
|
+
-d '{"variables":{"id":"flag_billing_v2"}}'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Raw REST routes are intentionally not the production-facing API in this example. `server.expose.rest: 'registered-only'` keeps app traffic on the registered operation boundary while the JSON files remain the first-party store.
|
|
56
|
+
|
|
57
|
+
## Why This Shape?
|
|
58
|
+
|
|
59
|
+
Feature flags and app settings are control-plane resources: small, low-write, easy to review, and useful to snapshot. That makes them a realistic production fit for `@async/db/json`.
|
|
60
|
+
|
|
61
|
+
The app should still evaluate sensitive targeting, auth, rate limits, and policy in app-owned code. The JSON store keeps the reviewed flag definitions; registered operations keep browser calls stable if a resource later graduates to SQLite, Postgres, Redis, or a custom store.
|
|
62
|
+
|
|
63
|
+
## Graduating One Resource
|
|
64
|
+
|
|
65
|
+
Keep `appSettings` and `featureFlags` on JSON when they remain small and low-write. If a new `orders` resource outgrows JSON, add a database store and move only that resource:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
import { defineConfig } from '@async/db/config';
|
|
69
|
+
import { postgresStore } from '@async/db/postgres';
|
|
70
|
+
import { pool } from './src/server/postgres-client.js';
|
|
71
|
+
|
|
72
|
+
export default defineConfig({
|
|
73
|
+
stores: {
|
|
74
|
+
default: 'json',
|
|
75
|
+
appDb: postgresStore({ client: pool }),
|
|
76
|
+
},
|
|
77
|
+
resources: {
|
|
78
|
+
appSettings: { store: 'json' },
|
|
79
|
+
featureFlags: { store: 'json' },
|
|
80
|
+
orders: { store: 'appDb' },
|
|
81
|
+
},
|
|
82
|
+
operations: {
|
|
83
|
+
enabled: true,
|
|
84
|
+
acceptRefs: 'ref',
|
|
85
|
+
sourceDir: './db/operations',
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Operation templates that read `appSettings`, `featureFlags`, or `orders` stay behind the same `client.query(ref, variables)` call shape. The resource store changes; the browser contract does not.
|
|
91
|
+
|
|
92
|
+
## Features To Notice
|
|
93
|
+
|
|
94
|
+
- [Production JSON Database](../../docs/json-production.md)
|
|
95
|
+
- [Resource Graduation And Mixed Stores](../../docs/store-graduation.md)
|
|
96
|
+
- [Registered REST operations](../../docs/server-and-viewer.md#registered-rest-operations)
|
|
97
|
+
- [Operation-only exposure](../../docs/configuration.md#server-exposure)
|
|
98
|
+
- [Generated operation refs](../../docs/generated-files.md#operation-registry-output)
|
|
99
|
+
|
|
100
|
+
## Cleanup
|
|
101
|
+
|
|
102
|
+
Generated `.db/` output is ignored by git and can be removed whenever you want a fresh mirror. Generated operation ref files under `src/generated/` are safe to regenerate after operation template changes.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "document",
|
|
3
|
+
"description": "Small production settings that are safe to keep as reviewed JSON.",
|
|
4
|
+
"fields": {
|
|
5
|
+
"appName": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"required": true,
|
|
8
|
+
"description": "Public product name shown by the app shell."
|
|
9
|
+
},
|
|
10
|
+
"environment": {
|
|
11
|
+
"type": "enum",
|
|
12
|
+
"values": ["development", "staging", "production"],
|
|
13
|
+
"required": true,
|
|
14
|
+
"description": "Deployment lane this settings file belongs to."
|
|
15
|
+
},
|
|
16
|
+
"maintenanceMode": {
|
|
17
|
+
"type": "boolean",
|
|
18
|
+
"required": true,
|
|
19
|
+
"default": false,
|
|
20
|
+
"description": "Whether the public app should show maintenance UI."
|
|
21
|
+
},
|
|
22
|
+
"defaultLocale": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"required": true,
|
|
25
|
+
"default": "en-US",
|
|
26
|
+
"description": "Fallback locale for unauthenticated visitors."
|
|
27
|
+
},
|
|
28
|
+
"supportEmail": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"required": true,
|
|
31
|
+
"description": "Public support contact shown in product chrome."
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"seed": {
|
|
35
|
+
"appName": "Launch Console",
|
|
36
|
+
"environment": "production",
|
|
37
|
+
"maintenanceMode": false,
|
|
38
|
+
"defaultLocale": "en-US",
|
|
39
|
+
"supportEmail": "support@example.test"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "collection",
|
|
3
|
+
"description": "Low-write production feature flags that control app behavior.",
|
|
4
|
+
"idField": "id",
|
|
5
|
+
"fields": {
|
|
6
|
+
"id": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"required": true,
|
|
9
|
+
"description": "Stable flag id used by operations and review notes."
|
|
10
|
+
},
|
|
11
|
+
"key": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"required": true,
|
|
14
|
+
"unique": true,
|
|
15
|
+
"description": "Stable application flag key."
|
|
16
|
+
},
|
|
17
|
+
"enabled": {
|
|
18
|
+
"type": "boolean",
|
|
19
|
+
"required": true,
|
|
20
|
+
"default": false,
|
|
21
|
+
"description": "Global default before app-owned targeting runs."
|
|
22
|
+
},
|
|
23
|
+
"audience": {
|
|
24
|
+
"type": "enum",
|
|
25
|
+
"values": ["everyone", "staff", "beta", "enterprise"],
|
|
26
|
+
"required": true,
|
|
27
|
+
"default": "staff",
|
|
28
|
+
"description": "Coarse audience label consumed by the app policy layer."
|
|
29
|
+
},
|
|
30
|
+
"rolloutPercent": {
|
|
31
|
+
"type": "number",
|
|
32
|
+
"required": true,
|
|
33
|
+
"default": 0,
|
|
34
|
+
"description": "Default rollout percentage for app-owned evaluation."
|
|
35
|
+
},
|
|
36
|
+
"description": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"required": true,
|
|
39
|
+
"description": "Human review note explaining the flag."
|
|
40
|
+
},
|
|
41
|
+
"owner": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"required": true,
|
|
44
|
+
"description": "Team or person responsible for changing this flag."
|
|
45
|
+
},
|
|
46
|
+
"updatedAt": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"required": true,
|
|
49
|
+
"description": "ISO timestamp for the last reviewed flag change."
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"seed": [
|
|
53
|
+
{
|
|
54
|
+
"id": "flag_billing_v2",
|
|
55
|
+
"key": "billing.v2",
|
|
56
|
+
"enabled": true,
|
|
57
|
+
"audience": "beta",
|
|
58
|
+
"rolloutPercent": 25,
|
|
59
|
+
"description": "Expose the new billing flow to beta accounts.",
|
|
60
|
+
"owner": "growth",
|
|
61
|
+
"updatedAt": "2026-05-01T12:00:00Z"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"id": "flag_enterprise_dashboard",
|
|
65
|
+
"key": "dashboard.enterprise",
|
|
66
|
+
"enabled": true,
|
|
67
|
+
"audience": "enterprise",
|
|
68
|
+
"rolloutPercent": 100,
|
|
69
|
+
"description": "Show enterprise reporting navigation.",
|
|
70
|
+
"owner": "platform",
|
|
71
|
+
"updatedAt": "2026-05-03T09:30:00Z"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "flag_editor_command_palette",
|
|
75
|
+
"key": "editor.commandPalette",
|
|
76
|
+
"enabled": false,
|
|
77
|
+
"audience": "staff",
|
|
78
|
+
"rolloutPercent": 0,
|
|
79
|
+
"description": "Internal preview of command palette shortcuts.",
|
|
80
|
+
"owner": "product",
|
|
81
|
+
"updatedAt": "2026-05-05T16:45:00Z"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "GetControlPlane",
|
|
3
|
+
"kind": "graphql",
|
|
4
|
+
"operationName": "GetControlPlane",
|
|
5
|
+
"query": "query GetControlPlane { appSettings { appName environment maintenanceMode defaultLocale supportEmail } featureFlags { id key enabled audience rolloutPercent description owner updatedAt } }"
|
|
6
|
+
}
|