@fougere/schema 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 (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -0
  3. package/dist/entity.d.ts +117 -0
  4. package/dist/entity.d.ts.map +1 -0
  5. package/dist/entity.js +285 -0
  6. package/dist/entity.js.map +1 -0
  7. package/dist/field/boundary.d.ts +75 -0
  8. package/dist/field/boundary.d.ts.map +1 -0
  9. package/dist/field/boundary.js +76 -0
  10. package/dist/field/boundary.js.map +1 -0
  11. package/dist/field/field.d.ts +64 -0
  12. package/dist/field/field.d.ts.map +1 -0
  13. package/dist/field/field.js +28 -0
  14. package/dist/field/field.js.map +1 -0
  15. package/dist/field/index.d.ts +7 -0
  16. package/dist/field/index.d.ts.map +1 -0
  17. package/dist/field/index.js +9 -0
  18. package/dist/field/index.js.map +1 -0
  19. package/dist/field/lifecycle.d.ts +40 -0
  20. package/dist/field/lifecycle.d.ts.map +1 -0
  21. package/dist/field/lifecycle.js +27 -0
  22. package/dist/field/lifecycle.js.map +1 -0
  23. package/dist/field/meta.d.ts +9 -0
  24. package/dist/field/meta.d.ts.map +1 -0
  25. package/dist/field/meta.js +7 -0
  26. package/dist/field/meta.js.map +1 -0
  27. package/dist/field/role.d.ts +54 -0
  28. package/dist/field/role.d.ts.map +1 -0
  29. package/dist/field/role.js +55 -0
  30. package/dist/field/role.js.map +1 -0
  31. package/dist/field/shape.d.ts +77 -0
  32. package/dist/field/shape.d.ts.map +1 -0
  33. package/dist/field/shape.js +49 -0
  34. package/dist/field/shape.js.map +1 -0
  35. package/dist/hints.d.ts +33 -0
  36. package/dist/hints.d.ts.map +1 -0
  37. package/dist/hints.js +2 -0
  38. package/dist/hints.js.map +1 -0
  39. package/dist/index.d.ts +36 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +46 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/projections/card.d.ts +117 -0
  44. package/dist/projections/card.d.ts.map +1 -0
  45. package/dist/projections/card.js +8 -0
  46. package/dist/projections/card.js.map +1 -0
  47. package/dist/projections/describe.d.ts +25 -0
  48. package/dist/projections/describe.d.ts.map +1 -0
  49. package/dist/projections/describe.js +129 -0
  50. package/dist/projections/describe.js.map +1 -0
  51. package/dist/projections/encode.d.ts +11 -0
  52. package/dist/projections/encode.d.ts.map +1 -0
  53. package/dist/projections/encode.js +26 -0
  54. package/dist/projections/encode.js.map +1 -0
  55. package/dist/projections/io.d.ts +30 -0
  56. package/dist/projections/io.d.ts.map +1 -0
  57. package/dist/projections/io.js +52 -0
  58. package/dist/projections/io.js.map +1 -0
  59. package/dist/projections/lifecycle.d.ts +43 -0
  60. package/dist/projections/lifecycle.d.ts.map +1 -0
  61. package/dist/projections/lifecycle.js +115 -0
  62. package/dist/projections/lifecycle.js.map +1 -0
  63. package/dist/projections/reconstruct.d.ts +36 -0
  64. package/dist/projections/reconstruct.d.ts.map +1 -0
  65. package/dist/projections/reconstruct.js +149 -0
  66. package/dist/projections/reconstruct.js.map +1 -0
  67. package/dist/projections/source.d.ts +41 -0
  68. package/dist/projections/source.d.ts.map +1 -0
  69. package/dist/projections/source.js +37 -0
  70. package/dist/projections/source.js.map +1 -0
  71. package/dist/projections/standard.d.ts +41 -0
  72. package/dist/projections/standard.d.ts.map +1 -0
  73. package/dist/projections/standard.js +10 -0
  74. package/dist/projections/standard.js.map +1 -0
  75. package/dist/projections/typescript.d.ts +53 -0
  76. package/dist/projections/typescript.d.ts.map +1 -0
  77. package/dist/projections/typescript.js +166 -0
  78. package/dist/projections/typescript.js.map +1 -0
  79. package/dist/projections/validation.d.ts +49 -0
  80. package/dist/projections/validation.d.ts.map +1 -0
  81. package/dist/projections/validation.js +140 -0
  82. package/dist/projections/validation.js.map +1 -0
  83. package/dist/unique.d.ts +65 -0
  84. package/dist/unique.d.ts.map +1 -0
  85. package/dist/unique.js +80 -0
  86. package/dist/unique.js.map +1 -0
  87. package/dist/vocabulary/auto.d.ts +8 -0
  88. package/dist/vocabulary/auto.d.ts.map +1 -0
  89. package/dist/vocabulary/auto.js +13 -0
  90. package/dist/vocabulary/auto.js.map +1 -0
  91. package/dist/vocabulary/bool.d.ts +6 -0
  92. package/dist/vocabulary/bool.d.ts.map +1 -0
  93. package/dist/vocabulary/bool.js +9 -0
  94. package/dist/vocabulary/bool.js.map +1 -0
  95. package/dist/vocabulary/date.d.ts +3 -0
  96. package/dist/vocabulary/date.d.ts.map +1 -0
  97. package/dist/vocabulary/date.js +5 -0
  98. package/dist/vocabulary/date.js.map +1 -0
  99. package/dist/vocabulary/email.d.ts +5 -0
  100. package/dist/vocabulary/email.d.ts.map +1 -0
  101. package/dist/vocabulary/email.js +6 -0
  102. package/dist/vocabulary/email.js.map +1 -0
  103. package/dist/vocabulary/immutable.d.ts +9 -0
  104. package/dist/vocabulary/immutable.d.ts.map +1 -0
  105. package/dist/vocabulary/immutable.js +11 -0
  106. package/dist/vocabulary/immutable.js.map +1 -0
  107. package/dist/vocabulary/indexed.d.ts +19 -0
  108. package/dist/vocabulary/indexed.d.ts.map +1 -0
  109. package/dist/vocabulary/indexed.js +21 -0
  110. package/dist/vocabulary/indexed.js.map +1 -0
  111. package/dist/vocabulary/json.d.ts +18 -0
  112. package/dist/vocabulary/json.d.ts.map +1 -0
  113. package/dist/vocabulary/json.js +16 -0
  114. package/dist/vocabulary/json.js.map +1 -0
  115. package/dist/vocabulary/list.d.ts +21 -0
  116. package/dist/vocabulary/list.d.ts.map +1 -0
  117. package/dist/vocabulary/list.js +21 -0
  118. package/dist/vocabulary/list.js.map +1 -0
  119. package/dist/vocabulary/many.d.ts +8 -0
  120. package/dist/vocabulary/many.d.ts.map +1 -0
  121. package/dist/vocabulary/many.js +12 -0
  122. package/dist/vocabulary/many.js.map +1 -0
  123. package/dist/vocabulary/nullable.d.ts +9 -0
  124. package/dist/vocabulary/nullable.d.ts.map +1 -0
  125. package/dist/vocabulary/nullable.js +13 -0
  126. package/dist/vocabulary/nullable.js.map +1 -0
  127. package/dist/vocabulary/number.d.ts +10 -0
  128. package/dist/vocabulary/number.d.ts.map +1 -0
  129. package/dist/vocabulary/number.js +9 -0
  130. package/dist/vocabulary/number.js.map +1 -0
  131. package/dist/vocabulary/oneOf.d.ts +16 -0
  132. package/dist/vocabulary/oneOf.d.ts.map +1 -0
  133. package/dist/vocabulary/oneOf.js +13 -0
  134. package/dist/vocabulary/oneOf.js.map +1 -0
  135. package/dist/vocabulary/optional.d.ts +10 -0
  136. package/dist/vocabulary/optional.d.ts.map +1 -0
  137. package/dist/vocabulary/optional.js +15 -0
  138. package/dist/vocabulary/optional.js.map +1 -0
  139. package/dist/vocabulary/primary.d.ts +22 -0
  140. package/dist/vocabulary/primary.d.ts.map +1 -0
  141. package/dist/vocabulary/primary.js +31 -0
  142. package/dist/vocabulary/primary.js.map +1 -0
  143. package/dist/vocabulary/readOnly.d.ts +10 -0
  144. package/dist/vocabulary/readOnly.d.ts.map +1 -0
  145. package/dist/vocabulary/readOnly.js +12 -0
  146. package/dist/vocabulary/readOnly.js.map +1 -0
  147. package/dist/vocabulary/ref.d.ts +12 -0
  148. package/dist/vocabulary/ref.d.ts.map +1 -0
  149. package/dist/vocabulary/ref.js +20 -0
  150. package/dist/vocabulary/ref.js.map +1 -0
  151. package/dist/vocabulary/text.d.ts +12 -0
  152. package/dist/vocabulary/text.d.ts.map +1 -0
  153. package/dist/vocabulary/text.js +9 -0
  154. package/dist/vocabulary/text.js.map +1 -0
  155. package/dist/vocabulary/unique.d.ts +23 -0
  156. package/dist/vocabulary/unique.d.ts.map +1 -0
  157. package/dist/vocabulary/unique.js +25 -0
  158. package/dist/vocabulary/unique.js.map +1 -0
  159. package/dist/vocabulary/updated.d.ts +8 -0
  160. package/dist/vocabulary/updated.d.ts.map +1 -0
  161. package/dist/vocabulary/updated.js +12 -0
  162. package/dist/vocabulary/updated.js.map +1 -0
  163. package/dist/vocabulary/url.d.ts +5 -0
  164. package/dist/vocabulary/url.d.ts.map +1 -0
  165. package/dist/vocabulary/url.js +6 -0
  166. package/dist/vocabulary/url.js.map +1 -0
  167. package/dist/vocabulary/writeOnly.d.ts +9 -0
  168. package/dist/vocabulary/writeOnly.d.ts.map +1 -0
  169. package/dist/vocabulary/writeOnly.js +11 -0
  170. package/dist/vocabulary/writeOnly.js.map +1 -0
  171. package/package.json +40 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fougere contributors
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,32 @@
1
+ # @fougere/schema
2
+ > The entity and its 4 axes
3
+ An entity declares fields; every field carries four independent axes — `shape` (which
4
+ IS JSON Schema), `role` (primary, ref…), `lifecycle` (who writes the value, and when)
5
+ and `boundary` (readOnly / writeOnly). Everything else in Fougere is a projection of
6
+ them: tables, GraphQL types, form contracts, validation.
7
+
8
+ This package names no adapter and depends on no engine.
9
+
10
+ ```ts
11
+ import { entity, primary, text, auto, readOnly } from '@fougere/schema';
12
+
13
+ export default class Post extends entity({
14
+ id: primary(),
15
+ title: text(),
16
+ authorId: readOnly(text()),
17
+ createdAt: auto(),
18
+ }) {}
19
+
20
+ Post.validate({ title: 'Bonjour' }); // → { success, data } | { success: false, errors }
21
+ ```
22
+
23
+ ## Installation
24
+ ```bash
25
+ pnpm add @fougere/schema
26
+ ```
27
+
28
+ ---
29
+
30
+ Part of [Fougere](https://github.com/chok/fougere) — one schema, a gradient from
31
+ monolith to distributed, the same user code.
32
+ Reference documentation: [the site](https://chok.github.io/fougere/) (en/fr).
@@ -0,0 +1,117 @@
1
+ import { type Field, type Fields } from "./field/index.js";
2
+ import type { Hints } from "./hints.js";
3
+ import { type CompositeUnique, type EntityDeclarations } from "./unique.js";
4
+ import { type ValidationResult, type ValidateOptions } from "./projections/validation.js";
5
+ import type { StandardSchemaV1 } from "./projections/standard.js";
6
+ /** The data shape an entity carries — every field present. */
7
+ export type SchemaViewInfer<TFields extends Fields> = {
8
+ [K in keyof TFields]: TFields[K] extends Field<infer T, any> ? T : never;
9
+ };
10
+ /** Keys whose value is auto-supplied at creation (primary/auto/default/optional). */
11
+ type AutoKeys<TFields extends Fields> = {
12
+ [K in keyof TFields]: TFields[K] extends Field<any, true> ? K : never;
13
+ }[keyof TFields];
14
+ /**
15
+ * Constructor input — like the data shape, but auto-at-creation fields are
16
+ * optional. `new Post({ title })` works without passing the generated id.
17
+ */
18
+ export type CtorInput<TFields extends Fields> = Omit<SchemaViewInfer<TFields>, AutoKeys<TFields>> & Partial<Pick<SchemaViewInfer<TFields>, AutoKeys<TFields>>>;
19
+ /**
20
+ * Fields as seen by a patch input: every field becomes OMISSIBLE (presence axis,
21
+ * `A = true` → optional in the input), but its nullity does NOT change — `null`
22
+ * stays governed by the base field (`optional()`). A patch may leave a field
23
+ * untouched; it may not erase a non-nullable field.
24
+ */
25
+ type PatchFields<TFields extends Fields> = {
26
+ [K in keyof TFields]: TFields[K] extends Field<infer T, any> ? Field<T, true> : never;
27
+ };
28
+ /**
29
+ * A schema constructor returned by Entity.pick(), omit(), partial(), extend().
30
+ *
31
+ * - Usable as a base class: `class X extends Post.pick('id') {}`
32
+ * - Instance type = the data shape (no Infer needed)
33
+ * - Has static methods: getFields(), validate(), pick(), omit(), partial(), extend()
34
+ */
35
+ export interface SchemaConstructor<TFields extends Fields> {
36
+ new (data: CtorInput<TFields>): SchemaViewInfer<TFields>;
37
+ readonly name: string;
38
+ readonly '~standard': StandardSchemaV1.Props<Record<string, unknown>, SchemaViewInfer<TFields>>;
39
+ /** The original Entity class this derivation was created from (undefined for compose() results). */
40
+ readonly source?: abstract new (...args: never[]) => unknown;
41
+ getFields(): TFields;
42
+ /**
43
+ * Per-consumer hints passed as the 2nd arg of `entity()`. Derivations carry them:
44
+ * pick/omit filter to surviving fields, rename remaps keys, partial/extend pass through.
45
+ */
46
+ getHints(): Hints<TFields> | undefined;
47
+ /**
48
+ * Field groups that are unique together, from the 2nd arg of `entity()`.
49
+ * Derivations carry them; a group loses a member and the group is dropped.
50
+ */
51
+ getUnique(): CompositeUnique<TFields> | undefined;
52
+ /** Validation options of this view (e.g. `patch` set by `partial()`). Derivations carry them. */
53
+ getOpts(): ValidateOptions;
54
+ validate(input: unknown): ValidationResult<SchemaViewInfer<TFields>>;
55
+ from(data: Record<string, unknown>): SchemaViewInfer<TFields>;
56
+ pick<K extends string & keyof TFields>(...keys: K[]): SchemaConstructor<Pick<TFields, K>>;
57
+ omit<K extends string & keyof TFields>(...keys: K[]): SchemaConstructor<Omit<TFields, K>>;
58
+ partial(): SchemaConstructor<PatchFields<TFields>>;
59
+ extend<E extends Fields>(extra: E): SchemaConstructor<TFields & E>;
60
+ /** Give an anonymous derivation an explicit runtime name. */
61
+ named(name: string): SchemaConstructor<TFields>;
62
+ rename(mapping: Partial<Record<string & keyof TFields, string>>): SchemaConstructor<Fields>;
63
+ }
64
+ /** Create a schema constructor from a field record. */
65
+ /**
66
+ * The runtime name a derivation carries when no class declaration named it.
67
+ *
68
+ * `class Post extends entity({…}) {}` is named by its declaration; `User.pick('id')` has
69
+ * only the factory's own class to be named after. A reader — the scanner, notably — needs
70
+ * to tell one from the other, so the name is stamped from here rather than read off a
71
+ * class whose identifier someone could rename without noticing the other end.
72
+ */
73
+ export declare const ANONYMOUS_SCHEMA_NAME = "Schema";
74
+ export declare function createSchemaConstructor<TFields extends Fields>(fields: TFields, source?: abstract new (...args: never[]) => unknown, hints?: Hints<TFields>, opts?: ValidateOptions, unique?: CompositeUnique<Fields>): SchemaConstructor<TFields>;
75
+ /**
76
+ * Define an entity — the factory that produces a schema-carrying class.
77
+ *
78
+ * ```ts
79
+ * class Post extends entity({ id: primary(), title: text({ min: 1 }) }) {}
80
+ *
81
+ * new Post({ id, title }) // real instance, data-typed (NOT a bag of Fields)
82
+ * Post.getFields() // metadata, no instantiation
83
+ * Post.pick('title') // derived view, same static API
84
+ * function publish(p: Post) // `Post` IS the data type — no Infer needed
85
+ * ```
86
+ *
87
+ * The class carries data + schema metadata only. No business behaviour lives on
88
+ * an entity — that belongs to handlers/commands (keeps form and behaviour apart).
89
+ *
90
+ * An optional 2nd argument carries per-consumer hints (see {@link Hints}) for the
91
+ * irreducible bits a neutral field can't express — only adapters present in the
92
+ * compilation are accepted; the field declarations themselves stay adapter-blind.
93
+ */
94
+ export declare function entity<TFields extends Fields>(fields: TFields, declarations?: EntityDeclarations<TFields>): SchemaConstructor<TFields>;
95
+ /** Anything that exposes its fields — an `entity()` class or a SchemaConstructor. */
96
+ interface HasFields {
97
+ getFields(): Fields;
98
+ }
99
+ type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
100
+ type FieldsFrom<T> = T extends {
101
+ getFields(): infer F;
102
+ } ? F : Fields;
103
+ /**
104
+ * Compose multiple entities/schemas into a single SchemaConstructor.
105
+ *
106
+ * ```ts
107
+ * class User extends compose(UserBase, Timestamps) {}
108
+ * class SearchPosts extends compose(Post.pick('title'), Pagination) {}
109
+ * ```
110
+ *
111
+ * One merge law for EVERYTHING a schema carries — fields, hints, opts: left to
112
+ * right, later sources override earlier ones on conflict (hints merge per adapter,
113
+ * per field key). Use .rename() before compose() to avoid field conflicts.
114
+ */
115
+ export declare function compose<T extends HasFields[]>(...sources: T): SchemaConstructor<UnionToIntersection<FieldsFrom<T[number]>> & Fields>;
116
+ export {};
117
+ //# sourceMappingURL=entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"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"}
package/dist/entity.js ADDED
@@ -0,0 +1,285 @@
1
+ import { resolveBoundary } from "./field/index.js";
2
+ import { deriveUnique, deriveUniqueRoles, projectUniqueOntoFields } from "./unique.js";
3
+ import { checkValue, validateFields } from "./projections/validation.js";
4
+ // ─── Factory ────────────────────────────────────
5
+ /**
6
+ * The single "trust me" point. The schema class is built from a runtime field-map:
7
+ * its instances get their shape from the constructor *data* (`Object.assign`), not
8
+ * from members written in the class body, and its derivations rebuild maps on the
9
+ * fly. TypeScript sees an empty class, so it cannot prove this matches the precise
10
+ * generic type — one assertion declares that it does. Every schema/mixin library
11
+ * (Effect's `Schema.Class`, ts-mixer, …) carries this same one line. Keeping it
12
+ * here, named and explained, keeps it the *only* such assertion in the package.
13
+ */
14
+ function asSchemaConstructor(impl) {
15
+ return impl;
16
+ }
17
+ /**
18
+ * Carry hints across a field-key transform — the schema-level twin of `cloneField`'s
19
+ * invariant: a derivation preserves everything it doesn't explicitly change. `transform`
20
+ * maps an old key to its new name, or to `undefined` when the field is dropped; each
21
+ * adapter's per-field hints follow their fields.
22
+ */
23
+ function deriveHints(hints, transform) {
24
+ if (!hints)
25
+ return undefined;
26
+ const out = {};
27
+ for (const [adapter, perField] of Object.entries(hints)) {
28
+ if (!perField || typeof perField !== 'object')
29
+ continue;
30
+ const mapped = {};
31
+ for (const [key, hint] of Object.entries(perField)) {
32
+ const next = transform(key);
33
+ if (next !== undefined)
34
+ mapped[next] = hint;
35
+ }
36
+ if (Object.keys(mapped).length)
37
+ out[adapter] = mapped;
38
+ }
39
+ return Object.keys(out).length ? out : undefined;
40
+ }
41
+ /**
42
+ * Refuse a key the schema does not carry, naming it and what was expected — the twin of
43
+ * `assertListOptions` in `@fougere/core`, applied to the derivation algebra.
44
+ *
45
+ * `pick` skipped an unknown key and `omit` filtered on a key nothing matched, so a typo
46
+ * was silently obeyed. `pick('titel')` returned a view without the field, and — the
47
+ * dangerous half — `omit('ownerUserID')` removed NOTHING: the field stayed in the input
48
+ * view, so the façade's unknown-key refusal no longer applied to it, and a client could
49
+ * write the very field the view was written to close. Measured: the view accepted
50
+ * `ownerUserId: 'someone-else'`.
51
+ *
52
+ * TypeScript catches this at the call site when the key is a literal. It does not when
53
+ * the keys come from a variable, from JSON, or across a package boundary — and a view
54
+ * that quietly widens is not a thing to leave to the type-checker alone.
55
+ */
56
+ function assertKnownKeys(operation, keys, fields) {
57
+ const strangers = keys.filter((key) => !(key in fields));
58
+ if (strangers.length === 0)
59
+ return;
60
+ throw new Error(`${operation}(): unknown field ${strangers.map((s) => `\`${s}\``).join(', ')}. ` +
61
+ `This schema carries ${Object.keys(fields).join(', ')}.`);
62
+ }
63
+ /** Create a schema constructor from a field record. */
64
+ /**
65
+ * The runtime name a derivation carries when no class declaration named it.
66
+ *
67
+ * `class Post extends entity({…}) {}` is named by its declaration; `User.pick('id')` has
68
+ * only the factory's own class to be named after. A reader — the scanner, notably — needs
69
+ * to tell one from the other, so the name is stamped from here rather than read off a
70
+ * class whose identifier someone could rename without noticing the other end.
71
+ */
72
+ export const ANONYMOUS_SCHEMA_NAME = 'Schema';
73
+ export function createSchemaConstructor(fields, source, hints, opts = {}, unique) {
74
+ class Schema {
75
+ /** Trusted constructor — assigns already-shaped data. Validate untrusted input via `validate()`/`from()` first. */
76
+ constructor(data) {
77
+ if (data)
78
+ Object.assign(this, data);
79
+ }
80
+ static get ['~standard']() {
81
+ return {
82
+ version: 1,
83
+ vendor: 'fougere',
84
+ validate(value) {
85
+ const result = validateFields(fields, value, '', opts);
86
+ if (result.success) {
87
+ return { value: result.data };
88
+ }
89
+ return {
90
+ issues: result.errors.map((e) => ({
91
+ message: e.message,
92
+ path: e.path && e.path !== '.' ? e.path.split('.').map((key) => ({ key })) : undefined,
93
+ })),
94
+ };
95
+ },
96
+ };
97
+ }
98
+ static source = source;
99
+ static getFields() {
100
+ return fields;
101
+ }
102
+ static getHints() {
103
+ return hints;
104
+ }
105
+ static getUnique() {
106
+ return unique;
107
+ }
108
+ static getOpts() {
109
+ return opts;
110
+ }
111
+ static validate(input) {
112
+ return validateFields(fields, input, '', opts);
113
+ }
114
+ static from(data) {
115
+ // Trusted projection: keep known keys, drop the rest. Runs the boundary's decode
116
+ // (the same wire→domain step validate() applies) so the returned value matches
117
+ // its declared type — a date-string in becomes the `Date` the type promises.
118
+ // Best-effort: a value that fails to decode is kept as-is rather than dropped.
119
+ const result = {};
120
+ for (const [key, field] of Object.entries(fields)) {
121
+ if (!(key in data))
122
+ continue;
123
+ const value = data[key];
124
+ if (value === null || value === undefined) {
125
+ result[key] = value;
126
+ continue;
127
+ }
128
+ const decoded = resolveBoundary(field).decode(value);
129
+ result[key] = 'error' in decoded ? value : decoded.value;
130
+ }
131
+ return result;
132
+ }
133
+ // Every derivation carries hints and opts — same invariant as cloneField, one
134
+ // level up: change the fields you mean, keep everything else the view holds.
135
+ //
136
+ // `source ?? this` records WHERE a view came from, once, at the first derivation:
137
+ // `this` is the class the static was called on (`Post`), and a view of a view keeps
138
+ // the origin rather than the intermediate. The slot existed and was only ever
139
+ // propagated — `entity()` passes `undefined` — so `describe` read an empty field and
140
+ // titled a view `Schema`, the anonymous class. One reader assumed it; now two do
141
+ // (GraphQL names `PostStatus` from it, so an input view and the type share one enum).
142
+ static pick(...keys) {
143
+ assertKnownKeys('pick', keys, fields);
144
+ const picked = {};
145
+ for (const key of keys) {
146
+ if (fields[key])
147
+ picked[key] = fields[key];
148
+ }
149
+ const survives = (k) => (keys.includes(k) ? k : undefined);
150
+ return createSchemaConstructor(deriveUniqueRoles(picked, survives), source ?? this, deriveHints(hints, survives), opts, deriveUnique(unique, survives));
151
+ }
152
+ static omit(...keys) {
153
+ assertKnownKeys('omit', keys, fields);
154
+ const omitted = {};
155
+ for (const [key, value] of Object.entries(fields)) {
156
+ if (!keys.includes(key))
157
+ omitted[key] = value;
158
+ }
159
+ const survives = (k) => (keys.includes(k) ? undefined : k);
160
+ return createSchemaConstructor(deriveUniqueRoles(omitted, survives), source ?? this, deriveHints(hints, survives), opts, deriveUnique(unique, survives));
161
+ }
162
+ static partial() {
163
+ // patch mode: an unsent field is omitted ("don't touch"), enforced by
164
+ // ValidateOptions.patch — the fields themselves are untouched, so `null`
165
+ // stays legal only where the base field is nullable. partial() moves the
166
+ // presence axis, never the nullity axis.
167
+ return createSchemaConstructor({ ...fields }, source ?? this, hints, { ...opts, patch: true }, unique);
168
+ }
169
+ static extend(extra) {
170
+ return createSchemaConstructor({ ...fields, ...extra }, source ?? this, hints, opts, unique);
171
+ }
172
+ static named(name) {
173
+ if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) {
174
+ throw new Error(`named(): \`${name}\` is not a valid class name.`);
175
+ }
176
+ // It renames in place, so it may only name what has no name. On a declared class
177
+ // `Post.named('Other')` would silently retarget its table, its GraphQL type and
178
+ // its registration key — every projection reads this name.
179
+ if (this.name !== ANONYMOUS_SCHEMA_NAME) {
180
+ throw new Error(`named(): \`${this.name}\` is already named by its class declaration.`);
181
+ }
182
+ Object.defineProperty(this, 'name', { value: name, configurable: true });
183
+ return this;
184
+ }
185
+ static rename(mapping) {
186
+ assertKnownKeys('rename', Object.keys(mapping), fields);
187
+ const renamed = {};
188
+ for (const [key, field] of Object.entries(fields)) {
189
+ renamed[mapping[key] ?? key] = field;
190
+ }
191
+ const remap = (k) => mapping[k] ?? k;
192
+ return createSchemaConstructor(deriveUniqueRoles(renamed, remap), source ?? this, deriveHints(hints, remap), opts, deriveUnique(unique, remap));
193
+ }
194
+ }
195
+ Object.defineProperty(Schema, 'name', { value: ANONYMOUS_SCHEMA_NAME, configurable: true });
196
+ return asSchemaConstructor(Schema);
197
+ }
198
+ // ─── Public entry ───────────────────────────────
199
+ /**
200
+ * Define an entity — the factory that produces a schema-carrying class.
201
+ *
202
+ * ```ts
203
+ * class Post extends entity({ id: primary(), title: text({ min: 1 }) }) {}
204
+ *
205
+ * new Post({ id, title }) // real instance, data-typed (NOT a bag of Fields)
206
+ * Post.getFields() // metadata, no instantiation
207
+ * Post.pick('title') // derived view, same static API
208
+ * function publish(p: Post) // `Post` IS the data type — no Infer needed
209
+ * ```
210
+ *
211
+ * The class carries data + schema metadata only. No business behaviour lives on
212
+ * an entity — that belongs to handlers/commands (keeps form and behaviour apart).
213
+ *
214
+ * An optional 2nd argument carries per-consumer hints (see {@link Hints}) for the
215
+ * irreducible bits a neutral field can't express — only adapters present in the
216
+ * compilation are accepted; the field declarations themselves stay adapter-blind.
217
+ */
218
+ export function entity(fields, declarations) {
219
+ // This is the only place that knows both the field KEYS and the entity's declarations,
220
+ // so it is where a composite group becomes readable on each member's role axis. The
221
+ // declaration remains the source — `getUnique()` still answers it.
222
+ const projected = projectUniqueOntoFields(fields, declarations?.unique);
223
+ assertDefaultsAreValid(projected);
224
+ return createSchemaConstructor(projected, undefined, declarations?.hints, {}, declarations?.unique);
225
+ }
226
+ /**
227
+ * A declared default must satisfy its own shape — checked once, here.
228
+ *
229
+ * `applyCreate` writes it into every row without passing the client judge, which is
230
+ * correct: the judge asks "is what the CALLER sent legal", and this value comes from the
231
+ * author. But that means `text({ min: 5, default: 'ab' })` produced rows the entity's own
232
+ * `validate` refuses — silently on a store that judges nothing, as a constraint violation
233
+ * on SQL, as a validator error on MongoDB. Three symptoms, one cause, none of them naming
234
+ * it.
235
+ *
236
+ * The value is static and so is the shape, so the answer is static: it belongs at the
237
+ * declaration, not on every write. `oneOf` closes its own case in the type system; this
238
+ * catches what no type can — a bound, a pattern, a format.
239
+ */
240
+ function assertDefaultsAreValid(fields) {
241
+ for (const [name, field] of Object.entries(fields)) {
242
+ const create = field.lifecycle?.create;
243
+ if (typeof create !== 'object' || create === null || !('value' in create))
244
+ continue;
245
+ const checked = checkValue(field, create.value);
246
+ if ('error' in checked) {
247
+ throw new Error(`Field '${name}': the declared default ${JSON.stringify(create.value)} `
248
+ + `is not a legal value for it — ${checked.error}. It would be written into every row `
249
+ + `without passing the judge.`);
250
+ }
251
+ }
252
+ }
253
+ /**
254
+ * Compose multiple entities/schemas into a single SchemaConstructor.
255
+ *
256
+ * ```ts
257
+ * class User extends compose(UserBase, Timestamps) {}
258
+ * class SearchPosts extends compose(Post.pick('title'), Pagination) {}
259
+ * ```
260
+ *
261
+ * One merge law for EVERYTHING a schema carries — fields, hints, opts: left to
262
+ * right, later sources override earlier ones on conflict (hints merge per adapter,
263
+ * per field key). Use .rename() before compose() to avoid field conflicts.
264
+ */
265
+ export function compose(...sources) {
266
+ const merged = {};
267
+ const mergedHints = {};
268
+ let mergedOpts = {};
269
+ for (const source of sources) {
270
+ Object.assign(merged, source.getFields());
271
+ const carrier = source;
272
+ const hints = carrier.getHints?.();
273
+ if (hints) {
274
+ for (const [adapter, perField] of Object.entries(hints)) {
275
+ if (!perField || typeof perField !== 'object')
276
+ continue;
277
+ mergedHints[adapter] = { ...mergedHints[adapter], ...perField };
278
+ }
279
+ }
280
+ mergedOpts = { ...mergedOpts, ...carrier.getOpts?.() };
281
+ }
282
+ const hints = Object.keys(mergedHints).length ? mergedHints : undefined;
283
+ return asSchemaConstructor(createSchemaConstructor(merged, undefined, hints, mergedOpts));
284
+ }
285
+ //# sourceMappingURL=entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity.js","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import type { AnyField } from './field.js';
2
+ /**
3
+ * Axis 4 · boundary — HOW AND IN WHICH DIRECTION a value crosses the CLIENT frontier.
4
+ *
5
+ * ⚠️ SCOPE — this axis covers the client frontier ONLY. A direction is meaningless
6
+ * unless stated relative to a centre, and this one is relative to the domain facing a
7
+ * client: `in` parses a request, `out` renders a response. It does NOT cover storage —
8
+ * no storage adapter reads it, which is exactly why `bool`, `list`, `json` and a judged
9
+ * `date` cannot be written today (they reach the driver unconverted). The domain↔column
10
+ * conversion belongs to the storage adapter, and naming that second frontier is an open
11
+ * design question — see the axes study.
12
+ *
13
+ * The normal form is indexed by DIRECTION. Each direction carries one of the two facets
14
+ * of the same frontier:
15
+ * a conversion (`{ decode }` / `{ encode }`, a NAMED rule) or the permission
16
+ * token `'closed'` — read-only closes `in` (never accepted from a client),
17
+ * write-only closes `out` (never emitted, e.g. a password). A key absent →
18
+ * that direction is open, identity conversion. Declarative and named — never
19
+ * an opaque closure, so adapters stay able to read what a field does.
20
+ *
21
+ * Two directional registries (decoders, encoders) are the pure base; an alias is
22
+ * just a named pair. A field rarely declares a boundary: the default is DERIVED
23
+ * from `shape` (convention over config). A declared boundary overrides that
24
+ * default PER DIRECTION — closing `out` on a date field leaves the derived
25
+ * isoDate decode on `in` intact.
26
+ */
27
+ /** Inbound: a supplied wire value → domain value. May fail (transformOrFail-style). */
28
+ export type Decoder = (value: unknown) => {
29
+ value: unknown;
30
+ } | {
31
+ error: string;
32
+ };
33
+ /** Outbound: a domain value → wire value. Total — a valid domain value always encodes. */
34
+ export type Encoder = (value: unknown) => unknown;
35
+ /** The normal form, indexed by direction. Either direction absent → open, identity. */
36
+ export interface Boundary {
37
+ in?: 'closed' | {
38
+ decode: string;
39
+ };
40
+ out?: 'closed' | {
41
+ encode: string;
42
+ };
43
+ }
44
+ /**
45
+ * What a field may carry on its `boundary` slot:
46
+ * - a string — an ALIAS, resolved to a {@link Boundary} in the alias registry
47
+ * - a {@link Boundary} — directional rules directly (allows asymmetry)
48
+ *
49
+ * The bare string literals are the built-in aliases; `(string & {})` keeps
50
+ * autocomplete on them while leaving the set open to `registerBoundaryAlias`.
51
+ */
52
+ export type BoundaryRef = 'isoDate' | (string & {}) | Boundary;
53
+ export declare function registerDecoder(name: string, fn: Decoder): void;
54
+ export declare function registerEncoder(name: string, fn: Encoder): void;
55
+ /** Register a named boundary — the alias a field can reference. */
56
+ export declare function registerBoundaryAlias(name: string, boundary: Boundary): void;
57
+ /** The field's DECLARED boundary in normal form (alias resolved) — no derived default. */
58
+ export declare function declaredBoundary(field: AnyField): Boundary;
59
+ /**
60
+ * The field's EFFECTIVE boundary: declared rules win per direction, the
61
+ * shape-derived default fills the rest. This is the reader every consumer of
62
+ * the axis goes through — `boundaryOf(f).in === 'closed'` is the read-only
63
+ * test, `.out === 'closed'` the write-only one.
64
+ */
65
+ export declare function boundaryOf(field: AnyField): Boundary;
66
+ /**
67
+ * A field's effective conversion functions. The permission facet is not read
68
+ * here: a `'closed'` direction converts as identity — its rejection/omission
69
+ * happens in the readers (validation, encode) BEFORE any conversion.
70
+ */
71
+ export declare function resolveBoundary(field: AnyField): {
72
+ decode: Decoder;
73
+ encode: Encoder;
74
+ };
75
+ //# sourceMappingURL=boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../src/field/boundary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACjF,0FAA0F;AAC1F,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;AAElD,uFAAuF;AACvF,MAAM,WAAW,QAAQ;IACvB,EAAE,CAAC,EAAE,QAAQ,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,GAAG,CAAC,EAAE,QAAQ,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,QAAQ,CAAC;AAQ/D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,IAAI,CAE/D;AACD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,IAAI,CAE/D;AACD,mEAAmE;AACnE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAE5E;AAgCD,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAS1D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAIpD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAKrF"}
@@ -0,0 +1,76 @@
1
+ import { anatomy } from './shape.js';
2
+ // ─── Registries (open, extensible — same spirit as FougereHints) ──
3
+ const decoders = new Map();
4
+ const encoders = new Map();
5
+ const aliases = new Map();
6
+ export function registerDecoder(name, fn) {
7
+ decoders.set(name, fn);
8
+ }
9
+ export function registerEncoder(name, fn) {
10
+ encoders.set(name, fn);
11
+ }
12
+ /** Register a named boundary — the alias a field can reference. */
13
+ export function registerBoundaryAlias(name, boundary) {
14
+ aliases.set(name, boundary);
15
+ }
16
+ // ─── Built-ins ───────────────────────────────────────
17
+ const identityDecoder = (value) => ({ value });
18
+ const identityEncoder = (value) => value;
19
+ // `isoDate`: the only non-identity built-in. Inbound accepts a Date or an ISO-ish
20
+ // string and yields a Date; outbound yields an ISO string. Validity is already
21
+ // guaranteed by `shape` (the date predicate) before decode runs.
22
+ registerDecoder('isoDate', (value) => {
23
+ if (value instanceof Date)
24
+ return { value };
25
+ if (typeof value === 'string') {
26
+ const d = new Date(value);
27
+ return Number.isNaN(d.getTime()) ? { error: 'Invalid date' } : { value: d };
28
+ }
29
+ return { error: 'Expected a date' };
30
+ });
31
+ registerEncoder('isoDate', (value) => value instanceof Date ? value.toISOString() : value);
32
+ registerBoundaryAlias('isoDate', { in: { decode: 'isoDate' }, out: { encode: 'isoDate' } });
33
+ /** Default boundary derived from a field's shape. A date-time string → isoDate, else identity. */
34
+ function defaultBoundaryForShape(shape) {
35
+ const base = anatomy(shape).base;
36
+ if (base && base.type === 'string' && base.format === 'date-time')
37
+ return aliases.get('isoDate');
38
+ return {};
39
+ }
40
+ // ─── Resolution ──────────────────────────────────────
41
+ /** The field's DECLARED boundary in normal form (alias resolved) — no derived default. */
42
+ export function declaredBoundary(field) {
43
+ const ref = field.boundary;
44
+ if (ref === undefined)
45
+ return {};
46
+ if (typeof ref === 'string') {
47
+ const alias = aliases.get(ref);
48
+ if (!alias)
49
+ throw new Error(`Unknown boundary alias: '${ref}'`);
50
+ return alias;
51
+ }
52
+ return ref;
53
+ }
54
+ /**
55
+ * The field's EFFECTIVE boundary: declared rules win per direction, the
56
+ * shape-derived default fills the rest. This is the reader every consumer of
57
+ * the axis goes through — `boundaryOf(f).in === 'closed'` is the read-only
58
+ * test, `.out === 'closed'` the write-only one.
59
+ */
60
+ export function boundaryOf(field) {
61
+ const declared = declaredBoundary(field);
62
+ const derived = defaultBoundaryForShape(field.shape);
63
+ return { in: declared.in ?? derived.in, out: declared.out ?? derived.out };
64
+ }
65
+ /**
66
+ * A field's effective conversion functions. The permission facet is not read
67
+ * here: a `'closed'` direction converts as identity — its rejection/omission
68
+ * happens in the readers (validation, encode) BEFORE any conversion.
69
+ */
70
+ export function resolveBoundary(field) {
71
+ const boundary = boundaryOf(field);
72
+ const decode = typeof boundary.in === 'object' ? decoders.get(boundary.in.decode) : undefined;
73
+ const encode = typeof boundary.out === 'object' ? encoders.get(boundary.out.encode) : undefined;
74
+ return { decode: decode ?? identityDecoder, encode: encode ?? identityEncoder };
75
+ }
76
+ //# sourceMappingURL=boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.js","sourceRoot":"","sources":["../../src/field/boundary.ts"],"names":[],"mappings":"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"}