@schemic/core 0.1.0-alpha.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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +212 -0
  3. package/lib/authoring.d.ts +89 -0
  4. package/lib/authoring.js +187 -0
  5. package/lib/authoring.js.map +1 -0
  6. package/lib/chunk-C4D6JWSE.js +54 -0
  7. package/lib/chunk-C4D6JWSE.js.map +1 -0
  8. package/lib/chunk-T23RNU7G.js +304 -0
  9. package/lib/chunk-T23RNU7G.js.map +1 -0
  10. package/lib/config-TIiKDd9t.d.ts +97 -0
  11. package/lib/config.d.ts +1 -0
  12. package/lib/config.js +8 -0
  13. package/lib/config.js.map +1 -0
  14. package/lib/driver-Dh5hLKHm.d.ts +736 -0
  15. package/lib/driver.d.ts +150 -0
  16. package/lib/driver.js +47 -0
  17. package/lib/driver.js.map +1 -0
  18. package/lib/index.d.ts +84 -0
  19. package/lib/index.js +794 -0
  20. package/lib/index.js.map +1 -0
  21. package/lib/testing.d.ts +29 -0
  22. package/lib/testing.js +111 -0
  23. package/lib/testing.js.map +1 -0
  24. package/package.json +93 -0
  25. package/src/authoring.ts +304 -0
  26. package/src/cli-kit/config.ts +179 -0
  27. package/src/cli-kit/diff.ts +230 -0
  28. package/src/cli-kit/filter.ts +159 -0
  29. package/src/cli-kit/merge.ts +380 -0
  30. package/src/cli-kit/meta.ts +123 -0
  31. package/src/cli-kit/pager.ts +42 -0
  32. package/src/cli-kit/schema.ts +186 -0
  33. package/src/cli-kit/style.ts +24 -0
  34. package/src/config.ts +51 -0
  35. package/src/connection.ts +78 -0
  36. package/src/driver/driver.ts +300 -0
  37. package/src/driver/index.ts +31 -0
  38. package/src/driver/portable-ir.ts +51 -0
  39. package/src/driver/portable.ts +124 -0
  40. package/src/driver/sdk.ts +66 -0
  41. package/src/index.ts +145 -0
  42. package/src/kind/index.ts +28 -0
  43. package/src/kind/plan.ts +390 -0
  44. package/src/kind/registry.ts +225 -0
  45. package/src/testing.ts +181 -0
