@better-schemic/core 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +40 -0
  3. package/lib/authoring.d.ts +114 -0
  4. package/lib/authoring.js +242 -0
  5. package/lib/authoring.js.map +1 -0
  6. package/lib/chunk-26D7WX7Q.js +31 -0
  7. package/lib/chunk-26D7WX7Q.js.map +1 -0
  8. package/lib/chunk-IUPOUD4L.js +330 -0
  9. package/lib/chunk-IUPOUD4L.js.map +1 -0
  10. package/lib/chunk-LC3VHUM2.js +56 -0
  11. package/lib/chunk-LC3VHUM2.js.map +1 -0
  12. package/lib/chunk-RSGP7GVO.js +252 -0
  13. package/lib/chunk-RSGP7GVO.js.map +1 -0
  14. package/lib/client-HZF4ZWGO.js +13 -0
  15. package/lib/client-HZF4ZWGO.js.map +1 -0
  16. package/lib/config-BYh7WA4P.d.ts +259 -0
  17. package/lib/config.d.ts +2 -0
  18. package/lib/config.js +27 -0
  19. package/lib/config.js.map +1 -0
  20. package/lib/driver-LVldBEhS.d.ts +818 -0
  21. package/lib/driver.d.ts +151 -0
  22. package/lib/driver.js +47 -0
  23. package/lib/driver.js.map +1 -0
  24. package/lib/index.d.ts +154 -0
  25. package/lib/index.js +758 -0
  26. package/lib/index.js.map +1 -0
  27. package/lib/query.d.ts +81 -0
  28. package/lib/query.js +30 -0
  29. package/lib/query.js.map +1 -0
  30. package/lib/secrets-BETi5p8g.d.ts +26 -0
  31. package/lib/testing.d.ts +99 -0
  32. package/lib/testing.js +212 -0
  33. package/lib/testing.js.map +1 -0
  34. package/package.json +102 -0
  35. package/src/authoring.ts +360 -0
  36. package/src/cli-kit/config.ts +226 -0
  37. package/src/cli-kit/diff.ts +273 -0
  38. package/src/cli-kit/filter.ts +159 -0
  39. package/src/cli-kit/merge.ts +380 -0
  40. package/src/cli-kit/meta.ts +123 -0
  41. package/src/cli-kit/pager.ts +42 -0
  42. package/src/cli-kit/schema.ts +214 -0
  43. package/src/cli-kit/style.ts +24 -0
  44. package/src/client.ts +244 -0
  45. package/src/config.ts +199 -0
  46. package/src/connection.ts +120 -0
  47. package/src/driver/driver.ts +413 -0
  48. package/src/driver/index.ts +31 -0
  49. package/src/driver/portable-ir.ts +51 -0
  50. package/src/driver/portable.ts +124 -0
  51. package/src/driver/sdk.ts +73 -0
  52. package/src/index.ts +185 -0
  53. package/src/kind/index.ts +28 -0
  54. package/src/kind/plan.ts +412 -0
  55. package/src/kind/registry.ts +270 -0
  56. package/src/query/call.ts +21 -0
  57. package/src/query/codec.ts +33 -0
  58. package/src/query/index.ts +22 -0
  59. package/src/query/project.ts +25 -0
  60. package/src/query/ref.ts +32 -0
  61. package/src/query.ts +5 -0
  62. package/src/secrets.ts +61 -0
  63. package/src/seed.ts +14 -0
  64. package/src/testing.ts +402 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/testing.ts"],"sourcesContent":["// A shared DRIVER CONFORMANCE suite — the runtime contract a `@better-schemic/<driver>` must satisfy, asserted\n// with `bun:test`. Each driver runs it against its own authoring surface:\n//\n// import { describeDriverConformance } from \"@better-schemic/core/testing\";\n// import { defineTable, s, surrealDriver } from \"@better-schemic/surrealdb\";\n// describeDriverConformance({ name: \"surrealdb\", s, driver: surrealDriver, defineEntity: defineTable });\n//\n// WHY a test, not a type: the zod drop-in builders (`s.string()` = `new <D>Field(z.string())`) are\n// mechanically identical across drivers, but TypeScript has NO higher-kinded types, so a generic core\n// factory can't preserve each driver's field type (it collapses to the base, dropping `$`-methods).\n// Each driver therefore hand-authors its drop-ins, and \"`s` is a Zod SUPERSET\" is enforceable only at\n// runtime. This suite is that enforcement.\n//\n// It DUCK-TYPES fields (a field is \"something with a `.schema` that is a Zod type\") rather than using\n// `instanceof SFieldBase` — a driver may extend its own copy of the base, so identity checks are unsafe.\n\nimport { describe, expect, test } from \"bun:test\";\nimport { readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type * as z from \"zod\";\nimport { type Driver, driverNames, getDriver } from \"./driver/driver\";\nimport { emitKinds, type KindRegistry, lowerSchema } from \"./kind\";\n\n/**\n * The driver's authoring namespace (`s`) — a bag of field builders, some NESTED (e.g.\n * `s.iso.{date,time,datetime,duration}`). Intentionally loose: the suite duck-types fields and\n * enforces the real contract at RUNTIME, not via this type (see the header note), so a precise\n * \"function-or-nested-namespace\" shape would only fight the internal `s.<key>()` calls for no gain.\n */\n// biome-ignore lint/suspicious/noExplicitAny: a driver's `s` is dialect-specific + may nest; runtime-duck-typed.\ntype Authoring = Record<string, any>;\n\nexport interface DriverConformanceOptions {\n /** The driver's registry name (e.g. `\"surrealdb\"`). */\n name: string;\n /** The driver's authoring namespace — the `s` each package exports. */\n s: Authoring;\n /** The driver under test (already registered by importing its package). */\n driver: Driver<unknown>;\n /**\n * Authors the driver's primary fielded definable — a table, collection, node-type, … — from a name\n * and a field shape. Used to lower a probe object through the pipeline. Drivers pass their own\n * `define*` for this (e.g. `defineEntity: defineTable`); the suite stays shape-agnostic.\n */\n // biome-ignore lint/suspicious/noExplicitAny: dialect-specific definable/shape types.\n defineEntity: (name: string, shape: Record<string, any>) => any;\n}\n\n/**\n * The canonical zod DROP-IN set every driver's `s` MUST expose — the structural Zod builders that make\n * a `@better-schemic/<driver>` a drop-in for `z`. Each maps to the DB's natural representation (a driver may\n * also offer richer native aliases, e.g. `text`/`varchar` alongside `string`). `object`/`array` nest a\n * `literal` (present everywhere) so a missing `string` doesn't cascade into their tests.\n */\nconst DROP_INS: { key: string; build: (s: Authoring) => unknown }[] = [\n { key: \"string\", build: (s) => s.string() },\n { key: \"number\", build: (s) => s.number() },\n { key: \"boolean\", build: (s) => s.boolean() },\n { key: \"date\", build: (s) => s.date() },\n { key: \"literal\", build: (s) => s.literal(\"a\") },\n { key: \"enum\", build: (s) => s.enum([\"a\", \"b\"]) },\n { key: \"object\", build: (s) => s.object({ inner: s.literal(\"a\") }) },\n { key: \"array\", build: (s) => s.array(s.literal(\"a\")) },\n];\n\n/** Value pairs that prove a scalar drop-in really carries the right Zod schema (unambiguous scalars only). */\nconst SCALAR_CHECKS: { key: string; valid: unknown; invalid: unknown }[] = [\n { key: \"string\", valid: \"hello\", invalid: 123 },\n { key: \"number\", valid: 123, invalid: \"hello\" },\n { key: \"boolean\", valid: true, invalid: \"hello\" },\n];\n\n/** Duck-typed: a field exposes a Zod `.schema`; a raw Zod type IS the schema. Throws if neither. */\nfunction toSchema(v: unknown): z.ZodType {\n const field = v as { schema?: unknown } | null;\n if (field && isZod(field.schema)) return field.schema as z.ZodType;\n if (isZod(v)) return v as z.ZodType;\n throw new Error(\"expected a field (with a `.schema` Zod type) or a Zod type\");\n}\n\nfunction isZod(v: unknown): boolean {\n return !!v && typeof (v as { safeParse?: unknown }).safeParse === \"function\";\n}\n\n/** Is `v` a driver field (has a `.schema` that is a Zod type)? */\nfunction isField(v: unknown): boolean {\n return isZod((v as { schema?: unknown } | null)?.schema);\n}\n\n/**\n * Assert a `@better-schemic/<driver>` conforms to the Better-schemic driver contract: the Driver is registered with\n * the IR pipeline + execution ops, and its `s` is a Zod-drop-in SUPERSET (the canonical drop-in set is\n * present, carries the right schemas, composes through wrappers, and lowers to the portable IR).\n */\nexport function describeDriverConformance(\n opts: DriverConformanceOptions,\n): void {\n const { name, s, driver, defineEntity } = opts;\n\n describe(`driver conformance: ${name}`, () => {\n describe(\"Driver contract\", () => {\n test(\"is registered under its name\", () => {\n expect(driverNames()).toContain(name);\n expect(getDriver(name)).toBe(driver);\n expect(driver.name).toBe(name);\n });\n\n test(\"exposes a kind registry + the schema/execution ops\", () => {\n // Schema ops are generic over `registry`; the driver provides the fan-out + execution.\n expect(driver.registry).toBeDefined();\n expect(typeof driver.registry.entries).toBe(\"function\");\n expect(driver.registry.names().length).toBeGreaterThan(0);\n for (const op of [\n \"explode\",\n \"introspectAll\",\n \"connect\",\n \"apply\",\n \"close\",\n ] as const) {\n expect(typeof driver[op]).toBe(\"function\");\n }\n });\n });\n\n describe(\"zod drop-in surface (s.* is a Zod superset)\", () => {\n for (const { key, build } of DROP_INS) {\n test(`s.${key}() exists and returns a field`, () => {\n expect(typeof s[key]).toBe(\"function\");\n const field = build(s);\n expect(isField(field)).toBe(true);\n });\n }\n\n for (const { key, valid, invalid } of SCALAR_CHECKS) {\n test(`s.${key}() carries a \"${key}\" Zod schema`, () => {\n const schema = toSchema(s[key]());\n expect(schema.safeParse(valid).success).toBe(true);\n expect(schema.safeParse(invalid).success).toBe(false);\n });\n }\n });\n\n describe(\"Zod-clean codecs + wrappers\", () => {\n test(\"decode/encode delegate to the inner Zod schema\", () => {\n const field = s.string() as {\n decode: (v: unknown) => unknown;\n encode: (v: unknown) => unknown;\n };\n expect(field.decode(\"hi\")).toBe(\"hi\");\n expect(field.encode(\"hi\")).toBe(\"hi\");\n });\n\n test(\"wrappers preserve field-ness (optional/array compose)\", () => {\n const field = s.string() as {\n optional: () => unknown;\n array: () => unknown;\n };\n expect(isField(field.optional())).toBe(true);\n expect(isField(field.array())).toBe(true);\n });\n });\n\n describe(\"lowering (drop-in fields → kind registry)\", () => {\n test(\"an entity of drop-in fields explodes + lowers + emits, carrying every field\", () => {\n const shape: Record<string, unknown> = {};\n for (const { key, build } of DROP_INS) shape[`f_${key}`] = build(s);\n const entity = defineEntity(\"better_schemic_conformance_probe\", shape);\n\n // explode (authoring -> kinded definables) -> lowerSchema -> portable objects.\n const portable = lowerSchema(\n driver.registry,\n driver.explode([entity], []),\n );\n // Kind-agnostic: the probe lowers to at least one object (its kind is the driver's own —\n // `table`, `collection`, …); the per-field check below is what proves lowering is faithful.\n expect(portable.length).toBeGreaterThan(0);\n\n // The portable shape is the driver's own, but the emitted DDL is generic: every drop-in\n // field name must appear in it (lowering + emit carried it through).\n const ddl = emitKinds(driver.registry, portable).join(\"\\n\");\n expect(ddl.length).toBeGreaterThan(0);\n for (const { key } of DROP_INS) {\n expect(ddl).toContain(`f_${key}`);\n }\n });\n });\n });\n}\n\n// --- Coverage reconcile -------------------------------------------------------------------------\n//\n// The shared, driver-AGNOSTIC guard that keeps a driver's `docs/COVERAGE.md` (prose discipline) honest\n// against reality — closing the \"done-vs-todo list silently drifts\" gap. A driver declares its coverage\n// as data (a KIND manifest + a FEATURE manifest) and this reconciles it against the LIVE facts: the\n// neutral `registry.names()`/`.entries()` enumeration (so the registered-kind side CAN'T drift from the\n// code) and the actual test titles (so a feature can't be marked done without a real test). The\n// ENFORCEMENT lives here, in ONE place — a driver supplies only its manifest, so a fix propagates to\n// every driver instead of drifting across three copies.\n//\n// DELIBERATELY NOT checked: \"every kind defines `canonical()`\". `KindEngine.canonical` is OPTIONAL by\n// contract (it defaults to `emit(portable).join(\"\\n\")`), so a kind whose `emit` already IS its canonical\n// form correctly omits it — requiring it here would false-fail a conformant driver. A driver that wants\n// the stricter \"all MY kinds define an explicit canonical\" invariant can assert it in a local test.\n\n/** Coverage status, mirroring the `docs/COVERAGE.md` checkbox: full round-trip / partial / not done. */\nexport type CoverageStatus = \"x\" | \"~\" | \" \";\n\n/** A registered KIND and the round-trip status it claims. */\nexport interface KindCoverage {\n name: string;\n status: CoverageStatus;\n note?: string;\n}\n\n/** A finer-grained FEATURE within a kind, and (when done) the test that proves it. */\nexport interface FeatureCoverage {\n key: string;\n kind: string;\n status: CoverageStatus;\n /** A substring of the test title that exercises this feature — REQUIRED when status is `x`. */\n coveredBy?: string;\n note?: string;\n}\n\n/** The live inputs a reconcile runs against (the pure form — no `bun:test`, no filesystem). */\nexport interface CoverageReconcileInput {\n registry: KindRegistry;\n kinds: KindCoverage[];\n features: FeatureCoverage[];\n /** Concatenated source of the driver's `*.test.ts` — real test titles are extracted from it. */\n testSrc: string;\n}\n\n/** One named check and the assertions it failed (empty = passed). */\nexport interface CoverageCheck {\n name: string;\n failures: string[];\n}\n\n/** The reconcile outcome: per-check breakdown + a flattened failure list for a single-assert test. */\nexport interface CoverageReconcileResult {\n checks: CoverageCheck[];\n failures: string[];\n}\n\n/**\n * Extract the titles of the REAL, non-skipped `test(...)`/`it(...)` calls from concatenated test source.\n * Matching against actual titles (rather than a raw `source.includes`) means a mention in a comment or an\n * unrelated string literal can't count as coverage, and a `.skip`/`.todo` test can't satisfy a done claim.\n */\nfunction extractTestTitles(src: string): string[] {\n const re =\n /\\b(?:test|it)(\\.[\\w.]+)?\\s*\\(\\s*([\"'`])((?:\\\\.|(?!\\2)[\\s\\S])*?)\\2/g;\n const titles: string[] = [];\n for (const m of src.matchAll(re)) {\n const modifier = m[1] ?? \"\";\n if (/\\.(?:skip|todo)\\b/.test(modifier)) continue;\n titles.push(m[3]);\n }\n return titles;\n}\n\n/**\n * Reconcile a driver's declared coverage against the live registry + tests. PURE — returns a per-check\n * breakdown; {@link describeCoverageReconcile} is the `bun:test` shell around it. See the section header\n * for what is (and deliberately isn't) checked.\n */\nexport function reconcileCoverage(\n input: CoverageReconcileInput,\n): CoverageReconcileResult {\n const { registry, kinds, features, testSrc } = input;\n const registered = new Set(registry.names());\n const checks: CoverageCheck[] = [];\n\n // 1. Registered kinds EXACTLY equal the manifest, both directions — the neutral registry is the LHS,\n // so registering a kind without listing it (or vice versa) fails by construction.\n const declared = new Set(kinds.map((k) => k.name));\n const kindsFailures: string[] = [];\n for (const name of registered)\n if (!declared.has(name))\n kindsFailures.push(\n `kind \"${name}\" is registered but missing from the manifest`,\n );\n for (const name of declared)\n if (!registered.has(name))\n kindsFailures.push(\n `kind \"${name}\" is in the manifest but not registered`,\n );\n checks.push({\n name: \"registered kinds match the manifest\",\n failures: kindsFailures,\n });\n\n // 2. Every feature references a registered kind (referential integrity).\n checks.push({\n name: \"every feature maps to a registered kind\",\n failures: features\n .filter((f) => !registered.has(f.kind))\n .map((f) => `feature \"${f.key}\" -> unknown kind \"${f.kind}\"`),\n });\n\n // 3. Every DONE feature names a covering test that actually exists.\n const titles = extractTestTitles(testSrc);\n const coverFailures: string[] = [];\n for (const f of features) {\n if (f.status !== \"x\") continue;\n if (!f.coveredBy) {\n coverFailures.push(\n `feature \"${f.key}\" is [x] but declares no coveredBy test`,\n );\n continue;\n }\n if (!titles.some((t) => t.includes(f.coveredBy as string)))\n coverFailures.push(\n `feature \"${f.key}\" coveredBy \"${f.coveredBy}\" — no matching (non-skipped) test title`,\n );\n }\n checks.push({\n name: \"every [x] feature names a covering test\",\n failures: coverFailures,\n });\n\n // 4. No duplicate feature keys / kind entries (hygiene — a dup silently hides one side).\n checks.push({\n name: \"no duplicate feature keys\",\n failures: duplicates(\n features.map((f) => f.key),\n \"feature key\",\n ),\n });\n checks.push({\n name: \"no duplicate kind entries\",\n failures: duplicates(\n kinds.map((k) => k.name),\n \"kind entry\",\n ),\n });\n\n return { checks, failures: checks.flatMap((c) => c.failures) };\n}\n\n/** Names appearing more than once, as failure messages. */\nfunction duplicates(names: string[], label: string): string[] {\n const seen = new Set<string>();\n const dup: string[] = [];\n for (const n of names) {\n if (seen.has(n)) dup.push(`duplicate ${label} \"${n}\"`);\n seen.add(n);\n }\n return dup;\n}\n\n/** Options for the `bun:test` reconcile shell. Supply `testDir` (read for you) OR a pre-read `testSrc`. */\nexport interface CoverageReconcileOptions {\n /** Label for the describe block (typically the driver name). */\n name?: string;\n registry: KindRegistry;\n kinds: KindCoverage[];\n features: FeatureCoverage[];\n /** The dir holding this driver's `*.test.ts`; the helper concatenates them to prove test coverage. */\n testDir?: string;\n /** Pre-read test source, if you'd rather gather it yourself (alternative to `testDir`). */\n testSrc?: string;\n}\n\n/**\n * Register the coverage reconcile as a `bun:test` block — one named `test(...)` per check, so CI reads\n * granularly. A driver calls this from a single `*.test.ts` with its manifest + `testDir`:\n *\n * import { describeCoverageReconcile } from \"@better-schemic/core/testing\";\n * import { registry } from \"../src/kinds\";\n * import { KIND_MANIFEST, FEATURE_MANIFEST } from \"./coverage-manifest\";\n * describeCoverageReconcile({ name: \"sqlite\", registry, kinds: KIND_MANIFEST,\n * features: FEATURE_MANIFEST, testDir: import.meta.dir });\n */\nexport function describeCoverageReconcile(\n opts: CoverageReconcileOptions,\n): void {\n const { name, registry, kinds, features } = opts;\n const testSrc =\n opts.testSrc ?? (opts.testDir ? readTestSrc(opts.testDir) : \"\");\n describe(name ? `coverage reconcile: ${name}` : \"coverage reconcile\", () => {\n for (const check of reconcileCoverage({\n registry,\n kinds,\n features,\n testSrc,\n }).checks) {\n test(check.name, () => {\n expect(check.failures).toEqual([]);\n });\n }\n });\n}\n\n/** Concatenate every `*.test.ts` in `dir` (the source the covering-test check scans). */\nfunction readTestSrc(dir: string): string {\n return readdirSync(dir)\n .filter((f) => f.endsWith(\".test.ts\"))\n .map((f) => readFileSync(join(dir, f), \"utf8\"))\n .join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,102 @@
