@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.
- package/LICENSE +21 -0
- package/README.md +40 -0
- package/lib/authoring.d.ts +114 -0
- package/lib/authoring.js +242 -0
- package/lib/authoring.js.map +1 -0
- package/lib/chunk-26D7WX7Q.js +31 -0
- package/lib/chunk-26D7WX7Q.js.map +1 -0
- package/lib/chunk-IUPOUD4L.js +330 -0
- package/lib/chunk-IUPOUD4L.js.map +1 -0
- package/lib/chunk-LC3VHUM2.js +56 -0
- package/lib/chunk-LC3VHUM2.js.map +1 -0
- package/lib/chunk-RSGP7GVO.js +252 -0
- package/lib/chunk-RSGP7GVO.js.map +1 -0
- package/lib/client-HZF4ZWGO.js +13 -0
- package/lib/client-HZF4ZWGO.js.map +1 -0
- package/lib/config-BYh7WA4P.d.ts +259 -0
- package/lib/config.d.ts +2 -0
- package/lib/config.js +27 -0
- package/lib/config.js.map +1 -0
- package/lib/driver-LVldBEhS.d.ts +818 -0
- package/lib/driver.d.ts +151 -0
- package/lib/driver.js +47 -0
- package/lib/driver.js.map +1 -0
- package/lib/index.d.ts +154 -0
- package/lib/index.js +758 -0
- package/lib/index.js.map +1 -0
- package/lib/query.d.ts +81 -0
- package/lib/query.js +30 -0
- package/lib/query.js.map +1 -0
- package/lib/secrets-BETi5p8g.d.ts +26 -0
- package/lib/testing.d.ts +99 -0
- package/lib/testing.js +212 -0
- package/lib/testing.js.map +1 -0
- package/package.json +102 -0
- package/src/authoring.ts +360 -0
- package/src/cli-kit/config.ts +226 -0
- package/src/cli-kit/diff.ts +273 -0
- package/src/cli-kit/filter.ts +159 -0
- package/src/cli-kit/merge.ts +380 -0
- package/src/cli-kit/meta.ts +123 -0
- package/src/cli-kit/pager.ts +42 -0
- package/src/cli-kit/schema.ts +214 -0
- package/src/cli-kit/style.ts +24 -0
- package/src/client.ts +244 -0
- package/src/config.ts +199 -0
- package/src/connection.ts +120 -0
- package/src/driver/driver.ts +413 -0
- package/src/driver/index.ts +31 -0
- package/src/driver/portable-ir.ts +51 -0
- package/src/driver/portable.ts +124 -0
- package/src/driver/sdk.ts +73 -0
- package/src/index.ts +185 -0
- package/src/kind/index.ts +28 -0
- package/src/kind/plan.ts +412 -0
- package/src/kind/registry.ts +270 -0
- package/src/query/call.ts +21 -0
- package/src/query/codec.ts +33 -0
- package/src/query/index.ts +22 -0
- package/src/query/project.ts +25 -0
- package/src/query/ref.ts +32 -0
- package/src/query.ts +5 -0
- package/src/secrets.ts +61 -0
- package/src/seed.ts +14 -0
- package/src/testing.ts +402 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vertio Solutions
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @better-schemic/core
|
|
2
|
+
|
|
3
|
+
The dialect-neutral engine behind [Better-schemic](https://github.com/NONSTANDARDCODE/better-schemic) — schema-as-code
|
|
4
|
+
for SurrealDB. It defines the **driver contract**, the **portable schema IR**, and the
|
|
5
|
+
**diff / migration / snapshot** engine that the CLI runs over the SurrealDB driver.
|
|
6
|
+
|
|
7
|
+
`@better-schemic/core` has **no authoring surface of its own** — you don't write schemas with it directly.
|
|
8
|
+
The SurrealDB driver provides that:
|
|
9
|
+
|
|
10
|
+
- [`@better-schemic/surrealdb`](../../drivers/surrealdb#readme) gives you the `s.*` authoring API and emits SurrealQL DDL.
|
|
11
|
+
- [`@better-schemic/cli`](../cli#readme) gives you the `better-schemic` / `sc` commands.
|
|
12
|
+
|
|
13
|
+
The CLI loads the driver from `better-schemic.config.ts` and orchestrates this engine generically — author →
|
|
14
|
+
diff → generate → migrate.
|
|
15
|
+
|
|
16
|
+
## When you touch it directly
|
|
17
|
+
|
|
18
|
+
Most projects depend on `@better-schemic/core` only transitively, through the CLI and a driver. The one
|
|
19
|
+
piece you import from it is the config helper:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { defineConfig } from "@better-schemic/core/config";
|
|
23
|
+
// pair it with a connection factory from the driver
|
|
24
|
+
// (e.g. surrealConnection from @better-schemic/surrealdb)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
With bun, npm, or yarn, `@better-schemic/core` is pulled in transitively (it's a dependency of the CLI and
|
|
28
|
+
every driver) — you don't install it directly. Under pnpm's strict `node_modules` the transitive copy
|
|
29
|
+
isn't reachable from your `better-schemic.config.ts`, so add it explicitly: `pnpm add @better-schemic/core`.
|
|
30
|
+
|
|
31
|
+
See your driver's README for the full `defineConfig({ connections: { … } })` setup.
|
|
32
|
+
|
|
33
|
+
## Docs
|
|
34
|
+
|
|
35
|
+
Guides, concepts, and reference live at [docs](https://github.com/NONSTANDARDCODE/better-schemic). This package is part of
|
|
36
|
+
the [Better-schemic](https://github.com/NONSTANDARDCODE/better-schemic) toolkit.
|
|
37
|
+
|
|
38
|
+
## License
|
|
39
|
+
|
|
40
|
+
[MIT](./LICENSE) © Vertio Solutions
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
export { S as SecretProvider, a as SecretRef, e as env, i as isSecretRef, s as secret } from './secrets-BETi5p8g.js';
|
|
3
|
+
|
|
4
|
+
/** Any field of ANY dialect — the base type the helpers + wrappers accept. */
|
|
5
|
+
type AnyField = SFieldBase<z.ZodType, string, any>;
|
|
6
|
+
/** The Zod schema a field (or a raw Zod schema) carries. */
|
|
7
|
+
type SchemaOf<F> = F extends SFieldBase<infer S, string, any> ? S : F extends z.ZodType ? F : never;
|
|
8
|
+
/** The `Flags` channel a field carries (driver `$`-methods brand it; widens to `string` for `Shape`). */
|
|
9
|
+
type FlagsOf<F> = F extends SFieldBase<z.ZodType, infer Fl, any> ? Fl : never;
|
|
10
|
+
/** The schema one wrapper down — what `unwrap()` returns. */
|
|
11
|
+
type InnerOf<S extends z.ZodType> = S extends z.ZodOptional<infer I extends z.ZodType> ? I : S extends z.ZodNullable<infer I extends z.ZodType> ? I : S extends z.ZodDefault<infer I extends z.ZodType> ? I : S extends z.ZodPrefault<infer I extends z.ZodType> ? I : S extends z.ZodCatch<infer I extends z.ZodType> ? I : S extends z.ZodReadonly<infer I extends z.ZodType> ? I : S extends z.ZodArray<infer I extends z.ZodType> ? I : S;
|
|
12
|
+
/**
|
|
13
|
+
* Maps an object schema (built via a driver's `s.object`) to its original field shape, so nested
|
|
14
|
+
* fields keep their authoring metadata through generation. Kept on the schema, not the field, so it
|
|
15
|
+
* composes through `array()`/`optional()`/nesting.
|
|
16
|
+
*/
|
|
17
|
+
declare const objectFieldsRegistry: WeakMap<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, Record<string, AnyField>>;
|
|
18
|
+
/**
|
|
19
|
+
* The PORTABLE, dialect-agnostic field base. Holds the Zod schema, an opaque per-dialect `native`
|
|
20
|
+
* metadata slot, the field-level codecs, and the app-land Zod wrappers (which carry `native` forward
|
|
21
|
+
* via the `rebuild` hook so a chain keeps its concrete dialect type). Each dialect subclasses it to
|
|
22
|
+
* add native authoring (`$`-methods) and re-type the wrappers so a chain stays its own field type.
|
|
23
|
+
*/
|
|
24
|
+
declare abstract class SFieldBase<S extends z.ZodType = z.ZodType, Flags extends string = never, N = unknown> {
|
|
25
|
+
readonly schema: S;
|
|
26
|
+
readonly native: N;
|
|
27
|
+
constructor(schema: S, native: N);
|
|
28
|
+
/**
|
|
29
|
+
* Standard Schema interface (https://standardschema.dev), forwarded from the wrapped Zod schema so a
|
|
30
|
+
* Better-schemic field IS a drop-in Standard Schema — it slots straight into any consumer (tRPC, TanStack
|
|
31
|
+
* Form/Router, …) without unwrapping. `validate` runs the DECODE direction (wire -> app), matching
|
|
32
|
+
* `decode`/`parse`. We wrap Zod by composition (not subclassing), so this getter is what carries the
|
|
33
|
+
* `~standard` contract across the wrapper; without it only `field.schema` would be compliant.
|
|
34
|
+
*/
|
|
35
|
+
get ["~standard"](): S["~standard"];
|
|
36
|
+
/** Rebuild a sibling field of the SAME dialect with a new schema/flags. Each dialect overrides it. */
|
|
37
|
+
protected abstract rebuild<S2 extends z.ZodType, F2 extends string>(schema: S2, native: N): SFieldBase<S2, F2, N>;
|
|
38
|
+
/** A fresh, empty native-metadata bag (for wrappers like `or`/`and` that reset it). */
|
|
39
|
+
protected abstract blank(): N;
|
|
40
|
+
/** Decode a DB value to its app type (wire -> app). */
|
|
41
|
+
decode(value: unknown): z.output<S>;
|
|
42
|
+
/** Encode an app value to its DB wire type (app -> wire). */
|
|
43
|
+
encode(value: z.output<S>): z.input<S>;
|
|
44
|
+
decodeAsync(value: unknown): Promise<z.output<S>>;
|
|
45
|
+
encodeAsync(value: z.output<S>): Promise<z.input<S>>;
|
|
46
|
+
safeDecode(value: unknown): z.ZodSafeParseResult<z.core.output<S>>;
|
|
47
|
+
safeEncode(value: z.output<S>): z.ZodSafeParseResult<z.core.input<S>>;
|
|
48
|
+
safeDecodeAsync(value: unknown): Promise<z.ZodSafeParseResult<z.core.output<S>>>;
|
|
49
|
+
safeEncodeAsync(value: z.output<S>): Promise<z.ZodSafeParseResult<z.core.input<S>>>;
|
|
50
|
+
/** @deprecated `parse` decodes a value (wire -> app). Use {@link decode}. */
|
|
51
|
+
parse(value: unknown): z.output<S>;
|
|
52
|
+
/** @deprecated Use {@link safeDecode}. */
|
|
53
|
+
safeParse(value: unknown): z.ZodSafeParseResult<z.core.output<S>>;
|
|
54
|
+
/** @deprecated Use {@link decodeAsync}. */
|
|
55
|
+
parseAsync(value: unknown): Promise<z.output<S>>;
|
|
56
|
+
/** @deprecated Use {@link safeDecodeAsync}. */
|
|
57
|
+
safeParseAsync(value: unknown): Promise<z.ZodSafeParseResult<z.core.output<S>>>;
|
|
58
|
+
/** Zod's `.spa` alias for {@link safeParseAsync} (drop-in). */
|
|
59
|
+
spa(value: unknown): Promise<z.ZodSafeParseResult<z.core.output<S>>>;
|
|
60
|
+
/** Does this field accept `undefined`? (Zod reflection.) */
|
|
61
|
+
isOptional(): boolean;
|
|
62
|
+
/** Does this field accept `null`? (Zod reflection.) */
|
|
63
|
+
isNullable(): boolean;
|
|
64
|
+
/** Read back the description set via {@link describe} / {@link meta}. */
|
|
65
|
+
get description(): string | undefined;
|
|
66
|
+
/** JSON Schema for this field's wire shape (delegates to `z.toJSONSchema`). */
|
|
67
|
+
toJSONSchema(): z.core.ZodStandardJSONSchemaPayload<S>;
|
|
68
|
+
/** Register the wrapped schema in a Zod registry for metadata interop; returns the field. */
|
|
69
|
+
register(...args: Parameters<S["register"]>): this;
|
|
70
|
+
optional(): SFieldBase<z.ZodOptional<S>, Flags, N>;
|
|
71
|
+
nullable(): SFieldBase<z.ZodNullable<S>, Flags, N>;
|
|
72
|
+
default(value: z.input<S>): SFieldBase<z.ZodDefault<S>, Flags, N>;
|
|
73
|
+
/** Zod prefault: fill an absent value with `value`, then validate it (unlike `.default`). */
|
|
74
|
+
prefault(value: z.input<S>): SFieldBase<z.ZodPrefault<S>, Flags, N>;
|
|
75
|
+
/** Zod catch: fall back to `value` when parsing fails. */
|
|
76
|
+
catch(value: z.output<S>): SFieldBase<z.ZodCatch<S>, Flags, N>;
|
|
77
|
+
array(): SFieldBase<z.ZodArray<S>, Flags, N>;
|
|
78
|
+
nullish(): SFieldBase<z.ZodOptional<z.ZodNullable<S>>, Flags, N>;
|
|
79
|
+
/** Zod `.nonoptional()` — require a value (strips an `.optional()`). */
|
|
80
|
+
nonoptional(): SFieldBase<z.ZodNonOptional<S>, Flags, N>;
|
|
81
|
+
/** Zod `.exactOptional()` — optional that rejects an explicit `undefined`. */
|
|
82
|
+
exactOptional(): SFieldBase<z.ZodExactOptional<S>, Flags, N>;
|
|
83
|
+
/** Zod union — `a.or(b)` accepts either. Mirrors Zod's `.or()`. */
|
|
84
|
+
or<F extends AnyField | z.ZodType>(other: F): SFieldBase<z.ZodUnion<[S, SchemaOf<F>]>, never, N>;
|
|
85
|
+
/** Zod intersection — `a.and(b)`. Mirrors Zod's `.and()`. */
|
|
86
|
+
and<F extends AnyField | z.ZodType>(other: F): SFieldBase<z.ZodIntersection<S, SchemaOf<F>>, never, N>;
|
|
87
|
+
refine(check: (arg: z.output<S>) => unknown, params?: string | z.core.$ZodCustomParams): this;
|
|
88
|
+
superRefine(refinement: (arg: z.output<S>, ctx: z.core.$RefinementCtx<z.output<S>>) => void): this;
|
|
89
|
+
check(...checks: (z.core.CheckFn<z.output<S>> | z.core.$ZodCheck<z.output<S>>)[]): this;
|
|
90
|
+
overwrite(fn: (x: z.output<S>) => z.output<S>): this;
|
|
91
|
+
brand<B extends PropertyKey = PropertyKey>(value?: B): this;
|
|
92
|
+
/** Zod's app-side metadata (JSON-schema/docs) — distinct from a driver's `$comment()`. */
|
|
93
|
+
describe(description: string): this;
|
|
94
|
+
meta(data: z.core.GlobalMeta): this;
|
|
95
|
+
/** Zod's app-side readonly (TS-immutable output) — distinct from a driver's `$readonly()`. */
|
|
96
|
+
readonly(): SFieldBase<z.ZodReadonly<S>, Flags, N>;
|
|
97
|
+
/** Zod transform — changes the decoded `App<>` value; the stored (wire) type is unchanged. */
|
|
98
|
+
transform<NewOut>(fn: (arg: z.output<S>, ctx: z.core.$RefinementCtx<z.output<S>>) => NewOut): SFieldBase<z.ZodPipe<S, z.ZodTransform<Awaited<NewOut>, z.output<S>>>, Flags, N>;
|
|
99
|
+
/** Zod pipe — feed this field's output into `target`; the stored (wire) type stays `this`. */
|
|
100
|
+
pipe<T extends z.core.$ZodType<unknown, z.output<S>>>(target: T): SFieldBase<z.ZodPipe<S, T>, Flags, N>;
|
|
101
|
+
/** Peel one wrapper (optional/nullable/default/prefault/catch/readonly/array) off the field. */
|
|
102
|
+
unwrap(): SFieldBase<InnerOf<S>, Flags, N>;
|
|
103
|
+
/** Object-only: allow arbitrary extra keys — `FLEXIBLE` in DDL. Mirrors Zod's `.loose()`. */
|
|
104
|
+
loose(): this;
|
|
105
|
+
/** Object-only: reject unknown keys — the default. Mirrors Zod's `.strict()`. */
|
|
106
|
+
strict(): this;
|
|
107
|
+
/** Alias for {@link loose} — a `FLEXIBLE` object accepting arbitrary keys. */
|
|
108
|
+
flexible(): this;
|
|
109
|
+
private objectMode;
|
|
110
|
+
}
|
|
111
|
+
/** Unwrap a field to its Zod schema (raw Zod schemas pass through). */
|
|
112
|
+
declare const toZod: (v: AnyField | z.ZodType) => z.ZodType;
|
|
113
|
+
|
|
114
|
+
export { type AnyField, type FlagsOf, type InnerOf, SFieldBase, type SchemaOf, objectFieldsRegistry, toZod };
|
package/lib/authoring.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import {
|
|
2
|
+
env,
|
|
3
|
+
isSecretRef,
|
|
4
|
+
secret
|
|
5
|
+
} from "./chunk-26D7WX7Q.js";
|
|
6
|
+
|
|
7
|
+
// src/authoring.ts
|
|
8
|
+
import * as z from "zod";
|
|
9
|
+
var objectFieldsRegistry = /* @__PURE__ */ new WeakMap();
|
|
10
|
+
var SFieldBase = class {
|
|
11
|
+
constructor(schema, native) {
|
|
12
|
+
this.schema = schema;
|
|
13
|
+
this.native = native;
|
|
14
|
+
}
|
|
15
|
+
schema;
|
|
16
|
+
native;
|
|
17
|
+
/**
|
|
18
|
+
* Standard Schema interface (https://standardschema.dev), forwarded from the wrapped Zod schema so a
|
|
19
|
+
* Better-schemic field IS a drop-in Standard Schema — it slots straight into any consumer (tRPC, TanStack
|
|
20
|
+
* Form/Router, …) without unwrapping. `validate` runs the DECODE direction (wire -> app), matching
|
|
21
|
+
* `decode`/`parse`. We wrap Zod by composition (not subclassing), so this getter is what carries the
|
|
22
|
+
* `~standard` contract across the wrapper; without it only `field.schema` would be compliant.
|
|
23
|
+
*/
|
|
24
|
+
get ["~standard"]() {
|
|
25
|
+
return this.schema["~standard"];
|
|
26
|
+
}
|
|
27
|
+
// --- Field-level codec (raw, on `this.schema`): `decode` reads (wire -> app), `encode` writes
|
|
28
|
+
// (app -> wire). Create-shaping is a table concept, so these are NOT create-shaped. ---
|
|
29
|
+
/** Decode a DB value to its app type (wire -> app). */
|
|
30
|
+
decode(value) {
|
|
31
|
+
return z.decode(this.schema, value);
|
|
32
|
+
}
|
|
33
|
+
/** Encode an app value to its DB wire type (app -> wire). */
|
|
34
|
+
encode(value) {
|
|
35
|
+
return z.encode(this.schema, value);
|
|
36
|
+
}
|
|
37
|
+
decodeAsync(value) {
|
|
38
|
+
return z.decodeAsync(this.schema, value);
|
|
39
|
+
}
|
|
40
|
+
encodeAsync(value) {
|
|
41
|
+
return z.encodeAsync(this.schema, value);
|
|
42
|
+
}
|
|
43
|
+
safeDecode(value) {
|
|
44
|
+
return z.safeDecode(this.schema, value);
|
|
45
|
+
}
|
|
46
|
+
safeEncode(value) {
|
|
47
|
+
return z.safeEncode(this.schema, value);
|
|
48
|
+
}
|
|
49
|
+
safeDecodeAsync(value) {
|
|
50
|
+
return z.safeDecodeAsync(this.schema, value);
|
|
51
|
+
}
|
|
52
|
+
safeEncodeAsync(value) {
|
|
53
|
+
return z.safeEncodeAsync(this.schema, value);
|
|
54
|
+
}
|
|
55
|
+
// Deprecated Zod-style aliases — `parse` runs the DECODE direction (wire -> app).
|
|
56
|
+
/** @deprecated `parse` decodes a value (wire -> app). Use {@link decode}. */
|
|
57
|
+
parse(value) {
|
|
58
|
+
return this.decode(value);
|
|
59
|
+
}
|
|
60
|
+
/** @deprecated Use {@link safeDecode}. */
|
|
61
|
+
safeParse(value) {
|
|
62
|
+
return this.safeDecode(value);
|
|
63
|
+
}
|
|
64
|
+
/** @deprecated Use {@link decodeAsync}. */
|
|
65
|
+
parseAsync(value) {
|
|
66
|
+
return this.decodeAsync(value);
|
|
67
|
+
}
|
|
68
|
+
/** @deprecated Use {@link safeDecodeAsync}. */
|
|
69
|
+
safeParseAsync(value) {
|
|
70
|
+
return this.safeDecodeAsync(value);
|
|
71
|
+
}
|
|
72
|
+
/** Zod's `.spa` alias for {@link safeParseAsync} (drop-in). */
|
|
73
|
+
spa(value) {
|
|
74
|
+
return this.safeParseAsync(value);
|
|
75
|
+
}
|
|
76
|
+
// --- Zod reflection + interop (drop-in for `z.*`), delegated to the inner schema ---
|
|
77
|
+
/** Does this field accept `undefined`? (Zod reflection.) */
|
|
78
|
+
isOptional() {
|
|
79
|
+
return this.schema.isOptional();
|
|
80
|
+
}
|
|
81
|
+
/** Does this field accept `null`? (Zod reflection.) */
|
|
82
|
+
isNullable() {
|
|
83
|
+
return this.schema.isNullable();
|
|
84
|
+
}
|
|
85
|
+
/** Read back the description set via {@link describe} / {@link meta}. */
|
|
86
|
+
get description() {
|
|
87
|
+
return this.schema.description;
|
|
88
|
+
}
|
|
89
|
+
/** JSON Schema for this field's wire shape (delegates to `z.toJSONSchema`). */
|
|
90
|
+
toJSONSchema() {
|
|
91
|
+
return z.toJSONSchema(this.schema);
|
|
92
|
+
}
|
|
93
|
+
/** Register the wrapped schema in a Zod registry for metadata interop; returns the field. */
|
|
94
|
+
register(...args) {
|
|
95
|
+
Reflect.apply(this.schema.register, this.schema, args);
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
// Zod wrappers — delegate to the inner schema, carry native metadata + flags forward.
|
|
99
|
+
optional() {
|
|
100
|
+
return this.rebuild(this.schema.optional(), this.native);
|
|
101
|
+
}
|
|
102
|
+
nullable() {
|
|
103
|
+
return this.rebuild(this.schema.nullable(), this.native);
|
|
104
|
+
}
|
|
105
|
+
default(value) {
|
|
106
|
+
return this.rebuild(this.schema.default(value), this.native);
|
|
107
|
+
}
|
|
108
|
+
/** Zod prefault: fill an absent value with `value`, then validate it (unlike `.default`). */
|
|
109
|
+
prefault(value) {
|
|
110
|
+
return this.rebuild(z.prefault(this.schema, value), this.native);
|
|
111
|
+
}
|
|
112
|
+
/** Zod catch: fall back to `value` when parsing fails. */
|
|
113
|
+
catch(value) {
|
|
114
|
+
return this.rebuild(this.schema.catch(value), this.native);
|
|
115
|
+
}
|
|
116
|
+
array() {
|
|
117
|
+
return this.rebuild(z.array(this.schema), this.native);
|
|
118
|
+
}
|
|
119
|
+
nullish() {
|
|
120
|
+
return this.rebuild(this.schema.nullish(), this.native);
|
|
121
|
+
}
|
|
122
|
+
/** Zod `.nonoptional()` — require a value (strips an `.optional()`). */
|
|
123
|
+
nonoptional() {
|
|
124
|
+
return this.rebuild(this.schema.nonoptional(), this.native);
|
|
125
|
+
}
|
|
126
|
+
/** Zod `.exactOptional()` — optional that rejects an explicit `undefined`. */
|
|
127
|
+
exactOptional() {
|
|
128
|
+
return this.rebuild(this.schema.exactOptional(), this.native);
|
|
129
|
+
}
|
|
130
|
+
/** Zod union — `a.or(b)` accepts either. Mirrors Zod's `.or()`. */
|
|
131
|
+
or(other) {
|
|
132
|
+
return this.rebuild(
|
|
133
|
+
z.union([this.schema, toZod(other)]),
|
|
134
|
+
this.blank()
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
/** Zod intersection — `a.and(b)`. Mirrors Zod's `.and()`. */
|
|
138
|
+
and(other) {
|
|
139
|
+
return this.rebuild(
|
|
140
|
+
z.intersection(this.schema, toZod(other)),
|
|
141
|
+
this.blank()
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
// --- Native Zod passthrough (drop-in for `z.*`): app-side validation / transform / metadata,
|
|
145
|
+
// delegated to the inner schema. The dialect-DDL side stays under the driver's `$`-methods. ---
|
|
146
|
+
refine(check, params) {
|
|
147
|
+
return this.rebuild(
|
|
148
|
+
this.schema.refine(check, params),
|
|
149
|
+
this.native
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
superRefine(refinement) {
|
|
153
|
+
return this.rebuild(
|
|
154
|
+
this.schema.superRefine(refinement),
|
|
155
|
+
this.native
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
check(...checks) {
|
|
159
|
+
return this.rebuild(
|
|
160
|
+
this.schema.check(...checks),
|
|
161
|
+
this.native
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
overwrite(fn) {
|
|
165
|
+
return this.rebuild(
|
|
166
|
+
this.schema.overwrite(fn),
|
|
167
|
+
this.native
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
brand(value) {
|
|
171
|
+
return this.rebuild(
|
|
172
|
+
this.schema.brand(value),
|
|
173
|
+
this.native
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
/** Zod's app-side metadata (JSON-schema/docs) — distinct from a driver's `$comment()`. */
|
|
177
|
+
describe(description) {
|
|
178
|
+
return this.rebuild(
|
|
179
|
+
this.schema.describe(description),
|
|
180
|
+
this.native
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
meta(data) {
|
|
184
|
+
return this.rebuild(
|
|
185
|
+
this.schema.meta(data),
|
|
186
|
+
this.native
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
/** Zod's app-side readonly (TS-immutable output) — distinct from a driver's `$readonly()`. */
|
|
190
|
+
readonly() {
|
|
191
|
+
return this.rebuild(this.schema.readonly(), this.native);
|
|
192
|
+
}
|
|
193
|
+
/** Zod transform — changes the decoded `App<>` value; the stored (wire) type is unchanged. */
|
|
194
|
+
transform(fn) {
|
|
195
|
+
return this.rebuild(this.schema.transform(fn), this.native);
|
|
196
|
+
}
|
|
197
|
+
/** Zod pipe — feed this field's output into `target`; the stored (wire) type stays `this`. */
|
|
198
|
+
pipe(target) {
|
|
199
|
+
return this.rebuild(
|
|
200
|
+
this.schema.pipe(target),
|
|
201
|
+
this.native
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
/** Peel one wrapper (optional/nullable/default/prefault/catch/readonly/array) off the field. */
|
|
205
|
+
unwrap() {
|
|
206
|
+
const def = this.schema._zod.def;
|
|
207
|
+
const inner = def.innerType ?? def.element ?? this.schema;
|
|
208
|
+
return this.rebuild(inner, this.native);
|
|
209
|
+
}
|
|
210
|
+
/** Object-only: allow arbitrary extra keys — `FLEXIBLE` in DDL. Mirrors Zod's `.loose()`. */
|
|
211
|
+
loose() {
|
|
212
|
+
return this.objectMode("loose");
|
|
213
|
+
}
|
|
214
|
+
/** Object-only: reject unknown keys — the default. Mirrors Zod's `.strict()`. */
|
|
215
|
+
strict() {
|
|
216
|
+
return this.objectMode("strict");
|
|
217
|
+
}
|
|
218
|
+
/** Alias for {@link loose} — a `FLEXIBLE` object accepting arbitrary keys. */
|
|
219
|
+
flexible() {
|
|
220
|
+
return this.loose();
|
|
221
|
+
}
|
|
222
|
+
objectMode(mode) {
|
|
223
|
+
const obj = this.schema;
|
|
224
|
+
if (typeof obj.loose !== "function" || typeof obj.strict !== "function") {
|
|
225
|
+
return this;
|
|
226
|
+
}
|
|
227
|
+
const next = mode === "loose" ? obj.loose() : obj.strict();
|
|
228
|
+
const fields = objectFieldsRegistry.get(this.schema);
|
|
229
|
+
if (fields) objectFieldsRegistry.set(next, fields);
|
|
230
|
+
return this.rebuild(next, this.native);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
var toZod = (v) => v instanceof SFieldBase ? v.schema : v;
|
|
234
|
+
export {
|
|
235
|
+
SFieldBase,
|
|
236
|
+
env,
|
|
237
|
+
isSecretRef,
|
|
238
|
+
objectFieldsRegistry,
|
|
239
|
+
secret,
|
|
240
|
+
toZod
|
|
241
|
+
};
|
|
242
|
+
//# sourceMappingURL=authoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/authoring.ts"],"sourcesContent":["// The NEUTRAL, dialect-agnostic AUTHORING BASE (docs/AUTHORING-SPLIT.md — \"base builder in core\").\n// Each driver package builds its `s.*` on this: `class <D>Field extends SFieldBase<S, Flags, <D>Meta>`\n// adds the dialect's native authoring (`$`-methods) and its `$<driver>(type, codec)` escape hatch for\n// types not representable on the wire; the base provides the Zod codec, the Zod wrappers, the full\n// `z.*` passthrough, and the `rebuild`/`blank` seam that carries native metadata through a chain.\n//\n// It references NOTHING dialect-specific — it's generic over the per-dialect native-metadata slot `N`.\n// It is also Zod-CLEAN: app-side behaviour delegates to the inner Zod schema (`z.decode`/`z.encode`/\n// the wrappers) via Zod's public API, with side-channel metadata kept on WeakMaps — never patching\n// Zod internals.\n\nimport * as z from \"zod\";\n\n// `SFieldBase` is INVARIANT in its native-metadata slot `N` (the protected `rebuild(native: N)` makes\n// N contravariant while `native`/`blank` make it covariant). So a dialect field — `SField` with\n// `N = SurrealMeta` — is NOT assignable to a fixed `N = unknown`, which would make `AnyField` reject\n// real dialect fields (e.g. `.or(s.int())`). At THIS cross-dialect boundary `N` is honestly \"any\n// dialect's metadata\": erase it to `any` (bivariant) so every driver's field is an `AnyField`. The\n// concrete `N` is preserved everywhere it matters — each driver's own field type keeps `N = <D>Meta`.\n\n/** Any field of ANY dialect — the base type the helpers + wrappers accept. */\n// biome-ignore lint/suspicious/noExplicitAny: cross-dialect erasure of the invariant native slot N.\nexport type AnyField = SFieldBase<z.ZodType, string, any>;\n\n/** The Zod schema a field (or a raw Zod schema) carries. */\nexport type SchemaOf<F> =\n // biome-ignore lint/suspicious/noExplicitAny: match a field of any dialect (N is invariant).\n F extends SFieldBase<infer S, string, any>\n ? S\n : F extends z.ZodType\n ? F\n : never;\n\n/** The `Flags` channel a field carries (driver `$`-methods brand it; widens to `string` for `Shape`). */\nexport type FlagsOf<F> =\n // biome-ignore lint/suspicious/noExplicitAny: match a field of any dialect (N is invariant).\n F extends SFieldBase<z.ZodType, infer Fl, any> ? Fl : never;\n\n/** The schema one wrapper down — what `unwrap()` returns. */\nexport type InnerOf<S extends z.ZodType> =\n S extends z.ZodOptional<infer I extends z.ZodType>\n ? I\n : S extends z.ZodNullable<infer I extends z.ZodType>\n ? I\n : S extends z.ZodDefault<infer I extends z.ZodType>\n ? I\n : S extends z.ZodPrefault<infer I extends z.ZodType>\n ? I\n : S extends z.ZodCatch<infer I extends z.ZodType>\n ? I\n : S extends z.ZodReadonly<infer I extends z.ZodType>\n ? I\n : S extends z.ZodArray<infer I extends z.ZodType>\n ? I\n : S;\n\n/**\n * Maps an object schema (built via a driver's `s.object`) to its original field shape, so nested\n * fields keep their authoring metadata through generation. Kept on the schema, not the field, so it\n * composes through `array()`/`optional()`/nesting.\n */\nexport const objectFieldsRegistry = new WeakMap<\n z.ZodType,\n Record<string, AnyField>\n>();\n\n/**\n * The PORTABLE, dialect-agnostic field base. Holds the Zod schema, an opaque per-dialect `native`\n * metadata slot, the field-level codecs, and the app-land Zod wrappers (which carry `native` forward\n * via the `rebuild` hook so a chain keeps its concrete dialect type). Each dialect subclasses it to\n * add native authoring (`$`-methods) and re-type the wrappers so a chain stays its own field type.\n */\nexport abstract class SFieldBase<\n S extends z.ZodType = z.ZodType,\n Flags extends string = never,\n N = unknown,\n> {\n constructor(\n readonly schema: S,\n readonly native: N,\n ) {}\n\n /**\n * Standard Schema interface (https://standardschema.dev), forwarded from the wrapped Zod schema so a\n * Better-schemic field IS a drop-in Standard Schema — it slots straight into any consumer (tRPC, TanStack\n * Form/Router, …) without unwrapping. `validate` runs the DECODE direction (wire -> app), matching\n * `decode`/`parse`. We wrap Zod by composition (not subclassing), so this getter is what carries the\n * `~standard` contract across the wrapper; without it only `field.schema` would be compliant.\n */\n get [\"~standard\"](): S[\"~standard\"] {\n return this.schema[\"~standard\"];\n }\n\n /** Rebuild a sibling field of the SAME dialect with a new schema/flags. Each dialect overrides it. */\n protected abstract rebuild<S2 extends z.ZodType, F2 extends string>(\n schema: S2,\n native: N,\n ): SFieldBase<S2, F2, N>;\n /** A fresh, empty native-metadata bag (for wrappers like `or`/`and` that reset it). */\n protected abstract blank(): N;\n\n // --- Field-level codec (raw, on `this.schema`): `decode` reads (wire -> app), `encode` writes\n // (app -> wire). Create-shaping is a table concept, so these are NOT create-shaped. ---\n /** Decode a DB value to its app type (wire -> app). */\n decode(value: unknown): z.output<S> {\n return z.decode(this.schema, value as never);\n }\n /** Encode an app value to its DB wire type (app -> wire). */\n encode(value: z.output<S>): z.input<S> {\n return z.encode(this.schema, value);\n }\n decodeAsync(value: unknown): Promise<z.output<S>> {\n return z.decodeAsync(this.schema, value as never);\n }\n encodeAsync(value: z.output<S>): Promise<z.input<S>> {\n return z.encodeAsync(this.schema, value);\n }\n safeDecode(value: unknown) {\n return z.safeDecode(this.schema, value as never);\n }\n safeEncode(value: z.output<S>) {\n return z.safeEncode(this.schema, value);\n }\n safeDecodeAsync(value: unknown) {\n return z.safeDecodeAsync(this.schema, value as never);\n }\n safeEncodeAsync(value: z.output<S>) {\n return z.safeEncodeAsync(this.schema, value);\n }\n // Deprecated Zod-style aliases — `parse` runs the DECODE direction (wire -> app).\n /** @deprecated `parse` decodes a value (wire -> app). Use {@link decode}. */\n parse(value: unknown): z.output<S> {\n return this.decode(value);\n }\n /** @deprecated Use {@link safeDecode}. */\n safeParse(value: unknown) {\n return this.safeDecode(value);\n }\n /** @deprecated Use {@link decodeAsync}. */\n parseAsync(value: unknown): Promise<z.output<S>> {\n return this.decodeAsync(value);\n }\n /** @deprecated Use {@link safeDecodeAsync}. */\n safeParseAsync(value: unknown) {\n return this.safeDecodeAsync(value);\n }\n /** Zod's `.spa` alias for {@link safeParseAsync} (drop-in). */\n spa(value: unknown) {\n return this.safeParseAsync(value);\n }\n\n // --- Zod reflection + interop (drop-in for `z.*`), delegated to the inner schema ---\n /** Does this field accept `undefined`? (Zod reflection.) */\n isOptional(): boolean {\n return this.schema.isOptional();\n }\n /** Does this field accept `null`? (Zod reflection.) */\n isNullable(): boolean {\n return this.schema.isNullable();\n }\n /** Read back the description set via {@link describe} / {@link meta}. */\n get description(): string | undefined {\n return this.schema.description;\n }\n /** JSON Schema for this field's wire shape (delegates to `z.toJSONSchema`). */\n toJSONSchema() {\n return z.toJSONSchema(this.schema);\n }\n /** Register the wrapped schema in a Zod registry for metadata interop; returns the field. */\n register(...args: Parameters<S[\"register\"]>): this {\n Reflect.apply(this.schema.register, this.schema, args);\n return this;\n }\n\n // Zod wrappers — delegate to the inner schema, carry native metadata + flags forward.\n optional(): SFieldBase<z.ZodOptional<S>, Flags, N> {\n return this.rebuild(this.schema.optional(), this.native);\n }\n nullable(): SFieldBase<z.ZodNullable<S>, Flags, N> {\n return this.rebuild(this.schema.nullable(), this.native);\n }\n default(value: z.input<S>): SFieldBase<z.ZodDefault<S>, Flags, N> {\n return this.rebuild(this.schema.default(value as never), this.native);\n }\n /** Zod prefault: fill an absent value with `value`, then validate it (unlike `.default`). */\n prefault(value: z.input<S>): SFieldBase<z.ZodPrefault<S>, Flags, N> {\n return this.rebuild(z.prefault(this.schema, value as never), this.native);\n }\n /** Zod catch: fall back to `value` when parsing fails. */\n catch(value: z.output<S>): SFieldBase<z.ZodCatch<S>, Flags, N> {\n return this.rebuild(this.schema.catch(value as never), this.native);\n }\n array(): SFieldBase<z.ZodArray<S>, Flags, N> {\n return this.rebuild(z.array(this.schema), this.native);\n }\n nullish(): SFieldBase<z.ZodOptional<z.ZodNullable<S>>, Flags, N> {\n return this.rebuild(this.schema.nullish(), this.native);\n }\n /** Zod `.nonoptional()` — require a value (strips an `.optional()`). */\n nonoptional(): SFieldBase<z.ZodNonOptional<S>, Flags, N> {\n return this.rebuild(this.schema.nonoptional(), this.native);\n }\n /** Zod `.exactOptional()` — optional that rejects an explicit `undefined`. */\n exactOptional(): SFieldBase<z.ZodExactOptional<S>, Flags, N> {\n return this.rebuild(this.schema.exactOptional(), this.native);\n }\n /** Zod union — `a.or(b)` accepts either. Mirrors Zod's `.or()`. */\n or<F extends AnyField | z.ZodType>(\n other: F,\n ): SFieldBase<z.ZodUnion<[S, SchemaOf<F>]>, never, N> {\n return this.rebuild<z.ZodUnion<[S, SchemaOf<F>]>, never>(\n z.union([this.schema, toZod(other)]) as z.ZodUnion<[S, SchemaOf<F>]>,\n this.blank(),\n );\n }\n /** Zod intersection — `a.and(b)`. Mirrors Zod's `.and()`. */\n and<F extends AnyField | z.ZodType>(\n other: F,\n ): SFieldBase<z.ZodIntersection<S, SchemaOf<F>>, never, N> {\n return this.rebuild<z.ZodIntersection<S, SchemaOf<F>>, never>(\n z.intersection(this.schema, toZod(other) as SchemaOf<F>),\n this.blank(),\n );\n }\n\n // --- Native Zod passthrough (drop-in for `z.*`): app-side validation / transform / metadata,\n // delegated to the inner schema. The dialect-DDL side stays under the driver's `$`-methods. ---\n refine(\n check: (arg: z.output<S>) => unknown,\n params?: string | z.core.$ZodCustomParams,\n ): this {\n return this.rebuild(\n this.schema.refine(check, params) as S,\n this.native,\n ) as unknown as this;\n }\n superRefine(\n refinement: (\n arg: z.output<S>,\n ctx: z.core.$RefinementCtx<z.output<S>>,\n ) => void,\n ): this {\n return this.rebuild(\n this.schema.superRefine(refinement) as S,\n this.native,\n ) as unknown as this;\n }\n check(\n ...checks: (z.core.CheckFn<z.output<S>> | z.core.$ZodCheck<z.output<S>>)[]\n ): this {\n return this.rebuild(\n this.schema.check(...checks) as S,\n this.native,\n ) as unknown as this;\n }\n overwrite(fn: (x: z.output<S>) => z.output<S>): this {\n return this.rebuild(\n this.schema.overwrite(fn) as S,\n this.native,\n ) as unknown as this;\n }\n brand<B extends PropertyKey = PropertyKey>(value?: B): this {\n return this.rebuild(\n this.schema.brand(value) as unknown as S,\n this.native,\n ) as unknown as this;\n }\n /** Zod's app-side metadata (JSON-schema/docs) — distinct from a driver's `$comment()`. */\n describe(description: string): this {\n return this.rebuild(\n this.schema.describe(description) as S,\n this.native,\n ) as unknown as this;\n }\n meta(data: z.core.GlobalMeta): this {\n return this.rebuild(\n this.schema.meta(data) as S,\n this.native,\n ) as unknown as this;\n }\n /** Zod's app-side readonly (TS-immutable output) — distinct from a driver's `$readonly()`. */\n readonly(): SFieldBase<z.ZodReadonly<S>, Flags, N> {\n return this.rebuild(this.schema.readonly(), this.native);\n }\n /** Zod transform — changes the decoded `App<>` value; the stored (wire) type is unchanged. */\n transform<NewOut>(\n fn: (arg: z.output<S>, ctx: z.core.$RefinementCtx<z.output<S>>) => NewOut,\n ): SFieldBase<\n z.ZodPipe<S, z.ZodTransform<Awaited<NewOut>, z.output<S>>>,\n Flags,\n N\n > {\n return this.rebuild(this.schema.transform(fn), this.native);\n }\n /** Zod pipe — feed this field's output into `target`; the stored (wire) type stays `this`. */\n pipe<T extends z.core.$ZodType<unknown, z.output<S>>>(\n target: T,\n ): SFieldBase<z.ZodPipe<S, T>, Flags, N> {\n return this.rebuild(\n this.schema.pipe(target) as z.ZodPipe<S, T>,\n this.native,\n );\n }\n /** Peel one wrapper (optional/nullable/default/prefault/catch/readonly/array) off the field. */\n unwrap(): SFieldBase<InnerOf<S>, Flags, N> {\n const def = this.schema._zod.def as {\n innerType?: z.ZodType;\n element?: z.ZodType;\n };\n const inner = def.innerType ?? def.element ?? this.schema;\n return this.rebuild(inner, this.native) as unknown as SFieldBase<\n InnerOf<S>,\n Flags,\n N\n >;\n }\n\n /** Object-only: allow arbitrary extra keys — `FLEXIBLE` in DDL. Mirrors Zod's `.loose()`. */\n loose(): this {\n return this.objectMode(\"loose\");\n }\n /** Object-only: reject unknown keys — the default. Mirrors Zod's `.strict()`. */\n strict(): this {\n return this.objectMode(\"strict\");\n }\n /** Alias for {@link loose} — a `FLEXIBLE` object accepting arbitrary keys. */\n flexible(): this {\n return this.loose();\n }\n private objectMode(mode: \"loose\" | \"strict\"): this {\n const obj = this.schema as unknown as {\n loose?: () => z.ZodType;\n strict?: () => z.ZodType;\n };\n if (typeof obj.loose !== \"function\" || typeof obj.strict !== \"function\") {\n return this; // not an object schema — no-op\n }\n const next = (mode === \"loose\"\n ? obj.loose()\n : obj.strict()) as unknown as S;\n // Carry the nested-field registry forward so DDL/create-shaping still see the subfields.\n const fields = objectFieldsRegistry.get(this.schema);\n if (fields) objectFieldsRegistry.set(next, fields);\n return this.rebuild(next, this.native) as unknown as this;\n }\n}\n\n/** Unwrap a field to its Zod schema (raw Zod schemas pass through). */\nexport const toZod = (v: AnyField | z.ZodType): z.ZodType =>\n v instanceof SFieldBase ? v.schema : v;\n\n// Secret-ref authoring helpers (env/secret) live here on the SIDE-EFFECT-FREE authoring subpath, so a\n// driver's authoring index can re-export them without dragging the engine. (Also on the main index.)\nexport {\n env,\n isSecretRef,\n type SecretProvider,\n type SecretRef,\n secret,\n} from \"./secrets\";\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/secrets.ts
|
|
2
|
+
function env(name) {
|
|
3
|
+
return { kind: "env", name };
|
|
4
|
+
}
|
|
5
|
+
function secret(name) {
|
|
6
|
+
return { kind: "secret", name };
|
|
7
|
+
}
|
|
8
|
+
function isSecretRef(v) {
|
|
9
|
+
if (typeof v !== "object" || v === null) return false;
|
|
10
|
+
const r = v;
|
|
11
|
+
return (r.kind === "env" || r.kind === "secret") && typeof r.name === "string";
|
|
12
|
+
}
|
|
13
|
+
var envSecretProvider = {
|
|
14
|
+
resolve(ref) {
|
|
15
|
+
const value = process.env[ref.name];
|
|
16
|
+
if (value === void 0) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`better-schemic: secret ${ref.kind}(${JSON.stringify(ref.name)}) is not set in the environment`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
env,
|
|
27
|
+
secret,
|
|
28
|
+
isSecretRef,
|
|
29
|
+
envSecretProvider
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=chunk-26D7WX7Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/secrets.ts"],"sourcesContent":["// Secret references for secret-bearing DDL (e.g. SurrealDB `DEFINE ACCESS` keys). A `SecretRef` is an\n// authoring-time PLACEHOLDER — it never carries the secret value. The value is resolved at APPLY time by\n// the CLI through a `SecretProvider` and handed to the database as a BOUND PARAMETER (never spliced into\n// the DDL string), so secrets stay out of the schema source, the snapshot, and the migration files.\n//\n// key: env(\"JWT_SECRET\") // resolved from process.env at apply\n// key: secret(\"jwt/signing-key\") // resolved from the configured SecretProvider at apply\n//\n// The clause carrying a `SecretRef` is marked `writeOnly` in the IR (diff-excluded + snapshot-omitted),\n// so a redacted secret never reads as drift; because the diff can't see the value, rotation is the\n// explicit `apply --rotate-keys` (it can't be auto-detected).\n\n/** An author-time reference to a secret, resolved to its value at apply time — never the value itself. */\nexport interface SecretRef {\n /** `env` → resolved from `process.env`; `secret` → resolved from the configured {@link SecretProvider}. */\n readonly kind: \"env\" | \"secret\";\n /** The environment-variable / secret name to resolve at apply. */\n readonly name: string;\n}\n\n/** Bind a value to an environment variable, resolved at apply from `process.env[name]`. */\nexport function env(name: string): SecretRef {\n return { kind: \"env\", name };\n}\n\n/** Bind a value to a named secret, resolved at apply from the configured {@link SecretProvider}. */\nexport function secret(name: string): SecretRef {\n return { kind: \"secret\", name };\n}\n\n/** Runtime guard: is `v` a {@link SecretRef} (vs a raw `string` literal key)? */\nexport function isSecretRef(v: unknown): v is SecretRef {\n if (typeof v !== \"object\" || v === null) return false;\n const r = v as Partial<SecretRef>;\n return (\n (r.kind === \"env\" || r.kind === \"secret\") && typeof r.name === \"string\"\n );\n}\n\n/**\n * Resolves {@link SecretRef}s to their values at apply time. Pluggable: the default\n * {@link envSecretProvider} reads every ref from `process.env`; swap it for a vault / file source by\n * passing a custom provider to the apply layer. The resolved value is handed to the DB as a BOUND\n * PARAMETER — never string-spliced into the DDL.\n */\nexport interface SecretProvider {\n resolve(ref: SecretRef): string | Promise<string>;\n}\n\n/** Default provider: resolves every {@link SecretRef} from `process.env[ref.name]`; throws if unset. */\nexport const envSecretProvider: SecretProvider = {\n resolve(ref: SecretRef): string {\n const value = process.env[ref.name];\n if (value === undefined) {\n throw new Error(\n `better-schemic: secret ${ref.kind}(${JSON.stringify(ref.name)}) is not set in the environment`,\n );\n }\n return value;\n },\n};\n"],"mappings":";AAqBO,SAAS,IAAI,MAAyB;AAC3C,SAAO,EAAE,MAAM,OAAO,KAAK;AAC7B;AAGO,SAAS,OAAO,MAAyB;AAC9C,SAAO,EAAE,MAAM,UAAU,KAAK;AAChC;AAGO,SAAS,YAAY,GAA4B;AACtD,MAAI,OAAO,MAAM,YAAY,MAAM,KAAM,QAAO;AAChD,QAAM,IAAI;AACV,UACG,EAAE,SAAS,SAAS,EAAE,SAAS,aAAa,OAAO,EAAE,SAAS;AAEnE;AAaO,IAAM,oBAAoC;AAAA,EAC/C,QAAQ,KAAwB;AAC9B,UAAM,QAAQ,QAAQ,IAAI,IAAI,IAAI;AAClC,QAAI,UAAU,QAAW;AACvB,YAAM,IAAI;AAAA,QACR,0BAA0B,IAAI,IAAI,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC;AAAA,MAChE;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
|