package/src/testing.ts ADDED
@@ -0,0 +1,181 @@
1
+ // A shared DRIVER CONFORMANCE suite — the runtime contract a `@schemic/<driver>` must satisfy, asserted
2
+ // with `bun:test`. Each driver runs it against its own authoring surface:
3
+ //
4
+ // import { describeDriverConformance } from "@schemic/core/testing";
5
+ // import { defineTable, s, surrealDriver } from "@schemic/surrealdb";
6
+ // describeDriverConformance({ name: "surrealdb", s, driver: surrealDriver, defineEntity: defineTable });
7
+ //
8
+ // WHY a test, not a type: the zod drop-in builders (`s.string()` = `new <D>Field(z.string())`) are
9
+ // mechanically identical across drivers, but TypeScript has NO higher-kinded types, so a generic core
10
+ // factory can't preserve each driver's field type (it collapses to the base, dropping `$`-methods).
11
+ // Each driver therefore hand-authors its drop-ins, and "`s` is a Zod SUPERSET" is enforceable only at
12
+ // runtime. This suite is that enforcement.
13
+ //
14
+ // It DUCK-TYPES fields (a field is "something with a `.schema` that is a Zod type") rather than using
15
+ // `instanceof SFieldBase` — a driver may extend its own copy of the base, so identity checks are unsafe.
16
+
17
+ import { describe, expect, test } from "bun:test";
18
+ import type * as z from "zod";
19
+ import { type Driver, driverNames, getDriver } from "./driver/driver";
20
+ import { emitKinds, lowerSchema } from "./kind";
21
+
22
+ /** The driver's authoring namespace (`s`) — a bag of field builders. Loosely typed (cross-driver). */
23
+ // biome-ignore lint/suspicious/noExplicitAny: a driver's `s` is dialect-specific; the suite is generic.
24
+ type Authoring = Record<string, (...args: any[]) => unknown>;
25
+
26
+ export interface DriverConformanceOptions {
27
+ /** The driver's registry name (e.g. `"surrealdb"`, `"postgres"`). */
28
+ name: string;
29
+ /** The driver's authoring namespace — the `s` each package exports. */
30
+ s: Authoring;
31
+ /** The driver under test (already registered by importing its package). */
32
+ driver: Driver<unknown>;
33
+ /**
34
+ * Authors the driver's primary fielded definable — a table, collection, node-type, … — from a name
35
+ * and a field shape. Used to lower a probe object through the pipeline. Drivers pass their own
36
+ * `define*` for this (e.g. `defineEntity: defineTable`); the suite stays shape-agnostic.
37
+ */
38
+ // biome-ignore lint/suspicious/noExplicitAny: dialect-specific definable/shape types.
39
+ defineEntity: (name: string, shape: Record<string, any>) => any;
40
+ }
41
+
42
+ /**
43
+ * The canonical zod DROP-IN set every driver's `s` MUST expose — the structural Zod builders that make
44
+ * a `@schemic/<driver>` a drop-in for `z`. Each maps to the DB's natural representation (a driver may
45
+ * also offer richer native aliases, e.g. `text`/`varchar` alongside `string`). `object`/`array` nest a
46
+ * `literal` (present everywhere) so a missing `string` doesn't cascade into their tests.
47
+ */
48
+ const DROP_INS: { key: string; build: (s: Authoring) => unknown }[] = [
49
+ { key: "string", build: (s) => s.string() },
50
+ { key: "number", build: (s) => s.number() },
51
+ { key: "boolean", build: (s) => s.boolean() },
52
+ { key: "date", build: (s) => s.date() },
53
+ { key: "literal", build: (s) => s.literal("a") },
54
+ { key: "enum", build: (s) => s.enum(["a", "b"]) },
55
+ { key: "object", build: (s) => s.object({ inner: s.literal("a") }) },
56
+ { key: "array", build: (s) => s.array(s.literal("a")) },
57
+ ];
58
+
59
+ /** Value pairs that prove a scalar drop-in really carries the right Zod schema (unambiguous scalars only). */
60
+ const SCALAR_CHECKS: { key: string; valid: unknown; invalid: unknown }[] = [
61
+ { key: "string", valid: "hello", invalid: 123 },
62
+ { key: "number", valid: 123, invalid: "hello" },
63
+ { key: "boolean", valid: true, invalid: "hello" },
64
+ ];
65
+
66
+ /** Duck-typed: a field exposes a Zod `.schema`; a raw Zod type IS the schema. Throws if neither. */
67
+ function toSchema(v: unknown): z.ZodType {
68
+ const field = v as { schema?: unknown } | null;
69
+ if (field && isZod(field.schema)) return field.schema as z.ZodType;
70
+ if (isZod(v)) return v as z.ZodType;
71
+ throw new Error("expected a field (with a `.schema` Zod type) or a Zod type");
72
+ }
73
+
74
+ function isZod(v: unknown): boolean {
75
+ return !!v && typeof (v as { safeParse?: unknown }).safeParse === "function";
76
+ }
77
+
78
+ /** Is `v` a driver field (has a `.schema` that is a Zod type)? */
79
+ function isField(v: unknown): boolean {
80
+ return isZod((v as { schema?: unknown } | null)?.schema);
81
+ }
82
+
83
+ /**
84
+ * Assert a `@schemic/<driver>` conforms to the Schemic driver contract: the Driver is registered with
85
+ * the IR pipeline + execution ops, and its `s` is a Zod-drop-in SUPERSET (the canonical drop-in set is
86
+ * present, carries the right schemas, composes through wrappers, and lowers to the portable IR).
87
+ */
88
+ export function describeDriverConformance(
89
+ opts: DriverConformanceOptions,
90
+ ): void {
91
+ const { name, s, driver, defineEntity } = opts;
92
+
93
+ describe(`driver conformance: ${name}`, () => {
94
+ describe("Driver contract", () => {
95
+ test("is registered under its name", () => {
96
+ expect(driverNames()).toContain(name);
97
+ expect(getDriver(name)).toBe(driver);
98
+ expect(driver.name).toBe(name);
99
+ });
100
+
101
+ test("exposes a kind registry + the schema/execution ops", () => {
102
+ // Schema ops are generic over `registry`; the driver provides the fan-out + execution.
103
+ expect(driver.registry).toBeDefined();
104
+ expect(typeof driver.registry.entries).toBe("function");
105
+ expect(driver.registry.names().length).toBeGreaterThan(0);
106
+ for (const op of [
107
+ "explode",
108
+ "introspectAll",
109
+ "connect",
110
+ "apply",
111
+ "close",
112
+ ] as const) {
113
+ expect(typeof driver[op]).toBe("function");
114
+ }
115
+ });
116
+ });
117
+
118
+ describe("zod drop-in surface (s.* is a Zod superset)", () => {
119
+ for (const { key, build } of DROP_INS) {
120
+ test(`s.${key}() exists and returns a field`, () => {
121
+ expect(typeof s[key]).toBe("function");
122
+ const field = build(s);
123
+ expect(isField(field)).toBe(true);
124
+ });
125
+ }
126
+
127
+ for (const { key, valid, invalid } of SCALAR_CHECKS) {
128
+ test(`s.${key}() carries a "${key}" Zod schema`, () => {
129
+ const schema = toSchema(s[key]());
130
+ expect(schema.safeParse(valid).success).toBe(true);
131
+ expect(schema.safeParse(invalid).success).toBe(false);
132
+ });
133
+ }
134
+ });
135
+
136
+ describe("Zod-clean codecs + wrappers", () => {
137
+ test("decode/encode delegate to the inner Zod schema", () => {
138
+ const field = s.string() as {
139
+ decode: (v: unknown) => unknown;
140
+ encode: (v: unknown) => unknown;
141
+ };
142
+ expect(field.decode("hi")).toBe("hi");
143
+ expect(field.encode("hi")).toBe("hi");
144
+ });
145
+
146
+ test("wrappers preserve field-ness (optional/array compose)", () => {
147
+ const field = s.string() as {
148
+ optional: () => unknown;
149
+ array: () => unknown;
150
+ };
151
+ expect(isField(field.optional())).toBe(true);
152
+ expect(isField(field.array())).toBe(true);
153
+ });
154
+ });
155
+
156
+ describe("lowering (drop-in fields → kind registry)", () => {
157
+ test("an entity of drop-in fields explodes + lowers + emits, carrying every field", () => {
158
+ const shape: Record<string, unknown> = {};
159
+ for (const { key, build } of DROP_INS) shape[`f_${key}`] = build(s);
160
+ const entity = defineEntity("schemic_conformance_probe", shape);
161
+
162
+ // explode (authoring -> kinded definables) -> lowerSchema -> portable objects.
163
+ const portable = lowerSchema(
164
+ driver.registry,
165
+ driver.explode([entity], []),
166
+ );
167
+ // Kind-agnostic: the probe lowers to at least one object (its kind is the driver's own —
168
+ // `table`, `collection`, …); the per-field check below is what proves lowering is faithful.
169
+ expect(portable.length).toBeGreaterThan(0);
170
+
171
+ // The portable shape is the driver's own, but the emitted DDL is generic: every drop-in
172
+ // field name must appear in it (lowering + emit carried it through).
173
+ const ddl = emitKinds(driver.registry, portable).join("\n");
174
+ expect(ddl.length).toBeGreaterThan(0);
175
+ for (const { key } of DROP_INS) {
176
+ expect(ddl).toContain(`f_${key}`);
177
+ }
178
+ });
179
+ });
180
+ });
181
+ }