1
+ {
2
+ "name": "@better-schemic/core",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "The dialect-neutral engine for Better-schemic — Driver contract, portable schema IR, and the migration/diff/snapshot engine.",
5
+ "license": "MIT",
6
+ "author": "Vertio Solutions",
7
+ "homepage": "https://github.com/NONSTANDARDCODE/better-schemic",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/NONSTANDARDCODE/better-schemic.git",
11
+ "directory": "packages/core"
12
+ },
13
+ "type": "module",
14
+ "sideEffects": false,
15
+ "module": "lib/index.js",
16
+ "types": "lib/index.d.ts",
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "files": [
21
+ "lib",
22
+ "src",
23
+ "LICENSE"
24
+ ],
25
+ "exports": {
26
+ ".": {
27
+ "bun": "./src/index.ts",
28
+ "import": {
29
+ "types": "./lib/index.d.ts",
30
+ "default": "./lib/index.js"
31
+ }
32
+ },
33
+ "./config": {
34
+ "bun": "./src/config.ts",
35
+ "import": {
36
+ "types": "./lib/config.d.ts",
37
+ "default": "./lib/config.js"
38
+ }
39
+ },
40
+ "./driver": {
41
+ "bun": "./src/driver/sdk.ts",
42
+ "import": {
43
+ "types": "./lib/driver.d.ts",
44
+ "default": "./lib/driver.js"
45
+ }
46
+ },
47
+ "./authoring": {
48
+ "bun": "./src/authoring.ts",
49
+ "import": {
50
+ "types": "./lib/authoring.d.ts",
51
+ "default": "./lib/authoring.js"
52
+ }
53
+ },
54
+ "./testing": {
55
+ "bun": "./src/testing.ts",
56
+ "import": {
57
+ "types": "./lib/testing.d.ts",
58
+ "default": "./lib/testing.js"
59
+ }
60
+ },
61
+ "./query": {
62
+ "bun": "./src/query.ts",
63
+ "import": {
64
+ "types": "./lib/query.d.ts",
65
+ "default": "./lib/query.js"
66
+ }
67
+ },
68
+ "./package.json": "./package.json"
69
+ },
70
+ "scripts": {
71
+ "build": "tsup",
72
+ "prepack": "bun run build",
73
+ "test": "bun test",
74
+ "test:unit": "bun test test/unit",
75
+ "test:live": "bun test test/live",
76
+ "test:e2e": "bun test test/e2e",
77
+ "test:types": "bun run ../../scripts/type-perf.ts packages/core",
78
+ "typecheck": "tsc --noEmit",
79
+ "lint": "biome check .",
80
+ "lint:fix": "biome check --write .",
81
+ "format": "biome format --write ."
82
+ },
83
+ "dependencies": {
84
+ "commander": "^14.0.2",
85
+ "jiti": "^2.4.2",
86
+ "magicast": "^0.5.3"
87
+ },
88
+ "peerDependencies": {
89
+ "surrealdb": "^2.0.3",
90
+ "zod": "^4.3.5"
91
+ },
92
+ "devDependencies": {
93
+ "@ark/attest": "0.56.3",
94
+ "@biomejs/biome": "^2.3.11",
95
+ "@types/bun": "latest",
96
+ "surrealdb": "^2.0.3",
97
+ "tsup": "^8",
98
+ "tsx": "^4.23.0",
99
+ "typescript": "^5",
100
+ "zod": "^4.3.5"
101
+ }
102
+ }
@@ -0,0 +1,360 @@
1
+ // The NEUTRAL, dialect-agnostic AUTHORING BASE (docs/AUTHORING-SPLIT.md — "base builder in core").
2
+ // Each driver package builds its `s.*` on this: `class <D>Field extends SFieldBase<S, Flags, <D>Meta>`
3
+ // adds the dialect's native authoring (`$`-methods) and its `$<driver>(type, codec)` escape hatch for
4
+ // types not representable on the wire; the base provides the Zod codec, the Zod wrappers, the full
5
+ // `z.*` passthrough, and the `rebuild`/`blank` seam that carries native metadata through a chain.
6
+ //
7
+ // It references NOTHING dialect-specific — it's generic over the per-dialect native-metadata slot `N`.
8
+ // It is also Zod-CLEAN: app-side behaviour delegates to the inner Zod schema (`z.decode`/`z.encode`/
9
+ // the wrappers) via Zod's public API, with side-channel metadata kept on WeakMaps — never patching
10
+ // Zod internals.
11
+
12
+ import * as z from "zod";
13
+
14
+ // `SFieldBase` is INVARIANT in its native-metadata slot `N` (the protected `rebuild(native: N)` makes
15
+ // N contravariant while `native`/`blank` make it covariant). So a dialect field — `SField` with
16
+ // `N = SurrealMeta` — is NOT assignable to a fixed `N = unknown`, which would make `AnyField` reject
17
+ // real dialect fields (e.g. `.or(s.int())`). At THIS cross-dialect boundary `N` is honestly "any
18
+ // dialect's metadata": erase it to `any` (bivariant) so every driver's field is an `AnyField`. The
19
+ // concrete `N` is preserved everywhere it matters — each driver's own field type keeps `N = <D>Meta`.
20
+
21
+ /** Any field of ANY dialect — the base type the helpers + wrappers accept. */
22
+ // biome-ignore lint/suspicious/noExplicitAny: cross-dialect erasure of the invariant native slot N.
23
+ export type AnyField = SFieldBase<z.ZodType, string, any>;
24
+
25
+ /** The Zod schema a field (or a raw Zod schema) carries. */
26
+ export type SchemaOf<F> =
27
+ // biome-ignore lint/suspicious/noExplicitAny: match a field of any dialect (N is invariant).
28
+ F extends SFieldBase<infer S, string, any>
29
+ ? S
30
+ : F extends z.ZodType
31
+ ? F
32
+ : never;
33
+
34
+ /** The `Flags` channel a field carries (driver `$`-methods brand it; widens to `string` for `Shape`). */
35
+ export type FlagsOf<F> =
36
+ // biome-ignore lint/suspicious/noExplicitAny: match a field of any dialect (N is invariant).
37
+ F extends SFieldBase<z.ZodType, infer Fl, any> ? Fl : never;
38
+
39
+ /** The schema one wrapper down — what `unwrap()` returns. */
40
+ export type InnerOf<S extends z.ZodType> =
41
+ S extends z.ZodOptional<infer I extends z.ZodType>
42
+ ? I
43
+ : S extends z.ZodNullable<infer I extends z.ZodType>
44
+ ? I
45
+ : S extends z.ZodDefault<infer I extends z.ZodType>
46
+ ? I
47
+ : S extends z.ZodPrefault<infer I extends z.ZodType>
48
+ ? I
49
+ : S extends z.ZodCatch<infer I extends z.ZodType>
50
+ ? I
51
+ : S extends z.ZodReadonly<infer I extends z.ZodType>
52
+ ? I
53
+ : S extends z.ZodArray<infer I extends z.ZodType>
54
+ ? I
55
+ : S;
56
+
57
+ /**
58
+ * Maps an object schema (built via a driver's `s.object`) to its original field shape, so nested
59
+ * fields keep their authoring metadata through generation. Kept on the schema, not the field, so it
60
+ * composes through `array()`/`optional()`/nesting.
61
+ */
62
+ export const objectFieldsRegistry = new WeakMap<
63
+ z.ZodType,
64
+ Record<string, AnyField>
65
+ >();
66
+
67
+ /**
68
+ * The PORTABLE, dialect-agnostic field base. Holds the Zod schema, an opaque per-dialect `native`
69
+ * metadata slot, the field-level codecs, and the app-land Zod wrappers (which carry `native` forward
70
+ * via the `rebuild` hook so a chain keeps its concrete dialect type). Each dialect subclasses it to
71
+ * add native authoring (`$`-methods) and re-type the wrappers so a chain stays its own field type.
72
+ */
73
+ export abstract class SFieldBase<
74
+ S extends z.ZodType = z.ZodType,
75
+ Flags extends string = never,
76
+ N = unknown,
77
+ > {
78
+ constructor(
79
+ readonly schema: S,
80
+ readonly native: N,
81
+ ) {}
82
+
83
+ /**
84
+ * Standard Schema interface (https://standardschema.dev), forwarded from the wrapped Zod schema so a
85
+ * Better-schemic field IS a drop-in Standard Schema — it slots straight into any consumer (tRPC, TanStack
86
+ * Form/Router, …) without unwrapping. `validate` runs the DECODE direction (wire -> app), matching
87
+ * `decode`/`parse`. We wrap Zod by composition (not subclassing), so this getter is what carries the
88
+ * `~standard` contract across the wrapper; without it only `field.schema` would be compliant.
89
+ */
90
+ get ["~standard"](): S["~standard"] {
91
+ return this.schema["~standard"];
92
+ }
93
+
94
+ /** Rebuild a sibling field of the SAME dialect with a new schema/flags. Each dialect overrides it. */
95
+ protected abstract rebuild<S2 extends z.ZodType, F2 extends string>(
96
+ schema: S2,
97
+ native: N,
98
+ ): SFieldBase<S2, F2, N>;
99
+ /** A fresh, empty native-metadata bag (for wrappers like `or`/`and` that reset it). */
100
+ protected abstract blank(): N;
101
+
102
+ // --- Field-level codec (raw, on `this.schema`): `decode` reads (wire -> app), `encode` writes
103
+ // (app -> wire). Create-shaping is a table concept, so these are NOT create-shaped. ---
104
+ /** Decode a DB value to its app type (wire -> app). */
105
+ decode(value: unknown): z.output<S> {
106
+ return z.decode(this.schema, value as never);
107
+ }
108
+ /** Encode an app value to its DB wire type (app -> wire). */
109
+ encode(value: z.output<S>): z.input<S> {
110
+ return z.encode(this.schema, value);
111
+ }
112
+ decodeAsync(value: unknown): Promise<z.output<S>> {
113
+ return z.decodeAsync(this.schema, value as never);
114
+ }
115
+ encodeAsync(value: z.output<S>): Promise<z.input<S>> {
116
+ return z.encodeAsync(this.schema, value);
117
+ }
118
+ safeDecode(value: unknown) {
119
+ return z.safeDecode(this.schema, value as never);
120
+ }
121
+ safeEncode(value: z.output<S>) {
122
+ return z.safeEncode(this.schema, value);
123
+ }
124
+ safeDecodeAsync(value: unknown) {
125
+ return z.safeDecodeAsync(this.schema, value as never);
126
+ }
127
+ safeEncodeAsync(value: z.output<S>) {
128
+ return z.safeEncodeAsync(this.schema, value);
129
+ }
130
+ // Deprecated Zod-style aliases — `parse` runs the DECODE direction (wire -> app).
131
+ /** @deprecated `parse` decodes a value (wire -> app). Use {@link decode}. */
132
+ parse(value: unknown): z.output<S> {
133
+ return this.decode(value);
134
+ }
135
+ /** @deprecated Use {@link safeDecode}. */
136
+ safeParse(value: unknown) {
137
+ return this.safeDecode(value);
138
+ }
139
+ /** @deprecated Use {@link decodeAsync}. */
140
+ parseAsync(value: unknown): Promise<z.output<S>> {
141
+ return this.decodeAsync(value);
142
+ }
143
+ /** @deprecated Use {@link safeDecodeAsync}. */
144
+ safeParseAsync(value: unknown) {
145
+ return this.safeDecodeAsync(value);
146
+ }
147
+ /** Zod's `.spa` alias for {@link safeParseAsync} (drop-in). */
148
+ spa(value: unknown) {
149
+ return this.safeParseAsync(value);
150
+ }
151
+
152
+ // --- Zod reflection + interop (drop-in for `z.*`), delegated to the inner schema ---
153
+ /** Does this field accept `undefined`? (Zod reflection.) */
154
+ isOptional(): boolean {
155
+ return this.schema.isOptional();
156
+ }
157
+ /** Does this field accept `null`? (Zod reflection.) */
158
+ isNullable(): boolean {
159
+ return this.schema.isNullable();
160
+ }
161
+ /** Read back the description set via {@link describe} / {@link meta}. */
162
+ get description(): string | undefined {
163
+ return this.schema.description;
164
+ }
165
+ /** JSON Schema for this field's wire shape (delegates to `z.toJSONSchema`). */
166
+ toJSONSchema() {
167
+ return z.toJSONSchema(this.schema);
168
+ }
169
+ /** Register the wrapped schema in a Zod registry for metadata interop; returns the field. */
170
+ register(...args: Parameters<S["register"]>): this {
171
+ Reflect.apply(this.schema.register, this.schema, args);
172
+ return this;
173
+ }
174
+
175
+ // Zod wrappers — delegate to the inner schema, carry native metadata + flags forward.
176
+ optional(): SFieldBase<z.ZodOptional<S>, Flags, N> {
177
+ return this.rebuild(this.schema.optional(), this.native);
178
+ }
179
+ nullable(): SFieldBase<z.ZodNullable<S>, Flags, N> {
180
+ return this.rebuild(this.schema.nullable(), this.native);
181
+ }
182
+ default(value: z.input<S>): SFieldBase<z.ZodDefault<S>, Flags, N> {
183
+ return this.rebuild(this.schema.default(value as never), this.native);
184
+ }
185
+ /** Zod prefault: fill an absent value with `value`, then validate it (unlike `.default`). */
186
+ prefault(value: z.input<S>): SFieldBase<z.ZodPrefault<S>, Flags, N> {
187
+ return this.rebuild(z.prefault(this.schema, value as never), this.native);
188
+ }
189
+ /** Zod catch: fall back to `value` when parsing fails. */
190
+ catch(value: z.output<S>): SFieldBase<z.ZodCatch<S>, Flags, N> {
191
+ return this.rebuild(this.schema.catch(value as never), this.native);
192
+ }
193
+ array(): SFieldBase<z.ZodArray<S>, Flags, N> {
194
+ return this.rebuild(z.array(this.schema), this.native);
195
+ }
196
+ nullish(): SFieldBase<z.ZodOptional<z.ZodNullable<S>>, Flags, N> {
197
+ return this.rebuild(this.schema.nullish(), this.native);
198
+ }
199
+ /** Zod `.nonoptional()` — require a value (strips an `.optional()`). */
200
+ nonoptional(): SFieldBase<z.ZodNonOptional<S>, Flags, N> {
201
+ return this.rebuild(this.schema.nonoptional(), this.native);
202
+ }
203
+ /** Zod `.exactOptional()` — optional that rejects an explicit `undefined`. */
204
+ exactOptional(): SFieldBase<z.ZodExactOptional<S>, Flags, N> {
205
+ return this.rebuild(this.schema.exactOptional(), this.native);
206
+ }
207
+ /** Zod union — `a.or(b)` accepts either. Mirrors Zod's `.or()`. */
208
+ or<F extends AnyField | z.ZodType>(
209
+ other: F,
210
+ ): SFieldBase<z.ZodUnion<[S, SchemaOf<F>]>, never, N> {
211
+ return this.rebuild<z.ZodUnion<[S, SchemaOf<F>]>, never>(
212
+ z.union([this.schema, toZod(other)]) as z.ZodUnion<[S, SchemaOf<F>]>,
213
+ this.blank(),
214
+ );
215
+ }
216
+ /** Zod intersection — `a.and(b)`. Mirrors Zod's `.and()`. */
217
+ and<F extends AnyField | z.ZodType>(
218
+ other: F,
219
+ ): SFieldBase<z.ZodIntersection<S, SchemaOf<F>>, never, N> {
220
+ return this.rebuild<z.ZodIntersection<S, SchemaOf<F>>, never>(
221
+ z.intersection(this.schema, toZod(other) as SchemaOf<F>),
222
+ this.blank(),
223
+ );
224
+ }
225
+
226
+ // --- Native Zod passthrough (drop-in for `z.*`): app-side validation / transform / metadata,
227
+ // delegated to the inner schema. The dialect-DDL side stays under the driver's `$`-methods. ---
228
+ refine(
229
+ check: (arg: z.output<S>) => unknown,
230
+ params?: string | z.core.$ZodCustomParams,
231
+ ): this {
232
+ return this.rebuild(
233
+ this.schema.refine(check, params) as S,
234
+ this.native,
235
+ ) as unknown as this;
236
+ }
237
+ superRefine(
238
+ refinement: (
239
+ arg: z.output<S>,
240
+ ctx: z.core.$RefinementCtx<z.output<S>>,
241
+ ) => void,
242
+ ): this {
243
+ return this.rebuild(
244
+ this.schema.superRefine(refinement) as S,
245
+ this.native,
246
+ ) as unknown as this;
247
+ }
248
+ check(
249
+ ...checks: (z.core.CheckFn<z.output<S>> | z.core.$ZodCheck<z.output<S>>)[]
250
+ ): this {
251
+ return this.rebuild(
252
+ this.schema.check(...checks) as S,
253
+ this.native,
254
+ ) as unknown as this;
255
+ }
256
+ overwrite(fn: (x: z.output<S>) => z.output<S>): this {
257
+ return this.rebuild(
258
+ this.schema.overwrite(fn) as S,
259
+ this.native,
260
+ ) as unknown as this;
261
+ }
262
+ brand<B extends PropertyKey = PropertyKey>(value?: B): this {
263
+ return this.rebuild(
264
+ this.schema.brand(value) as unknown as S,
265
+ this.native,
266
+ ) as unknown as this;
267
+ }
268
+ /** Zod's app-side metadata (JSON-schema/docs) — distinct from a driver's `$comment()`. */
269
+ describe(description: string): this {
270
+ return this.rebuild(
271
+ this.schema.describe(description) as S,
272
+ this.native,
273
+ ) as unknown as this;
274
+ }
275
+ meta(data: z.core.GlobalMeta): this {
276
+ return this.rebuild(
277
+ this.schema.meta(data) as S,
278
+ this.native,
279
+ ) as unknown as this;
280
+ }
281
+ /** Zod's app-side readonly (TS-immutable output) — distinct from a driver's `$readonly()`. */
282
+ readonly(): SFieldBase<z.ZodReadonly<S>, Flags, N> {
283
+ return this.rebuild(this.schema.readonly(), this.native);
284
+ }
285
+ /** Zod transform — changes the decoded `App<>` value; the stored (wire) type is unchanged. */
286
+ transform<NewOut>(
287
+ fn: (arg: z.output<S>, ctx: z.core.$RefinementCtx<z.output<S>>) => NewOut,
288
+ ): SFieldBase<
289
+ z.ZodPipe<S, z.ZodTransform<Awaited<NewOut>, z.output<S>>>,
290
+ Flags,
291
+ N
292
+ > {
293
+ return this.rebuild(this.schema.transform(fn), this.native);
294
+ }
295
+ /** Zod pipe — feed this field's output into `target`; the stored (wire) type stays `this`. */
296
+ pipe<T extends z.core.$ZodType<unknown, z.output<S>>>(
297
+ target: T,
298
+ ): SFieldBase<z.ZodPipe<S, T>, Flags, N> {
299
+ return this.rebuild(
300
+ this.schema.pipe(target) as z.ZodPipe<S, T>,
301
+ this.native,
302
+ );
303
+ }
304
+ /** Peel one wrapper (optional/nullable/default/prefault/catch/readonly/array) off the field. */
305
+ unwrap(): SFieldBase<InnerOf<S>, Flags, N> {
306
+ const def = this.schema._zod.def as {
307
+ innerType?: z.ZodType;
308
+ element?: z.ZodType;
309
+ };
310
+ const inner = def.innerType ?? def.element ?? this.schema;
311
+ return this.rebuild(inner, this.native) as unknown as SFieldBase<
312
+ InnerOf<S>,
313
+ Flags,
314
+ N
315
+ >;
316
+ }
317
+
318
+ /** Object-only: allow arbitrary extra keys — `FLEXIBLE` in DDL. Mirrors Zod's `.loose()`. */
319
+ loose(): this {
320
+ return this.objectMode("loose");
321
+ }
322
+ /** Object-only: reject unknown keys — the default. Mirrors Zod's `.strict()`. */
323
+ strict(): this {
324
+ return this.objectMode("strict");
325
+ }
326
+ /** Alias for {@link loose} — a `FLEXIBLE` object accepting arbitrary keys. */
327
+ flexible(): this {
328
+ return this.loose();
329
+ }
330
+ private objectMode(mode: "loose" | "strict"): this {
331
+ const obj = this.schema as unknown as {
332
+ loose?: () => z.ZodType;
333
+ strict?: () => z.ZodType;
334
+ };
335
+ if (typeof obj.loose !== "function" || typeof obj.strict !== "function") {
336
+ return this; // not an object schema — no-op
337
+ }
338
+ const next = (mode === "loose"
339
+ ? obj.loose()
340
+ : obj.strict()) as unknown as S;
341
+ // Carry the nested-field registry forward so DDL/create-shaping still see the subfields.
342
+ const fields = objectFieldsRegistry.get(this.schema);
343
+ if (fields) objectFieldsRegistry.set(next, fields);
344
+ return this.rebuild(next, this.native) as unknown as this;
345
+ }
346
+ }
347
+
348
+ /** Unwrap a field to its Zod schema (raw Zod schemas pass through). */
349
+ export const toZod = (v: AnyField | z.ZodType): z.ZodType =>
350
+ v instanceof SFieldBase ? v.schema : v;
351
+
352
+ // Secret-ref authoring helpers (env/secret) live here on the SIDE-EFFECT-FREE authoring subpath, so a
353
+ // driver's authoring index can re-export them without dragging the engine. (Also on the main index.)
354
+ export {
355
+ env,
356
+ isSecretRef,
357
+ type SecretProvider,
358
+ type SecretRef,
359
+ secret,
360
+ } from "./secrets";