@fougere/schema 0.2.0-alpha.0 → 0.2.0-alpha.2
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/dist/entity.d.ts +9 -106
- package/dist/entity.d.ts.map +1 -1
- package/dist/entity.js +16 -266
- package/dist/entity.js.map +1 -1
- package/dist/field/boundary.d.ts +26 -43
- package/dist/field/boundary.d.ts.map +1 -1
- package/dist/field/boundary.js +30 -42
- package/dist/field/boundary.js.map +1 -1
- package/dist/field/field.d.ts +13 -56
- package/dist/field/field.d.ts.map +1 -1
- package/dist/field/field.js +22 -26
- package/dist/field/field.js.map +1 -1
- package/dist/field/format.d.ts +29 -0
- package/dist/field/format.d.ts.map +1 -0
- package/dist/field/format.js +24 -0
- package/dist/field/format.js.map +1 -0
- package/dist/field/index.d.ts +7 -6
- package/dist/field/index.d.ts.map +1 -1
- package/dist/field/index.js +6 -5
- package/dist/field/index.js.map +1 -1
- package/dist/field/lifecycle.d.ts +15 -24
- package/dist/field/lifecycle.d.ts.map +1 -1
- package/dist/field/lifecycle.js +10 -17
- package/dist/field/lifecycle.js.map +1 -1
- package/dist/field/meta.d.ts +1 -5
- package/dist/field/meta.d.ts.map +1 -1
- package/dist/field/meta.js +2 -4
- package/dist/field/meta.js.map +1 -1
- package/dist/field/role.d.ts +15 -22
- package/dist/field/role.d.ts.map +1 -1
- package/dist/field/role.js +11 -31
- package/dist/field/role.js.map +1 -1
- package/dist/field/shape.d.ts +24 -12
- package/dist/field/shape.d.ts.map +1 -1
- package/dist/field/shape.js +45 -27
- package/dist/field/shape.js.map +1 -1
- package/dist/hints.d.ts +10 -15
- package/dist/hints.d.ts.map +1 -1
- package/dist/hints.js +22 -1
- package/dist/hints.js.map +1 -1
- package/dist/index.d.ts +37 -36
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -36
- package/dist/index.js.map +1 -1
- package/dist/name.d.ts +4 -12
- package/dist/name.d.ts.map +1 -1
- package/dist/name.js +4 -12
- package/dist/name.js.map +1 -1
- package/dist/projections/card.d.ts +39 -58
- package/dist/projections/card.d.ts.map +1 -1
- package/dist/projections/card.js.map +1 -1
- package/dist/projections/describe.d.ts +4 -4
- package/dist/projections/describe.d.ts.map +1 -1
- package/dist/projections/describe.js +4 -9
- package/dist/projections/describe.js.map +1 -1
- package/dist/projections/io.d.ts +5 -11
- package/dist/projections/io.d.ts.map +1 -1
- package/dist/projections/io.js +5 -11
- package/dist/projections/io.js.map +1 -1
- package/dist/projections/lifecycle.d.ts +5 -19
- package/dist/projections/lifecycle.d.ts.map +1 -1
- package/dist/projections/lifecycle.js +7 -29
- package/dist/projections/lifecycle.js.map +1 -1
- package/dist/projections/reconstruct.d.ts +11 -16
- package/dist/projections/reconstruct.d.ts.map +1 -1
- package/dist/projections/reconstruct.js +29 -32
- package/dist/projections/reconstruct.js.map +1 -1
- package/dist/projections/source.d.ts +11 -34
- package/dist/projections/source.d.ts.map +1 -1
- package/dist/projections/source.js +4 -23
- package/dist/projections/source.js.map +1 -1
- package/dist/projections/typescript.d.ts +6 -22
- package/dist/projections/typescript.d.ts.map +1 -1
- package/dist/projections/typescript.js +16 -43
- package/dist/projections/typescript.js.map +1 -1
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/schema.d.ts +74 -0
- package/dist/schema/schema.d.ts.map +1 -0
- package/dist/schema/schema.js +162 -0
- package/dist/schema/schema.js.map +1 -0
- package/dist/schema/view.d.ts +26 -0
- package/dist/schema/view.d.ts.map +1 -0
- package/dist/schema/view.js +2 -0
- package/dist/schema/view.js.map +1 -0
- package/dist/unique.d.ts +12 -37
- package/dist/unique.d.ts.map +1 -1
- package/dist/unique.js +11 -34
- package/dist/unique.js.map +1 -1
- package/dist/validation/index.d.ts +4 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +2 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/judge.d.ts +55 -0
- package/dist/validation/judge.d.ts.map +1 -0
- package/dist/validation/judge.js +351 -0
- package/dist/validation/judge.js.map +1 -0
- package/dist/validation/options.d.ts +6 -0
- package/dist/validation/options.d.ts.map +1 -0
- package/dist/validation/options.js +3 -0
- package/dist/validation/options.js.map +1 -0
- package/dist/validation/result.d.ts +20 -0
- package/dist/validation/result.d.ts.map +1 -0
- package/dist/validation/result.js +3 -0
- package/dist/validation/result.js.map +1 -0
- package/dist/vocabulary/bool.d.ts +1 -1
- package/dist/vocabulary/bool.d.ts.map +1 -1
- package/dist/vocabulary/bool.js +2 -2
- package/dist/vocabulary/bool.js.map +1 -1
- package/dist/vocabulary/created.d.ts +4 -10
- package/dist/vocabulary/created.d.ts.map +1 -1
- package/dist/vocabulary/created.js +5 -12
- package/dist/vocabulary/created.js.map +1 -1
- package/dist/vocabulary/date.d.ts +1 -1
- package/dist/vocabulary/date.d.ts.map +1 -1
- package/dist/vocabulary/date.js +2 -2
- package/dist/vocabulary/date.js.map +1 -1
- package/dist/vocabulary/immutable.d.ts +4 -6
- package/dist/vocabulary/immutable.d.ts.map +1 -1
- package/dist/vocabulary/immutable.js +3 -6
- package/dist/vocabulary/immutable.js.map +1 -1
- package/dist/vocabulary/indexed.d.ts +5 -16
- package/dist/vocabulary/indexed.d.ts.map +1 -1
- package/dist/vocabulary/indexed.js +4 -16
- package/dist/vocabulary/indexed.js.map +1 -1
- package/dist/vocabulary/json.d.ts +8 -13
- package/dist/vocabulary/json.d.ts.map +1 -1
- package/dist/vocabulary/json.js +3 -3
- package/dist/vocabulary/json.js.map +1 -1
- package/dist/vocabulary/list.d.ts +3 -6
- package/dist/vocabulary/list.d.ts.map +1 -1
- package/dist/vocabulary/list.js +6 -5
- package/dist/vocabulary/list.js.map +1 -1
- package/dist/vocabulary/many.d.ts +3 -4
- package/dist/vocabulary/many.d.ts.map +1 -1
- package/dist/vocabulary/many.js +5 -5
- package/dist/vocabulary/many.js.map +1 -1
- package/dist/vocabulary/nullable.d.ts +2 -4
- package/dist/vocabulary/nullable.d.ts.map +1 -1
- package/dist/vocabulary/nullable.js +4 -8
- package/dist/vocabulary/nullable.js.map +1 -1
- package/dist/vocabulary/number.d.ts +1 -1
- package/dist/vocabulary/number.d.ts.map +1 -1
- package/dist/vocabulary/number.js +2 -2
- package/dist/vocabulary/number.js.map +1 -1
- package/dist/vocabulary/oneOf.d.ts +1 -1
- package/dist/vocabulary/oneOf.d.ts.map +1 -1
- package/dist/vocabulary/oneOf.js +2 -2
- package/dist/vocabulary/oneOf.js.map +1 -1
- package/dist/vocabulary/optional.d.ts +1 -1
- package/dist/vocabulary/optional.d.ts.map +1 -1
- package/dist/vocabulary/optional.js +3 -3
- package/dist/vocabulary/optional.js.map +1 -1
- package/dist/vocabulary/primary.d.ts +5 -5
- package/dist/vocabulary/primary.d.ts.map +1 -1
- package/dist/vocabulary/primary.js +14 -11
- package/dist/vocabulary/primary.js.map +1 -1
- package/dist/vocabulary/readOnly.d.ts +4 -7
- package/dist/vocabulary/readOnly.d.ts.map +1 -1
- package/dist/vocabulary/readOnly.js +4 -7
- package/dist/vocabulary/readOnly.js.map +1 -1
- package/dist/vocabulary/ref.d.ts +1 -1
- package/dist/vocabulary/ref.d.ts.map +1 -1
- package/dist/vocabulary/ref.js +2 -2
- package/dist/vocabulary/ref.js.map +1 -1
- package/dist/vocabulary/text.d.ts +1 -1
- package/dist/vocabulary/text.d.ts.map +1 -1
- package/dist/vocabulary/text.js +2 -2
- package/dist/vocabulary/text.js.map +1 -1
- package/dist/vocabulary/unique.d.ts +8 -18
- package/dist/vocabulary/unique.d.ts.map +1 -1
- package/dist/vocabulary/unique.js +7 -18
- package/dist/vocabulary/unique.js.map +1 -1
- package/dist/vocabulary/updated.d.ts +2 -2
- package/dist/vocabulary/updated.d.ts.map +1 -1
- package/dist/vocabulary/updated.js +1 -2
- package/dist/vocabulary/updated.js.map +1 -1
- package/dist/vocabulary/writeOnly.d.ts +4 -6
- package/dist/vocabulary/writeOnly.d.ts.map +1 -1
- package/dist/vocabulary/writeOnly.js +4 -6
- package/dist/vocabulary/writeOnly.js.map +1 -1
- package/package.json +2 -2
- package/dist/projections/validation.d.ts +0 -57
- package/dist/projections/validation.d.ts.map +0 -1
- package/dist/projections/validation.js +0 -148
- package/dist/projections/validation.js.map +0 -1
package/dist/entity.d.ts
CHANGED
|
@@ -1,117 +1,20 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import type
|
|
3
|
-
import { type
|
|
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>;
|
|
1
|
+
import { type Fields } from "./field/index.js";
|
|
2
|
+
import { type EntityDeclarations } from "./unique.js";
|
|
3
|
+
import { type SchemaConstructor } from "./schema/index.js";
|
|
75
4
|
/**
|
|
76
5
|
* Define an entity — the factory that produces a schema-carrying class.
|
|
77
6
|
*
|
|
78
7
|
* ```ts
|
|
79
8
|
* class Post extends entity({ id: primary(), title: text({ min: 1 }) }) {}
|
|
80
9
|
*
|
|
81
|
-
* new Post({ id, title }) // real instance, data-typed
|
|
82
|
-
* Post.
|
|
83
|
-
* Post
|
|
84
|
-
* function publish(p: Post) // `Post` IS the data type — no Infer needed
|
|
10
|
+
* new Post({ id, title }) // a real instance, data-typed
|
|
11
|
+
* Post.pick('title') // a derived view, same static API
|
|
12
|
+
* function publish(p: Post) // `Post` IS the data type
|
|
85
13
|
* ```
|
|
86
14
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
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.
|
|
15
|
+
* Data and schema metadata only — no business behaviour lives on an entity. The 2nd
|
|
16
|
+
* argument carries what the entity states about itself: `unique` groups, and per-adapter
|
|
17
|
+
* hints for what a neutral field cannot express.
|
|
93
18
|
*/
|
|
94
19
|
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
20
|
//# sourceMappingURL=entity.d.ts.map
|
package/dist/entity.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAA2B,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAU,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEnE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CAAC,OAAO,SAAS,MAAM,EAC3C,MAAM,EAAE,OAAO,EACf,YAAY,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,GACzC,iBAAiB,CAAC,OAAO,CAAC,CAM5B"}
|
package/dist/entity.js
CHANGED
|
@@ -1,254 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
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
|
-
// ONE segment, not a split: `validateFields` joins with `.` only when it
|
|
93
|
-
// receives a `pathPrefix`, and no caller passes one — the recursion that
|
|
94
|
-
// parameter exists for is not written. So a path is always a single field
|
|
95
|
-
// name, and splitting it invented segments for a name legally spelled `a.b`.
|
|
96
|
-
// The day nested objects report their own path, this becomes a real split
|
|
97
|
-
// and the joining side has to say where the boundaries are.
|
|
98
|
-
path: e.path && e.path !== '.' ? [{ key: e.path }] : undefined,
|
|
99
|
-
})),
|
|
100
|
-
};
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
static source = source;
|
|
105
|
-
static getFields() {
|
|
106
|
-
return fields;
|
|
107
|
-
}
|
|
108
|
-
static getHints() {
|
|
109
|
-
return hints;
|
|
110
|
-
}
|
|
111
|
-
static getUnique() {
|
|
112
|
-
return unique;
|
|
113
|
-
}
|
|
114
|
-
static getOpts() {
|
|
115
|
-
return opts;
|
|
116
|
-
}
|
|
117
|
-
static validate(input) {
|
|
118
|
-
return validateFields(fields, input, opts);
|
|
119
|
-
}
|
|
120
|
-
static from(data) {
|
|
121
|
-
// Trusted projection: keep known keys, drop the rest. Runs the boundary's decode
|
|
122
|
-
// (the same wire→domain step validate() applies) so the returned value matches
|
|
123
|
-
// its declared type — a date-string in becomes the `Date` the type promises.
|
|
124
|
-
// Best-effort: a value that fails to decode is kept as-is rather than dropped.
|
|
125
|
-
const result = {};
|
|
126
|
-
for (const [key, field] of Object.entries(fields)) {
|
|
127
|
-
if (!(key in data))
|
|
128
|
-
continue;
|
|
129
|
-
const value = data[key];
|
|
130
|
-
if (value === null || value === undefined) {
|
|
131
|
-
result[key] = value;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
const decoded = resolveBoundary(field).decode(value);
|
|
135
|
-
result[key] = 'error' in decoded ? value : decoded.value;
|
|
136
|
-
}
|
|
137
|
-
return result;
|
|
138
|
-
}
|
|
139
|
-
// Every derivation carries hints and opts — same invariant as cloneField, one
|
|
140
|
-
// level up: change the fields you mean, keep everything else the view holds.
|
|
141
|
-
//
|
|
142
|
-
// `source ?? this` records WHERE a view came from, once, at the first derivation:
|
|
143
|
-
// `this` is the class the static was called on (`Post`), and a view of a view keeps
|
|
144
|
-
// the origin rather than the intermediate. The slot existed and was only ever
|
|
145
|
-
// propagated — `entity()` passes `undefined` — so `describe` read an empty field and
|
|
146
|
-
// titled a view `Schema`, the anonymous class. One reader assumed it; now two do
|
|
147
|
-
// (GraphQL names `PostStatus` from it, so an input view and the type share one enum).
|
|
148
|
-
static pick(...keys) {
|
|
149
|
-
assertKnownKeys('pick', keys, fields);
|
|
150
|
-
const picked = {};
|
|
151
|
-
for (const key of keys) {
|
|
152
|
-
if (fields[key])
|
|
153
|
-
picked[key] = fields[key];
|
|
154
|
-
}
|
|
155
|
-
const survives = (k) => (keys.includes(k) ? k : undefined);
|
|
156
|
-
return createSchemaConstructor(deriveUniqueRoles(picked, survives), source ?? this, deriveHints(hints, survives), opts, deriveUnique(unique, survives));
|
|
157
|
-
}
|
|
158
|
-
static omit(...keys) {
|
|
159
|
-
assertKnownKeys('omit', keys, fields);
|
|
160
|
-
const omitted = {};
|
|
161
|
-
for (const [key, value] of Object.entries(fields)) {
|
|
162
|
-
if (!keys.includes(key))
|
|
163
|
-
omitted[key] = value;
|
|
164
|
-
}
|
|
165
|
-
const survives = (k) => (keys.includes(k) ? undefined : k);
|
|
166
|
-
return createSchemaConstructor(deriveUniqueRoles(omitted, survives), source ?? this, deriveHints(hints, survives), opts, deriveUnique(unique, survives));
|
|
167
|
-
}
|
|
168
|
-
static partial() {
|
|
169
|
-
// patch mode: an unsent field is omitted ("don't touch"), enforced by
|
|
170
|
-
// ValidateOptions.patch — the fields themselves are untouched, so `null`
|
|
171
|
-
// stays legal only where the base field is nullable. partial() moves the
|
|
172
|
-
// presence axis, never the nullity axis.
|
|
173
|
-
return createSchemaConstructor({ ...fields }, source ?? this, hints, { ...opts, patch: true }, unique);
|
|
174
|
-
}
|
|
175
|
-
static extend(extra) {
|
|
176
|
-
return createSchemaConstructor({ ...fields, ...extra }, source ?? this, hints, opts, unique);
|
|
177
|
-
}
|
|
178
|
-
static named(name) {
|
|
179
|
-
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) {
|
|
180
|
-
throw new Error(`named(): \`${name}\` is not a valid class name.`);
|
|
181
|
-
}
|
|
182
|
-
// It renames in place, so it may only name what has no name. On a declared class
|
|
183
|
-
// `Post.named('Other')` would silently retarget its table, its GraphQL type and
|
|
184
|
-
// its registration key — every projection reads this name.
|
|
185
|
-
if (this.name !== ANONYMOUS_SCHEMA_NAME) {
|
|
186
|
-
throw new Error(`named(): \`${this.name}\` is already named by its class declaration.`);
|
|
187
|
-
}
|
|
188
|
-
Object.defineProperty(this, 'name', { value: name, configurable: true });
|
|
189
|
-
return this;
|
|
190
|
-
}
|
|
191
|
-
static rename(mapping) {
|
|
192
|
-
assertKnownKeys('rename', Object.keys(mapping), fields);
|
|
193
|
-
const renamed = {};
|
|
194
|
-
for (const [key, field] of Object.entries(fields)) {
|
|
195
|
-
renamed[mapping[key] ?? key] = field;
|
|
196
|
-
}
|
|
197
|
-
const remap = (k) => mapping[k] ?? k;
|
|
198
|
-
return createSchemaConstructor(deriveUniqueRoles(renamed, remap), source ?? this, deriveHints(hints, remap), opts, deriveUnique(unique, remap));
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
Object.defineProperty(Schema, 'name', { value: ANONYMOUS_SCHEMA_NAME, configurable: true });
|
|
202
|
-
return asSchemaConstructor(Schema);
|
|
203
|
-
}
|
|
204
|
-
// ─── Public entry ───────────────────────────────
|
|
1
|
+
import { Field } from "./field/index.js";
|
|
2
|
+
import { Judge } from './validation/index.js';
|
|
3
|
+
import { projectUniqueOntoFields } from "./unique.js";
|
|
4
|
+
import { Schema } from "./schema/index.js";
|
|
205
5
|
/**
|
|
206
6
|
* Define an entity — the factory that produces a schema-carrying class.
|
|
207
7
|
*
|
|
208
8
|
* ```ts
|
|
209
9
|
* class Post extends entity({ id: primary(), title: text({ min: 1 }) }) {}
|
|
210
10
|
*
|
|
211
|
-
* new Post({ id, title }) // real instance, data-typed
|
|
212
|
-
* Post.
|
|
213
|
-
* Post
|
|
214
|
-
* function publish(p: Post) // `Post` IS the data type — no Infer needed
|
|
11
|
+
* new Post({ id, title }) // a real instance, data-typed
|
|
12
|
+
* Post.pick('title') // a derived view, same static API
|
|
13
|
+
* function publish(p: Post) // `Post` IS the data type
|
|
215
14
|
* ```
|
|
216
15
|
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* An optional 2nd argument carries per-consumer hints (see {@link Hints}) for the
|
|
221
|
-
* irreducible bits a neutral field can't express — only adapters present in the
|
|
222
|
-
* compilation are accepted; the field declarations themselves stay adapter-blind.
|
|
16
|
+
* Data and schema metadata only — no business behaviour lives on an entity. The 2nd
|
|
17
|
+
* argument carries what the entity states about itself: `unique` groups, and per-adapter
|
|
18
|
+
* hints for what a neutral field cannot express.
|
|
223
19
|
*/
|
|
224
20
|
export function entity(fields, declarations) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const projected = projectUniqueOntoFields(
|
|
21
|
+
const own = {};
|
|
22
|
+
for (const [key, field] of Object.entries(fields))
|
|
23
|
+
own[key] = new Field(field, key);
|
|
24
|
+
const projected = projectUniqueOntoFields(own, declarations?.unique);
|
|
229
25
|
assertDefaultsAreValid(projected);
|
|
230
|
-
return
|
|
26
|
+
return Schema.of(projected, undefined, declarations?.hints, {}, declarations?.unique);
|
|
231
27
|
}
|
|
232
|
-
/**
|
|
233
|
-
* A declared default must satisfy its own shape — checked once, here.
|
|
234
|
-
*
|
|
235
|
-
* `applyCreate` writes it into every row without passing the client judge, which is
|
|
236
|
-
* correct: the judge asks "is what the CALLER sent legal", and this value comes from the
|
|
237
|
-
* author. But that means `text({ min: 5, default: 'ab' })` produced rows the entity's own
|
|
238
|
-
* `validate` refuses — silently on a store that judges nothing, as a constraint violation
|
|
239
|
-
* on SQL, as a validator error on MongoDB. Three symptoms, one cause, none of them naming
|
|
240
|
-
* it.
|
|
241
|
-
*
|
|
242
|
-
* The value is static and so is the shape, so the answer is static: it belongs at the
|
|
243
|
-
* declaration, not on every write. `oneOf` closes its own case in the type system; this
|
|
244
|
-
* catches what no type can — a bound, a pattern, a format.
|
|
245
|
-
*/
|
|
246
28
|
function assertDefaultsAreValid(fields) {
|
|
247
29
|
for (const [name, field] of Object.entries(fields)) {
|
|
248
30
|
const create = field.lifecycle?.create;
|
|
249
31
|
if (typeof create !== 'object' || create === null || !('value' in create))
|
|
250
32
|
continue;
|
|
251
|
-
const checked =
|
|
33
|
+
const checked = Judge.value(field, create.value);
|
|
252
34
|
if ('error' in checked) {
|
|
253
35
|
throw new Error(`Field '${name}': the declared default ${JSON.stringify(create.value)} `
|
|
254
36
|
+ `is not a legal value for it — ${checked.error}. It would be written into every row `
|
|
@@ -256,36 +38,4 @@ function assertDefaultsAreValid(fields) {
|
|
|
256
38
|
}
|
|
257
39
|
}
|
|
258
40
|
}
|
|
259
|
-
/**
|
|
260
|
-
* Compose multiple entities/schemas into a single SchemaConstructor.
|
|
261
|
-
*
|
|
262
|
-
* ```ts
|
|
263
|
-
* class User extends compose(UserBase, Timestamps) {}
|
|
264
|
-
* class SearchPosts extends compose(Post.pick('title'), Pagination) {}
|
|
265
|
-
* ```
|
|
266
|
-
*
|
|
267
|
-
* One merge law for EVERYTHING a schema carries — fields, hints, opts: left to
|
|
268
|
-
* right, later sources override earlier ones on conflict (hints merge per adapter,
|
|
269
|
-
* per field key). Use .rename() before compose() to avoid field conflicts.
|
|
270
|
-
*/
|
|
271
|
-
export function compose(...sources) {
|
|
272
|
-
const merged = {};
|
|
273
|
-
const mergedHints = {};
|
|
274
|
-
let mergedOpts = {};
|
|
275
|
-
for (const source of sources) {
|
|
276
|
-
Object.assign(merged, source.getFields());
|
|
277
|
-
const carrier = source;
|
|
278
|
-
const hints = carrier.getHints?.();
|
|
279
|
-
if (hints) {
|
|
280
|
-
for (const [adapter, perField] of Object.entries(hints)) {
|
|
281
|
-
if (!perField || typeof perField !== 'object')
|
|
282
|
-
continue;
|
|
283
|
-
mergedHints[adapter] = { ...mergedHints[adapter], ...perField };
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
mergedOpts = { ...mergedOpts, ...carrier.getOpts?.() };
|
|
287
|
-
}
|
|
288
|
-
const hints = Object.keys(mergedHints).length ? mergedHints : undefined;
|
|
289
|
-
return asSchemaConstructor(createSchemaConstructor(merged, undefined, hints, mergedOpts));
|
|
290
|
-
}
|
|
291
41
|
//# sourceMappingURL=entity.js.map
|
package/dist/entity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity.js","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"entity.js","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAe,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAA2B,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAE,MAAM,EAA0B,MAAM,mBAAmB,CAAC;AAEnE;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,MAAM,CACpB,MAAe,EACf,YAA0C;IAE1C,MAAM,GAAG,GAAG,EAA2B,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACpF,MAAM,SAAS,GAAG,uBAAuB,CAAC,GAAc,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IAChF,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAClC,OAAO,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;AACxF,CAAC;AAED,SAAS,sBAAsB,CAAC,MAAc;IAC5C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC;QACvC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC;YAAE,SAAS;QAEpF,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAG,MAA6B,CAAC,KAAK,CAAC,CAAC;QACzE,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,UAAU,IAAI,2BAA2B,IAAI,CAAC,SAAS,CAAE,MAA6B,CAAC,KAAK,CAAC,GAAG;kBAC9F,iCAAiC,OAAO,CAAC,KAAK,uCAAuC;kBACrF,4BAA4B,CAC/B,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/field/boundary.d.ts
CHANGED
|
@@ -1,28 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Field } from './field.js';
|
|
2
2
|
/**
|
|
3
|
-
* Axis 4 · boundary — HOW
|
|
3
|
+
* Axis 4 · boundary — HOW and IN WHICH DIRECTION a value crosses the CLIENT frontier.
|
|
4
4
|
*
|
|
5
|
-
* ⚠️
|
|
6
|
-
*
|
|
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.
|
|
5
|
+
* ⚠️ The client frontier ONLY: `in` parses a request, `out` renders a response. No storage
|
|
6
|
+
* adapter reads it, so the domain↔column conversion is a second frontier, unnamed today.
|
|
12
7
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
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.
|
|
8
|
+
* Each direction carries one of two facets: a NAMED conversion (`{ decode }`/`{ encode }`)
|
|
9
|
+
* or the permission token `'closed'`. Absent → open, identity. Named and never a closure,
|
|
10
|
+
* so an adapter can read what a field does. The default is derived from `shape`; a
|
|
11
|
+
* declared boundary overrides it per direction.
|
|
26
12
|
*/
|
|
27
13
|
/** Inbound: a supplied wire value → domain value. May fail (transformOrFail-style). */
|
|
28
14
|
export type Decoder = (value: unknown) => {
|
|
@@ -50,36 +36,33 @@ export interface Boundary {
|
|
|
50
36
|
* autocomplete on them while leaving the set open to `registerBoundaryAlias`.
|
|
51
37
|
*/
|
|
52
38
|
export type BoundaryRef = 'isoDate' | (string & {}) | Boundary;
|
|
53
|
-
export declare
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
39
|
+
export declare class Boundaries {
|
|
40
|
+
private static readonly decoders;
|
|
41
|
+
private static readonly encoders;
|
|
42
|
+
private static readonly aliases;
|
|
43
|
+
static registerDecoder(name: string, fn: Decoder): void;
|
|
44
|
+
static registerEncoder(name: string, fn: Encoder): void;
|
|
45
|
+
/** Name a pair of directional rules, so a field declares `boundary: 'moneyCents'`. */
|
|
46
|
+
static registerAlias(name: string, boundary: Boundary): void;
|
|
47
|
+
static alias(name: string): Boundary | undefined;
|
|
48
|
+
static decoder(name: string): Decoder;
|
|
49
|
+
static encoder(name: string): Encoder;
|
|
50
|
+
private static named;
|
|
51
|
+
}
|
|
57
52
|
/** The field's DECLARED boundary in normal form (alias resolved) — no derived default. */
|
|
58
|
-
export declare function declaredBoundary(field:
|
|
53
|
+
export declare function declaredBoundary(field: Field): Boundary;
|
|
59
54
|
/**
|
|
60
55
|
* The field's EFFECTIVE boundary: declared rules win per direction, the
|
|
61
56
|
* shape-derived default fills the rest. This is the reader every consumer of
|
|
62
57
|
* the axis goes through — `boundaryOf(f).in === 'closed'` is the read-only
|
|
63
58
|
* test, `.out === 'closed'` the write-only one.
|
|
64
59
|
*/
|
|
65
|
-
export declare function boundaryOf(field:
|
|
60
|
+
export declare function boundaryOf(field: Field): Boundary;
|
|
66
61
|
/**
|
|
67
|
-
* A field's effective conversion functions.
|
|
68
|
-
*
|
|
69
|
-
* happens in the readers (validation, encode) BEFORE any conversion.
|
|
70
|
-
*
|
|
71
|
-
* A NAMED codec that no one registered throws, exactly like an unknown alias
|
|
72
|
-
* (`declaredBoundary`). It used to fall back to identity: a frond declared
|
|
73
|
-
* `{ decode: 'celsius' }`, the consumer never called `registerDecoder`, and the
|
|
74
|
-
* value arrived unconverted while the card said it had been converted — the same
|
|
75
|
-
* silent loss this repo measured on Remult (`validate: [null, null]`) and on
|
|
76
|
-
* zod v4 (a computed default frozen at export). One axis, two spellings, one
|
|
77
|
-
* failure mode.
|
|
78
|
-
*
|
|
79
|
-
* It throws where the conversion is needed, not at boot: nothing walks every
|
|
80
|
-
* field at startup. Loud and late beats silent and never.
|
|
62
|
+
* A field's effective conversion functions. A closed direction converts as identity — the
|
|
63
|
+
* permission facet is judged by the façade, not here.
|
|
81
64
|
*/
|
|
82
|
-
export declare function resolveBoundary(field:
|
|
65
|
+
export declare function resolveBoundary(field: Field): {
|
|
83
66
|
decode: Decoder;
|
|
84
67
|
encode: Encoder;
|
|
85
68
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../src/field/boundary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../src/field/boundary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC;;;;;;;;;;GAUG;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;AAI/D,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAA8B;IAC9D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAA8B;IAC9D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAA+B;IAE9D,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,IAAI,CAAiC;IACxF,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,IAAI,CAAiC;IAExF,sFAAsF;IACtF,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAsC;IAElG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAmC;IAEnF,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAuD;IAC5F,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAuD;IAE5F,OAAO,CAAC,MAAM,CAAC,KAAK;CAUrB;AA8BD,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,CASvD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,CAIjD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAMlF"}
|