@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,460 @@
|
|
|
1
|
+
import { loadConfig } from '../../config.js';
|
|
2
|
+
import { dbError, listChoices } from '../../errors.js';
|
|
3
|
+
import { resolveResource } from '../../names.js';
|
|
4
|
+
import { loadProjectSchema } from './project.js';
|
|
5
|
+
import { callFieldResolver, valueFromResolveManyResult } from './resolvers.js';
|
|
6
|
+
import { isPromiseLike, standardSchemaIssueDiagnostics } from './standard-schema.js';
|
|
7
|
+
import { validateRecordAgainstResource } from './validation.js';
|
|
8
|
+
export async function loadDbSchema(options = {}) {
|
|
9
|
+
const rawOptions = typeof options === 'string'
|
|
10
|
+
? { from: options }
|
|
11
|
+
: { ...options };
|
|
12
|
+
const load = (rawOptions.load ?? 'schema');
|
|
13
|
+
const config = await loadConfig({
|
|
14
|
+
...rawOptions,
|
|
15
|
+
load,
|
|
16
|
+
});
|
|
17
|
+
const project = await loadProjectSchema(config, {
|
|
18
|
+
load: config.schemaLoadMode ?? load,
|
|
19
|
+
});
|
|
20
|
+
return createDbSchema(project, config);
|
|
21
|
+
}
|
|
22
|
+
export function createDbSchema(project, config) {
|
|
23
|
+
const resources = new Map(project.resources.map((resource) => [resource.name, resource]));
|
|
24
|
+
return {
|
|
25
|
+
kind: 'DbSchema',
|
|
26
|
+
config,
|
|
27
|
+
loadMode: project.loadMode,
|
|
28
|
+
locator: project.locator,
|
|
29
|
+
rootSchema: project.rootSchema,
|
|
30
|
+
resources,
|
|
31
|
+
diagnostics: project.diagnostics,
|
|
32
|
+
schema: project.schema,
|
|
33
|
+
resource(name) {
|
|
34
|
+
return requireSchemaResource(resources, name);
|
|
35
|
+
},
|
|
36
|
+
resourceNames() {
|
|
37
|
+
return [...resources.keys()];
|
|
38
|
+
},
|
|
39
|
+
validator(name, options = {}) {
|
|
40
|
+
return createSchemaValidator(requireSchemaResource(resources, name), config, options);
|
|
41
|
+
},
|
|
42
|
+
resolver(selector, options = {}) {
|
|
43
|
+
const { resourceName, fieldName } = parseResolverSelector(selector);
|
|
44
|
+
const resource = requireSchemaResource(resources, resourceName);
|
|
45
|
+
if (fieldName) {
|
|
46
|
+
return createSchemaFieldResolver(resource, fieldName, config, options);
|
|
47
|
+
}
|
|
48
|
+
return createSchemaResourceResolverMap(resource, config, options);
|
|
49
|
+
},
|
|
50
|
+
validate(name, value, options = {}) {
|
|
51
|
+
return this.validator(name, options).validate(value);
|
|
52
|
+
},
|
|
53
|
+
assert(name, value, options = {}) {
|
|
54
|
+
return this.validator(name, options).assert(value);
|
|
55
|
+
},
|
|
56
|
+
validateAsync(name, value, options = {}) {
|
|
57
|
+
return this.validator(name, options).validateAsync(value);
|
|
58
|
+
},
|
|
59
|
+
assertAsync(name, value, options = {}) {
|
|
60
|
+
return this.validator(name, options).assertAsync(value);
|
|
61
|
+
},
|
|
62
|
+
toJSON() {
|
|
63
|
+
return project.schema;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export function createSchemaResourceResolverMap(resource, config, options = {}) {
|
|
68
|
+
const resolvers = {};
|
|
69
|
+
for (const fieldName of Object.keys(resource.resolvers?.fields ?? {})) {
|
|
70
|
+
resolvers[fieldName] = createSchemaFieldResolver(resource, fieldName, config, options);
|
|
71
|
+
}
|
|
72
|
+
return resolvers;
|
|
73
|
+
}
|
|
74
|
+
export function createSchemaFieldResolver(resource, fieldName, config, options = {}) {
|
|
75
|
+
const resolver = resource.resolvers?.fields?.[fieldName];
|
|
76
|
+
if (!resolver) {
|
|
77
|
+
throw dbError('DB_SCHEMA_RESOLVER_NOT_FOUND', `Resource "${resource.name}" does not define a resolver for field "${fieldName}".`, {
|
|
78
|
+
status: 404,
|
|
79
|
+
hint: `Use one of: ${listChoices(Object.keys(resource.resolvers?.fields ?? {}))}.`,
|
|
80
|
+
details: {
|
|
81
|
+
resource: resource.name,
|
|
82
|
+
field: fieldName,
|
|
83
|
+
availableFields: Object.keys(resource.resolvers?.fields ?? {}),
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const resolve = (async (args = {}) => {
|
|
88
|
+
if (typeof resolver.resolve === 'function') {
|
|
89
|
+
return callFieldResolver(resolver.resolve, normalizeResolverArgs(args), resolverCallOptions(resource, fieldName, config, options, args));
|
|
90
|
+
}
|
|
91
|
+
if (typeof resolver.resolveMany === 'function') {
|
|
92
|
+
const normalizedArgs = normalizeResolverArgs(args);
|
|
93
|
+
const record = normalizedArgs.record ?? normalizedArgs.value ?? {};
|
|
94
|
+
const manyArgs = {
|
|
95
|
+
...normalizedArgs,
|
|
96
|
+
records: [record],
|
|
97
|
+
};
|
|
98
|
+
const values = await callFieldResolver(resolver.resolveMany, manyArgs, resolverCallOptions(resource, fieldName, config, options, {
|
|
99
|
+
...spreadableRecord(args),
|
|
100
|
+
records: manyArgs.records,
|
|
101
|
+
}));
|
|
102
|
+
return valueFromResolveManyResult(values, resource, record, 0);
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
});
|
|
106
|
+
if (typeof resolver.resolve === 'function') {
|
|
107
|
+
resolve.resolve = resolve;
|
|
108
|
+
}
|
|
109
|
+
if (typeof resolver.resolveMany === 'function') {
|
|
110
|
+
resolve.resolveMany = async (args = {}) => {
|
|
111
|
+
const normalizedArgs = normalizeResolveManyArgs(args);
|
|
112
|
+
return callFieldResolver(resolver.resolveMany, normalizedArgs, resolverCallOptions(resource, fieldName, config, options, {
|
|
113
|
+
...spreadableRecord(args),
|
|
114
|
+
records: normalizedArgs.records,
|
|
115
|
+
}));
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return resolve;
|
|
119
|
+
}
|
|
120
|
+
export function createSchemaValidator(resource, config, options = {}) {
|
|
121
|
+
const mode = normalizeValidationMode(options.mode);
|
|
122
|
+
const unknownFields = normalizeValidatorUnknownFields(options.unknownFields);
|
|
123
|
+
function validate(value, validateOptions = {}) {
|
|
124
|
+
const result = validateInternal(value, validateOptions);
|
|
125
|
+
if (isPromiseLike(result)) {
|
|
126
|
+
throw asyncValidatorRequiredError(resource);
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
async function validateAsync(value, validateOptions = {}) {
|
|
131
|
+
return validateInternal(value, validateOptions, { allowAsync: true });
|
|
132
|
+
}
|
|
133
|
+
function validateInternal(value, validateOptions = {}, internalOptions = {}) {
|
|
134
|
+
const activeMode = normalizeValidationMode(validateOptions.mode ?? mode);
|
|
135
|
+
const activeUnknownFields = normalizeValidatorUnknownFields(validateOptions.unknownFields ?? unknownFields);
|
|
136
|
+
const source = validateOptions.source ?? options.source ?? `${resource.name} ${activeMode} input`;
|
|
137
|
+
const input = cloneInput(value);
|
|
138
|
+
const withDefaults = applyValidationDefaults(input, resource, {
|
|
139
|
+
mode: activeMode,
|
|
140
|
+
applyDefaults: validateOptions.applyDefaults ?? options.applyDefaults,
|
|
141
|
+
});
|
|
142
|
+
const standardResult = validateWithStandardSchema(resource, withDefaults);
|
|
143
|
+
if (isPromiseLike(standardResult)) {
|
|
144
|
+
if (!internalOptions.allowAsync) {
|
|
145
|
+
throw asyncValidatorRequiredError(resource);
|
|
146
|
+
}
|
|
147
|
+
return standardResult.then((resolvedStandardResult) => finishValidation(resolvedStandardResult, {
|
|
148
|
+
activeMode,
|
|
149
|
+
activeUnknownFields,
|
|
150
|
+
source,
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
return finishValidation(standardResult, {
|
|
154
|
+
activeMode,
|
|
155
|
+
activeUnknownFields,
|
|
156
|
+
source,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function finishValidation(standardResult, context) {
|
|
160
|
+
const { activeMode, activeUnknownFields, source } = context;
|
|
161
|
+
const standardValue = standardResult.value;
|
|
162
|
+
const sanitized = activeUnknownFields === 'strip'
|
|
163
|
+
? stripUnknownResourceFields(standardValue, resource)
|
|
164
|
+
: standardValue;
|
|
165
|
+
const validationConfig = {
|
|
166
|
+
...config,
|
|
167
|
+
schema: {
|
|
168
|
+
...config.schema,
|
|
169
|
+
unknownFields: activeUnknownFields === 'strip' || resource.fieldsAuthoritative === false
|
|
170
|
+
? 'allow'
|
|
171
|
+
: activeUnknownFields,
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
const validationResource = validationResourceForMode(resource, sanitized, activeMode);
|
|
175
|
+
const diagnostics = [
|
|
176
|
+
...standardResult.diagnostics,
|
|
177
|
+
...validateRecordAgainstResource(sanitized, validationResource, validationConfig, {
|
|
178
|
+
source,
|
|
179
|
+
requireFields: activeMode !== 'patch',
|
|
180
|
+
}),
|
|
181
|
+
];
|
|
182
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === 'error');
|
|
183
|
+
return {
|
|
184
|
+
ok: errors.length === 0,
|
|
185
|
+
value: sanitized,
|
|
186
|
+
diagnostics,
|
|
187
|
+
errors,
|
|
188
|
+
resource: resource.name,
|
|
189
|
+
mode: activeMode,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
resource: resource.name,
|
|
194
|
+
mode,
|
|
195
|
+
unknownFields,
|
|
196
|
+
validate,
|
|
197
|
+
validateAsync,
|
|
198
|
+
assert(value, assertOptions = {}) {
|
|
199
|
+
const result = validate(value, assertOptions);
|
|
200
|
+
if (result.ok) {
|
|
201
|
+
return result.value;
|
|
202
|
+
}
|
|
203
|
+
throw dbError('DB_SCHEMA_VALIDATION_FAILED', `${resource.name} input does not match its schema: ${result.errors[0].message}`, {
|
|
204
|
+
status: 400,
|
|
205
|
+
hint: 'Update the input to match the schema field types, required fields, enum values, constraints, and read-only field rules.',
|
|
206
|
+
details: {
|
|
207
|
+
resource: resource.name,
|
|
208
|
+
mode: result.mode,
|
|
209
|
+
diagnostics: result.diagnostics,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
async assertAsync(value, assertOptions = {}) {
|
|
214
|
+
const result = await validateAsync(value, assertOptions);
|
|
215
|
+
if (result.ok) {
|
|
216
|
+
return result.value;
|
|
217
|
+
}
|
|
218
|
+
throw dbError('DB_SCHEMA_VALIDATION_FAILED', `${resource.name} input does not match its schema: ${result.errors[0].message}`, {
|
|
219
|
+
status: 400,
|
|
220
|
+
hint: 'Update the input to match the schema field types, required fields, enum values, constraints, and read-only field rules.',
|
|
221
|
+
details: {
|
|
222
|
+
resource: resource.name,
|
|
223
|
+
mode: result.mode,
|
|
224
|
+
diagnostics: result.diagnostics,
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function validateWithStandardSchema(resource, value) {
|
|
231
|
+
const standardSchema = resource.validators?.standard;
|
|
232
|
+
const validate = standardSchema?.['~standard']?.validate;
|
|
233
|
+
if (typeof validate !== 'function') {
|
|
234
|
+
return {
|
|
235
|
+
value,
|
|
236
|
+
diagnostics: [],
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const result = validate(value);
|
|
240
|
+
if (isPromiseLike(result)) {
|
|
241
|
+
return result.then((resolved) => standardSchemaValidationResult(standardSchema, resource, value, resolved));
|
|
242
|
+
}
|
|
243
|
+
return standardSchemaValidationResult(standardSchema, resource, value, result);
|
|
244
|
+
}
|
|
245
|
+
function standardSchemaValidationResult(standardSchema, resource, inputValue, result) {
|
|
246
|
+
const issues = Array.isArray(result?.issues) ? result.issues : [];
|
|
247
|
+
return {
|
|
248
|
+
value: Object.prototype.hasOwnProperty.call(result ?? {}, 'value') ? result.value : inputValue,
|
|
249
|
+
diagnostics: standardSchemaIssueDiagnostics(standardSchema, issues, resource),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function asyncValidatorRequiredError(resource) {
|
|
253
|
+
return dbError('DB_SCHEMA_ASYNC_VALIDATOR_REQUIRED', `${resource.name} uses an async Standard Schema validator, but this helper is synchronous.`, {
|
|
254
|
+
status: 400,
|
|
255
|
+
hint: 'Use validateAsync(...) or assertAsync(...) for resources backed by async Standard Schema validators.',
|
|
256
|
+
details: {
|
|
257
|
+
resource: resource.name,
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
function parseResolverSelector(selector) {
|
|
262
|
+
const value = String(selector ?? '');
|
|
263
|
+
const [resourceName, ...fieldParts] = value.split('.');
|
|
264
|
+
const fieldName = fieldParts.join('.');
|
|
265
|
+
if (!resourceName) {
|
|
266
|
+
throw dbError('DB_SCHEMA_RESOLVER_SELECTOR_INVALID', `Invalid schema resolver selector ${JSON.stringify(selector)}.`, {
|
|
267
|
+
status: 400,
|
|
268
|
+
hint: 'Use "resourceName" for a resolver map or "resourceName.fieldName" for one field resolver.',
|
|
269
|
+
details: {
|
|
270
|
+
selector,
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
resourceName,
|
|
276
|
+
fieldName: fieldName || null,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function resolverCallOptions(resource, fieldName, config, options, args) {
|
|
280
|
+
const normalizedArgs = normalizeResolverArgs(args);
|
|
281
|
+
return {
|
|
282
|
+
db: options.db,
|
|
283
|
+
config,
|
|
284
|
+
resource,
|
|
285
|
+
fieldName,
|
|
286
|
+
cache: options.cache ?? new Map(),
|
|
287
|
+
context: options.context,
|
|
288
|
+
services: options.services ?? config.services,
|
|
289
|
+
value: options.value ?? normalizedArgs.record ?? normalizedArgs.records ?? normalizedArgs.value,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function normalizeResolverArgs(args) {
|
|
293
|
+
if (args === undefined || args === null) {
|
|
294
|
+
return {};
|
|
295
|
+
}
|
|
296
|
+
if (Array.isArray(args)) {
|
|
297
|
+
return { records: args };
|
|
298
|
+
}
|
|
299
|
+
if (typeof args === 'object') {
|
|
300
|
+
return args;
|
|
301
|
+
}
|
|
302
|
+
return { value: args };
|
|
303
|
+
}
|
|
304
|
+
function normalizeResolveManyArgs(args) {
|
|
305
|
+
const normalizedArgs = normalizeResolverArgs(args);
|
|
306
|
+
if (Array.isArray(normalizedArgs.records)) {
|
|
307
|
+
return normalizedArgs;
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
...normalizedArgs,
|
|
311
|
+
records: [],
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function spreadableRecord(value) {
|
|
315
|
+
if (value === null || value === undefined) {
|
|
316
|
+
return {};
|
|
317
|
+
}
|
|
318
|
+
return Object(value);
|
|
319
|
+
}
|
|
320
|
+
function requireSchemaResource(resources, name) {
|
|
321
|
+
const { resource, candidates } = resolveResource(resources, name);
|
|
322
|
+
if (resource) {
|
|
323
|
+
return resource;
|
|
324
|
+
}
|
|
325
|
+
throw dbError('DB_UNKNOWN_RESOURCE', `Unknown schema resource "${name}".`, {
|
|
326
|
+
status: 404,
|
|
327
|
+
hint: `Use one of: ${listChoices([...resources.keys()])}.`,
|
|
328
|
+
details: {
|
|
329
|
+
resource: name,
|
|
330
|
+
requestedResource: name,
|
|
331
|
+
normalizedCandidates: candidates,
|
|
332
|
+
availableResources: [...resources.keys()],
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
function normalizeValidationMode(value = 'create') {
|
|
337
|
+
if (value === 'create' || value === 'replace' || value === 'patch') {
|
|
338
|
+
return value;
|
|
339
|
+
}
|
|
340
|
+
throw dbError('DB_SCHEMA_VALIDATOR_MODE_INVALID', `Invalid schema validator mode ${JSON.stringify(value)}.`, {
|
|
341
|
+
status: 400,
|
|
342
|
+
hint: 'Use "create", "replace", or "patch".',
|
|
343
|
+
details: {
|
|
344
|
+
value,
|
|
345
|
+
allowed: ['create', 'replace', 'patch'],
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
function normalizeValidatorUnknownFields(value = 'error') {
|
|
350
|
+
if (value === 'ignore') {
|
|
351
|
+
return 'allow';
|
|
352
|
+
}
|
|
353
|
+
if (value === 'allow' || value === 'warn' || value === 'error' || value === 'strip') {
|
|
354
|
+
return value;
|
|
355
|
+
}
|
|
356
|
+
throw dbError('DB_SCHEMA_VALIDATOR_UNKNOWN_FIELDS_INVALID', `Invalid schema validator unknownFields setting ${JSON.stringify(value)}.`, {
|
|
357
|
+
status: 400,
|
|
358
|
+
hint: 'Use "error", "strip", "allow", "warn", or "ignore".',
|
|
359
|
+
details: {
|
|
360
|
+
value,
|
|
361
|
+
allowed: ['error', 'strip', 'allow', 'warn', 'ignore'],
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
function validationResourceForMode(resource, value, mode) {
|
|
366
|
+
if (mode !== 'create' || resource.kind !== 'collection' || !isPlainRecord(value)) {
|
|
367
|
+
return resource;
|
|
368
|
+
}
|
|
369
|
+
const idField = resource.idField ?? 'id';
|
|
370
|
+
if (value[idField] !== undefined || !resource.fields?.[idField]?.required) {
|
|
371
|
+
return resource;
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
...resource,
|
|
375
|
+
fields: {
|
|
376
|
+
...resource.fields,
|
|
377
|
+
[idField]: {
|
|
378
|
+
...resource.fields[idField],
|
|
379
|
+
required: false,
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function applyValidationDefaults(value, resource, options) {
|
|
385
|
+
if (options.mode !== 'create' || options.applyDefaults === false || !isPlainRecord(value)) {
|
|
386
|
+
return value;
|
|
387
|
+
}
|
|
388
|
+
const next = { ...value };
|
|
389
|
+
for (const [fieldName, field] of Object.entries(resource.fields ?? {})) {
|
|
390
|
+
if (next[fieldName] !== undefined || !Object.prototype.hasOwnProperty.call(field, 'default')) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (field.readOnly || field.computed) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
next[fieldName] = structuredClone(field.default);
|
|
397
|
+
}
|
|
398
|
+
return next;
|
|
399
|
+
}
|
|
400
|
+
function stripUnknownResourceFields(value, resource) {
|
|
401
|
+
if (resource.fieldsAuthoritative === false) {
|
|
402
|
+
return value;
|
|
403
|
+
}
|
|
404
|
+
return stripUnknownFields(value, resource.fields ?? {});
|
|
405
|
+
}
|
|
406
|
+
function stripUnknownFields(value, fields) {
|
|
407
|
+
if (!isPlainRecord(value)) {
|
|
408
|
+
return value;
|
|
409
|
+
}
|
|
410
|
+
const next = {};
|
|
411
|
+
for (const [fieldName, fieldValue] of Object.entries(value)) {
|
|
412
|
+
const field = fields[fieldName];
|
|
413
|
+
if (!field) {
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (field.type === 'object') {
|
|
417
|
+
next[fieldName] = stripUnknownObjectValue(fieldValue, field);
|
|
418
|
+
}
|
|
419
|
+
else if (field.type === 'array' && field.items?.type === 'object' && Array.isArray(fieldValue)) {
|
|
420
|
+
next[fieldName] = fieldValue.map((item) => stripUnknownObjectValue(item, field.items));
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
next[fieldName] = fieldValue;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return next;
|
|
427
|
+
}
|
|
428
|
+
function stripUnknownObjectValue(value, field) {
|
|
429
|
+
if (field.additionalProperties === true || !isPlainRecord(value)) {
|
|
430
|
+
return value;
|
|
431
|
+
}
|
|
432
|
+
return stripUnknownFields(value, fieldsForObjectValue(value, field));
|
|
433
|
+
}
|
|
434
|
+
function fieldsForObjectValue(value, field) {
|
|
435
|
+
if (!field.discriminator || !field.variants) {
|
|
436
|
+
return field.fields ?? {};
|
|
437
|
+
}
|
|
438
|
+
const discriminatorValue = value?.[field.discriminator];
|
|
439
|
+
const variant = field.variants[String(discriminatorValue)];
|
|
440
|
+
if (!variant) {
|
|
441
|
+
return field.fields ?? {};
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
[field.discriminator]: {
|
|
445
|
+
type: 'enum',
|
|
446
|
+
values: [String(discriminatorValue)],
|
|
447
|
+
required: true,
|
|
448
|
+
},
|
|
449
|
+
...(variant.fields ?? {}),
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function cloneInput(value) {
|
|
453
|
+
if (!isPlainRecord(value)) {
|
|
454
|
+
return value;
|
|
455
|
+
}
|
|
456
|
+
return structuredClone(value);
|
|
457
|
+
}
|
|
458
|
+
function isPlainRecord(value) {
|
|
459
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
460
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export type SchemaFieldDefinition = {
|
|
2
|
+
type: string;
|
|
3
|
+
fields?: Record<string, SchemaFieldDefinition>;
|
|
4
|
+
items?: SchemaFieldDefinition;
|
|
5
|
+
metadataOnly?: boolean;
|
|
6
|
+
computed?: boolean;
|
|
7
|
+
readOnly?: boolean;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
nullable?: boolean;
|
|
10
|
+
resolve?: ComputedResolveFunction;
|
|
11
|
+
resolveMany?: ComputedResolveManyFunction;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
};
|
|
14
|
+
export type SchemaResourceDefinition = {
|
|
15
|
+
kind?: 'collection' | 'document';
|
|
16
|
+
fields?: Record<string, SchemaFieldDefinition>;
|
|
17
|
+
validator?: unknown;
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
};
|
|
20
|
+
type FieldOptions = Omit<SchemaFieldDefinition, 'type'> & {
|
|
21
|
+
type?: string;
|
|
22
|
+
};
|
|
23
|
+
type ComputedResolveFunction = (...args: unknown[]) => unknown;
|
|
24
|
+
type ComputedResolveManyFunction = (...args: unknown[]) => unknown;
|
|
25
|
+
type ComputedResolver = {
|
|
26
|
+
resolve?: ComputedResolveFunction;
|
|
27
|
+
resolveMany?: ComputedResolveManyFunction;
|
|
28
|
+
};
|
|
29
|
+
type FilesOptions = {
|
|
30
|
+
read?: string;
|
|
31
|
+
};
|
|
32
|
+
export declare function collection(definition: SchemaResourceDefinition | unknown, options?: SchemaResourceDefinition | undefined): {
|
|
33
|
+
kind: "collection" | "document";
|
|
34
|
+
validator: import("./standard-schema.js").StandardSchemaV1;
|
|
35
|
+
fields?: Record<string, SchemaFieldDefinition>;
|
|
36
|
+
} | {
|
|
37
|
+
kind: "collection" | "document";
|
|
38
|
+
};
|
|
39
|
+
export declare function document(definition: SchemaResourceDefinition | unknown, options?: SchemaResourceDefinition | undefined): {
|
|
40
|
+
kind: "collection" | "document";
|
|
41
|
+
validator: import("./standard-schema.js").StandardSchemaV1;
|
|
42
|
+
fields?: Record<string, SchemaFieldDefinition>;
|
|
43
|
+
} | {
|
|
44
|
+
kind: "collection" | "document";
|
|
45
|
+
};
|
|
46
|
+
export declare function files(patterns: string | readonly string[], options?: FilesOptions): {
|
|
47
|
+
kind: string;
|
|
48
|
+
patterns: any[];
|
|
49
|
+
read: string;
|
|
50
|
+
};
|
|
51
|
+
export declare const field: {
|
|
52
|
+
string(options?: FieldOptions): SchemaFieldDefinition;
|
|
53
|
+
datetime(options?: FieldOptions): SchemaFieldDefinition;
|
|
54
|
+
number(options?: FieldOptions): SchemaFieldDefinition;
|
|
55
|
+
boolean(options?: FieldOptions): SchemaFieldDefinition;
|
|
56
|
+
enum(values: readonly (string | number | boolean)[], options?: FieldOptions): SchemaFieldDefinition;
|
|
57
|
+
object(fieldsOrOptions?: Record<string, SchemaFieldDefinition> | FieldOptions, maybeOptions?: FieldOptions): SchemaFieldDefinition;
|
|
58
|
+
array(items?: SchemaFieldDefinition, options?: FieldOptions): SchemaFieldDefinition;
|
|
59
|
+
json(options?: FieldOptions): SchemaFieldDefinition;
|
|
60
|
+
meta(options?: FieldOptions): SchemaFieldDefinition;
|
|
61
|
+
nullable(definition: SchemaFieldDefinition, options?: FieldOptions): {
|
|
62
|
+
nullable: boolean;
|
|
63
|
+
type: string;
|
|
64
|
+
fields?: Record<string, SchemaFieldDefinition>;
|
|
65
|
+
items?: SchemaFieldDefinition;
|
|
66
|
+
metadataOnly?: boolean;
|
|
67
|
+
computed?: boolean;
|
|
68
|
+
readOnly?: boolean;
|
|
69
|
+
required?: boolean;
|
|
70
|
+
resolve?: ComputedResolveFunction;
|
|
71
|
+
resolveMany?: ComputedResolveManyFunction;
|
|
72
|
+
};
|
|
73
|
+
computed(definition: SchemaFieldDefinition, resolver?: ComputedResolveFunction | ComputedResolver): {
|
|
74
|
+
computed: boolean;
|
|
75
|
+
readOnly: boolean;
|
|
76
|
+
required: boolean;
|
|
77
|
+
resolve: ComputedResolveFunction;
|
|
78
|
+
resolveMany: ComputedResolveManyFunction;
|
|
79
|
+
type: string;
|
|
80
|
+
fields?: Record<string, SchemaFieldDefinition>;
|
|
81
|
+
items?: SchemaFieldDefinition;
|
|
82
|
+
metadataOnly?: boolean;
|
|
83
|
+
nullable?: boolean;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { isStandardSchema } from './standard-schema.js';
|
|
2
|
+
export function collection(definition, options = undefined) {
|
|
3
|
+
return resourceDefinition('collection', definition, options);
|
|
4
|
+
}
|
|
5
|
+
export function document(definition, options = undefined) {
|
|
6
|
+
return resourceDefinition('document', definition, options);
|
|
7
|
+
}
|
|
8
|
+
export function files(patterns, options = {}) {
|
|
9
|
+
return {
|
|
10
|
+
kind: 'files',
|
|
11
|
+
patterns: Array.isArray(patterns) ? [...patterns] : [patterns],
|
|
12
|
+
read: options.read ?? 'frontmatter',
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function makeField(type, extras = {}) {
|
|
16
|
+
return {
|
|
17
|
+
type,
|
|
18
|
+
...extras,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export const field = {
|
|
22
|
+
string(options = {}) {
|
|
23
|
+
return makeField('string', options);
|
|
24
|
+
},
|
|
25
|
+
datetime(options = {}) {
|
|
26
|
+
return makeField('datetime', options);
|
|
27
|
+
},
|
|
28
|
+
number(options = {}) {
|
|
29
|
+
return makeField('number', options);
|
|
30
|
+
},
|
|
31
|
+
boolean(options = {}) {
|
|
32
|
+
return makeField('boolean', options);
|
|
33
|
+
},
|
|
34
|
+
enum(values, options = {}) {
|
|
35
|
+
return makeField('enum', {
|
|
36
|
+
values,
|
|
37
|
+
...options,
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
object(fieldsOrOptions = {}, maybeOptions = {}) {
|
|
41
|
+
if (isRecord(fieldsOrOptions) && fieldsOrOptions.fields) {
|
|
42
|
+
return makeField('object', fieldsOrOptions);
|
|
43
|
+
}
|
|
44
|
+
if (isFieldMap(fieldsOrOptions)) {
|
|
45
|
+
return makeField('object', {
|
|
46
|
+
fields: fieldsOrOptions,
|
|
47
|
+
...maybeOptions,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return makeField('object', fieldsOrOptions);
|
|
51
|
+
},
|
|
52
|
+
array(items = { type: 'unknown' }, options = {}) {
|
|
53
|
+
return makeField('array', {
|
|
54
|
+
items,
|
|
55
|
+
...options,
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
json(options = {}) {
|
|
59
|
+
return makeField('unknown', options);
|
|
60
|
+
},
|
|
61
|
+
meta(options = {}) {
|
|
62
|
+
const { type = 'unknown', ...metadata } = options;
|
|
63
|
+
return makeField(type, {
|
|
64
|
+
...metadata,
|
|
65
|
+
metadataOnly: true,
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
nullable(definition, options = {}) {
|
|
69
|
+
return {
|
|
70
|
+
...definition,
|
|
71
|
+
...options,
|
|
72
|
+
nullable: true,
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
computed(definition, resolver = {}) {
|
|
76
|
+
const normalizedResolver = typeof resolver === 'function'
|
|
77
|
+
? { resolve: resolver }
|
|
78
|
+
: resolver;
|
|
79
|
+
return {
|
|
80
|
+
...definition,
|
|
81
|
+
computed: true,
|
|
82
|
+
readOnly: true,
|
|
83
|
+
required: false,
|
|
84
|
+
resolve: normalizedResolver?.resolve,
|
|
85
|
+
resolveMany: normalizedResolver?.resolveMany,
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
function resourceDefinition(kind, definition, options) {
|
|
90
|
+
if (isStandardSchema(definition)) {
|
|
91
|
+
return {
|
|
92
|
+
...(options ?? {}),
|
|
93
|
+
kind,
|
|
94
|
+
validator: definition,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
...(isRecord(definition) ? definition : {}),
|
|
99
|
+
kind,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function isFieldMap(value) {
|
|
103
|
+
if (!value || Array.isArray(value) || typeof value !== 'object') {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return Object.values(value).some((entry) => entry && typeof entry === 'object' && 'type' in entry);
|
|
107
|
+
}
|
|
108
|
+
function isRecord(value) {
|
|
109
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
110
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
type FieldRelation = {
|
|
2
|
+
name: string;
|
|
3
|
+
to?: string;
|
|
4
|
+
toField: string;
|
|
5
|
+
cardinality: 'one' | 'many';
|
|
6
|
+
};
|
|
7
|
+
export type SchemaField = {
|
|
8
|
+
type?: string;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
nullable?: boolean;
|
|
11
|
+
computed?: boolean;
|
|
12
|
+
readOnly?: boolean;
|
|
13
|
+
description?: string;
|
|
14
|
+
default?: unknown;
|
|
15
|
+
unique?: boolean;
|
|
16
|
+
relation?: FieldRelation;
|
|
17
|
+
values?: unknown[];
|
|
18
|
+
items?: SchemaField;
|
|
19
|
+
fields?: Record<string, SchemaField>;
|
|
20
|
+
variants?: Record<string, SchemaVariant>;
|
|
21
|
+
discriminator?: string;
|
|
22
|
+
additionalProperties?: boolean;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
};
|
|
25
|
+
type SchemaVariant = {
|
|
26
|
+
fields?: Record<string, SchemaField>;
|
|
27
|
+
additionalProperties?: boolean;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
};
|
|
30
|
+
type InferFieldOptions = {
|
|
31
|
+
required?: boolean;
|
|
32
|
+
inferVariants?: boolean;
|
|
33
|
+
};
|
|
34
|
+
export declare function normalizeField(field: unknown, fieldName?: string): SchemaField;
|
|
35
|
+
export declare function inferFieldsFromData(value: unknown, kind?: string): Record<string, SchemaField>;
|
|
36
|
+
export declare function inferFieldFromSamples(samples: unknown[], fieldName: string, options?: InferFieldOptions): SchemaField;
|
|
37
|
+
export declare function inferFieldFromValue(value: unknown, fieldName: string, options?: InferFieldOptions): SchemaField;
|
|
38
|
+
export {};
|