constructa-core 0.4.0 → 0.5.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.
- package/README.md +2 -0
- package/dist/index.d.ts +25 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +69 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ Use `defineGenerator()` for developer-authored executable implementations. An im
|
|
|
25
25
|
|
|
26
26
|
`RandomSource` exposes `float()` in `[0, 1)`, `integer(maxExclusive)` in `[0, maxExclusive)`, and `bytes(length)` with exactly the requested number of bytes. Use `createRandomSource()` to validate injected adapters or `createDefaultRandomSource()` for the platform-backed source. Invalid source output is a system error; Constructa never substitutes biased fallback randomness. The default source uses platform cryptographic bytes, but this package makes no claim that generated values are suitable for a security-sensitive use case.
|
|
27
27
|
|
|
28
|
+
`createSeededRandom(seed)` provides an isolated deterministic source using `mulberry32` algorithm version 1. String seeds are canonical UTF-8 strings; finite numeric seeds use their JavaScript representation, with `-0` normalized to `0`. `getSeededRandomMetadata()` exposes only the algorithm and version for diagnostics—never the seed. Reproducibility later depends on the complete compatibility tuple: engine version, generator implementation version, random algorithm/version, definition, seed, and execution mode. Changing a generator's draw count is therefore a deliberate determinism compatibility change.
|
|
29
|
+
|
|
28
30
|
## Registry
|
|
29
31
|
|
|
30
32
|
`createRegistry()` is advanced infrastructure. Register trusted implementations explicitly with `register()`. Duplicate type IDs fail without changing registry state; `replace()` is the deliberate replacement path and requires the type to already be registered. `lookup(type, path?)` resolves a registered implementation without a central built-in switch. Unknown IDs throw a dependency `UNKNOWN_GENERATOR` error at the supplied definition path plus `type`, with safe registered-type diagnostics. `snapshot()` returns an immutable, type-sorted registry view with the same lookup behavior; later registry changes do not affect it. Dispatch remains a separate concern.
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,21 @@ type RandomSource = {
|
|
|
10
10
|
bytes(length: number): Uint8Array;
|
|
11
11
|
};
|
|
12
12
|
type RandomSourceAdapter = RandomSource;
|
|
13
|
+
type Seed = number | string;
|
|
14
|
+
declare const SEEDED_RANDOM_ALGORITHM = "mulberry32";
|
|
15
|
+
declare const SEEDED_RANDOM_ALGORITHM_VERSION = 1;
|
|
16
|
+
type SeededRandomMetadata = {
|
|
17
|
+
readonly algorithm: typeof SEEDED_RANDOM_ALGORITHM;
|
|
18
|
+
readonly version: typeof SEEDED_RANDOM_ALGORITHM_VERSION;
|
|
19
|
+
};
|
|
20
|
+
type DeterminismCompatibility = {
|
|
21
|
+
readonly engineVersion: string;
|
|
22
|
+
readonly generatorImplementationVersion: number;
|
|
23
|
+
readonly random: SeededRandomMetadata;
|
|
24
|
+
readonly definition: GeneratorDefinition$1;
|
|
25
|
+
readonly seed: Seed;
|
|
26
|
+
readonly executionMode: string;
|
|
27
|
+
};
|
|
13
28
|
/**
|
|
14
29
|
* Validates an injected source and guards every produced value. No fallback
|
|
15
30
|
* randomness is used when an adapter violates its contract.
|
|
@@ -17,6 +32,15 @@ type RandomSourceAdapter = RandomSource;
|
|
|
17
32
|
declare function createRandomSource(adapter: RandomSourceAdapter): RandomSource;
|
|
18
33
|
/** Creates the default platform-backed random source. It makes no security claim. */
|
|
19
34
|
declare function createDefaultRandomSource(): RandomSource;
|
|
35
|
+
/**
|
|
36
|
+
* Returns a canonical seed representation. Strings use UTF-8 exactly; finite
|
|
37
|
+
* numbers use their JavaScript numeric representation, with -0 normalized to 0.
|
|
38
|
+
*/
|
|
39
|
+
declare function normalizeSeed(seed: Seed): string;
|
|
40
|
+
/** Creates an isolated deterministic random source for the current algorithm version. */
|
|
41
|
+
declare function createSeededRandom(seed: Seed): RandomSource;
|
|
42
|
+
/** Metadata for reproducibility diagnostics. It intentionally contains no seed. */
|
|
43
|
+
declare function getSeededRandomMetadata(): SeededRandomMetadata;
|
|
20
44
|
/** Services supplied by the engine. Implementations must not use global randomness. */
|
|
21
45
|
type GenerationContext = {
|
|
22
46
|
readonly random: RandomSource;
|
|
@@ -70,5 +94,5 @@ declare function invokeGeneratorImplementation<Definition extends GeneratorDefin
|
|
|
70
94
|
readonly path?: ValidationPath$1;
|
|
71
95
|
}): Output;
|
|
72
96
|
//#endregion
|
|
73
|
-
export { GenerationContext, type GeneratorDefinition, GeneratorDependency, GeneratorImplementation, GeneratorRegistry, GeneratorRegistrySnapshot, type Infer, RandomSource, RandomSourceAdapter, RegisteredGenerator, type ValidationIssue, type ValidationPath, createDefaultRandomSource, createGeneratorDefinition, createRandomSource, createRegistry, defineGenerator, invokeGeneratorImplementation };
|
|
97
|
+
export { DeterminismCompatibility, GenerationContext, type GeneratorDefinition, GeneratorDependency, GeneratorImplementation, GeneratorRegistry, GeneratorRegistrySnapshot, type Infer, RandomSource, RandomSourceAdapter, RegisteredGenerator, SEEDED_RANDOM_ALGORITHM, SEEDED_RANDOM_ALGORITHM_VERSION, Seed, SeededRandomMetadata, type ValidationIssue, type ValidationPath, createDefaultRandomSource, createGeneratorDefinition, createRandomSource, createRegistry, createSeededRandom, defineGenerator, getSeededRandomMetadata, invokeGeneratorImplementation, normalizeSeed };
|
|
74
98
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;KAoBY;EACV;EACA,QAAQ;EACR,MAAM,iBAAiB;;KAGb,sBAAsB
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;KAoBY;EACV;EACA,QAAQ;EACR,MAAM,iBAAiB;;KAGb,sBAAsB;KAEtB;cAEC;cACA;KAED;WACD,kBAAkB;WAClB,gBAAgB;;KAGf;WACD;WACA;WACA,QAAQ;WACR,YAAY;WACZ,MAAM;WACN;;;;;;iBAYK,mBAAmB,SAAS,sBAAsB;;iBA0ClD,6BAA6B;;;;;iBAwC7B,cAAc,MAAM;;iBAcpB,mBAAmB,MAAM,OAAO;;iBAuChC,2BAA2B;;KAK/B;WACD,QAAQ;WACR,gBAAgB,QACvB,YAAY,sBAAoB,YAC7B;;KAGK;WACD;WACA,MAAM;;KAGL,wBACV,mBAAmB,sBAAoB,SACvC;WAES;WACA;WACA,qBACP,iCACY;WACL,uBACP,YAAY,wBACA;WACL,WAAW;aACT,YAAY;aACZ,SAAS;QACd;;KAGI;WACD;WACA;;KAGC;WACD,qBAAqB;WACrB,SACP,cACA,OAAO,qBACJ,wBAAwB;;KAGnB;EACV,WAAW,mBAAmB,sBAAoB,SAAS,QACzD,gBAAgB,wBAAwB,YAAY;EAEtD,UAAU,mBAAmB,sBAAoB,SAAS,QACxD,gBAAgB,wBAAwB,YAAY;EAEtD,SACE,cACA,OAAO,qBACJ,wBAAwB;EAC7B,gBAAgB;;;iBAUF,kBAAkB;;;;;iBAoDlB,sBACR,mBAAmB,sBAAoB,SAC7C,QAEA,gBAAgB,wBAAwB,YAAY,UACnD,wBAAwB,YAAY;;iBAMvB,0BACd,cACM,mBAAmB,sBAAoB,SAC7C,YAAY,aAAa;;;;;;iBAUX,8BACd,mBAAmB,sBAAoB,SACvC,QAEA,gBAAgB,wBAAwB,YAAY,SACpD;WACW,YAAY;WACZ,SAAS;WACT,OAAO;IAEjB"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { ConstructaError, assertGeneratorDefinition, normalizeConstructaError } from "constructa-schema";
|
|
2
2
|
//#region src/index.ts
|
|
3
|
+
const SEEDED_RANDOM_ALGORITHM = "mulberry32";
|
|
4
|
+
const SEEDED_RANDOM_ALGORITHM_VERSION = 1;
|
|
5
|
+
const SEEDED_RANDOM_METADATA = Object.freeze({
|
|
6
|
+
algorithm: SEEDED_RANDOM_ALGORITHM,
|
|
7
|
+
version: 1
|
|
8
|
+
});
|
|
3
9
|
/**
|
|
4
10
|
* Validates an injected source and guards every produced value. No fallback
|
|
5
11
|
* randomness is used when an adapter violates its contract.
|
|
@@ -19,7 +25,7 @@ function createRandomSource(adapter) {
|
|
|
19
25
|
return value;
|
|
20
26
|
},
|
|
21
27
|
bytes(length) {
|
|
22
|
-
|
|
28
|
+
assertByteLength(length);
|
|
23
29
|
const value = adapter.bytes(length);
|
|
24
30
|
if (!(value instanceof Uint8Array) || value.length !== length) throw invalidRandomSource("bytes(length) must return a Uint8Array with exactly length bytes.");
|
|
25
31
|
return value;
|
|
@@ -56,6 +62,59 @@ function createDefaultRandomSource() {
|
|
|
56
62
|
bytes: randomBytes
|
|
57
63
|
});
|
|
58
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns a canonical seed representation. Strings use UTF-8 exactly; finite
|
|
67
|
+
* numbers use their JavaScript numeric representation, with -0 normalized to 0.
|
|
68
|
+
*/
|
|
69
|
+
function normalizeSeed(seed) {
|
|
70
|
+
if (typeof seed === "string") return `string:${seed}`;
|
|
71
|
+
if (typeof seed === "number" && Number.isFinite(seed)) return `number:${Object.is(seed, -0) ? "0" : String(seed)}`;
|
|
72
|
+
throw new ConstructaError({
|
|
73
|
+
kind: "configuration",
|
|
74
|
+
code: "INVALID_SEED",
|
|
75
|
+
path: ["seed"],
|
|
76
|
+
message: "seed must be a string or finite number."
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/** Creates an isolated deterministic random source for the current algorithm version. */
|
|
80
|
+
function createSeededRandom(seed) {
|
|
81
|
+
let state = hashSeed(normalizeSeed(seed));
|
|
82
|
+
const uint32 = () => {
|
|
83
|
+
state = state + 1831565813 >>> 0;
|
|
84
|
+
let value = state;
|
|
85
|
+
value = Math.imul(value ^ value >>> 15, value | 1);
|
|
86
|
+
value ^= value + Math.imul(value ^ value >>> 7, value | 61);
|
|
87
|
+
return (value ^ value >>> 14) >>> 0;
|
|
88
|
+
};
|
|
89
|
+
const uint53 = () => (uint32() & 2097151) * 2 ** 32 + uint32();
|
|
90
|
+
return createRandomSource({
|
|
91
|
+
float() {
|
|
92
|
+
return uint53() / 2 ** 53;
|
|
93
|
+
},
|
|
94
|
+
integer(maxExclusive) {
|
|
95
|
+
const range = 2 ** 53;
|
|
96
|
+
const upperLimit = range - range % maxExclusive;
|
|
97
|
+
let value = uint53();
|
|
98
|
+
while (value >= upperLimit) value = uint53();
|
|
99
|
+
return value % maxExclusive;
|
|
100
|
+
},
|
|
101
|
+
bytes(length) {
|
|
102
|
+
const bytes = new Uint8Array(length);
|
|
103
|
+
for (let index = 0; index < length; index += 1) if (index % 4 === 0) {
|
|
104
|
+
const value = uint32();
|
|
105
|
+
bytes[index] = value & 255;
|
|
106
|
+
if (index + 1 < length) bytes[index + 1] = value >>> 8 & 255;
|
|
107
|
+
if (index + 2 < length) bytes[index + 2] = value >>> 16 & 255;
|
|
108
|
+
if (index + 3 < length) bytes[index + 3] = value >>> 24;
|
|
109
|
+
}
|
|
110
|
+
return bytes;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/** Metadata for reproducibility diagnostics. It intentionally contains no seed. */
|
|
115
|
+
function getSeededRandomMetadata() {
|
|
116
|
+
return SEEDED_RANDOM_METADATA;
|
|
117
|
+
}
|
|
59
118
|
const RESERVED_GENERATOR_TYPE_IDS = /* @__PURE__ */ new Set([
|
|
60
119
|
"__proto__",
|
|
61
120
|
"constructor",
|
|
@@ -201,6 +260,9 @@ function assertRandomSourceAdapter(adapter) {
|
|
|
201
260
|
function assertRandomLength(value, name) {
|
|
202
261
|
if (!Number.isSafeInteger(value) || value < 1) throw invalidRandomSource(`${name} must be a positive safe integer.`);
|
|
203
262
|
}
|
|
263
|
+
function assertByteLength(length) {
|
|
264
|
+
if (!Number.isSafeInteger(length) || length < 0) throw invalidRandomSource("length must be a non-negative safe integer.");
|
|
265
|
+
}
|
|
204
266
|
function invalidRandomSource(message) {
|
|
205
267
|
return new ConstructaError({
|
|
206
268
|
kind: "system",
|
|
@@ -209,10 +271,15 @@ function invalidRandomSource(message) {
|
|
|
209
271
|
message
|
|
210
272
|
});
|
|
211
273
|
}
|
|
274
|
+
function hashSeed(seed) {
|
|
275
|
+
let hash = 2166136261;
|
|
276
|
+
for (const byte of new TextEncoder().encode(seed)) hash = Math.imul(hash ^ byte, 16777619) >>> 0;
|
|
277
|
+
return hash;
|
|
278
|
+
}
|
|
212
279
|
function isStableTypeId(value) {
|
|
213
280
|
return /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/u.test(value);
|
|
214
281
|
}
|
|
215
282
|
//#endregion
|
|
216
|
-
export { createDefaultRandomSource, createGeneratorDefinition, createRandomSource, createRegistry, defineGenerator, invokeGeneratorImplementation };
|
|
283
|
+
export { SEEDED_RANDOM_ALGORITHM, SEEDED_RANDOM_ALGORITHM_VERSION, createDefaultRandomSource, createGeneratorDefinition, createRandomSource, createRegistry, createSeededRandom, defineGenerator, getSeededRandomMetadata, invokeGeneratorImplementation, normalizeSeed };
|
|
217
284
|
|
|
218
285
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n assertGeneratorDefinition,\n ConstructaError,\n type GeneratorDefinition,\n normalizeConstructaError,\n type ValidationIssue,\n type ValidationPath,\n} from \"constructa-schema\";\n\nexport type {\n GeneratorDefinition,\n Infer,\n ValidationIssue,\n ValidationPath,\n} from \"constructa-schema\";\n\n/**\n * Random values are always half-open: `float()` returns [0, 1), while\n * `integer(maxExclusive)` returns an integer in [0, maxExclusive).\n */\nexport type RandomSource = {\n float(): number;\n integer(maxExclusive: number): number;\n bytes(length: number): Uint8Array;\n};\n\nexport type RandomSourceAdapter = RandomSource;\n\n/**\n * Validates an injected source and guards every produced value. No fallback\n * randomness is used when an adapter violates its contract.\n */\nexport function createRandomSource(adapter: RandomSourceAdapter): RandomSource {\n assertRandomSourceAdapter(adapter);\n\n return Object.freeze({\n float() {\n const value = adapter.float();\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value < 0 ||\n value >= 1\n ) {\n throw invalidRandomSource(\n \"float() must return a finite number in [0, 1).\",\n );\n }\n return value;\n },\n integer(maxExclusive: number) {\n assertRandomLength(maxExclusive, \"maxExclusive\");\n const value = adapter.integer(maxExclusive);\n if (!Number.isSafeInteger(value) || value < 0 || value >= maxExclusive) {\n throw invalidRandomSource(\n \"integer(maxExclusive) must return a safe integer in [0, maxExclusive).\",\n );\n }\n return value;\n },\n bytes(length: number) {\n assertRandomLength(length, \"length\");\n const value = adapter.bytes(length);\n if (!(value instanceof Uint8Array) || value.length !== length) {\n throw invalidRandomSource(\n \"bytes(length) must return a Uint8Array with exactly length bytes.\",\n );\n }\n return value;\n },\n });\n}\n\n/** Creates the default platform-backed random source. It makes no security claim. */\nexport function createDefaultRandomSource(): RandomSource {\n const crypto = globalThis.crypto;\n if (crypto?.getRandomValues === undefined) {\n throw new ConstructaError({\n kind: \"system\",\n code: \"SYSTEM_RANDOM_UNAVAILABLE\",\n path: [],\n message: \"Platform cryptographic random values are unavailable.\",\n });\n }\n\n const randomBytes = (length: number) => {\n const bytes = new Uint8Array(length);\n for (let offset = 0; offset < length; offset += 65_536) {\n crypto.getRandomValues(bytes.subarray(offset, offset + 65_536));\n }\n return bytes;\n };\n const uint32 = () => new DataView(randomBytes(4).buffer).getUint32(0);\n const uint53 = () => (uint32() & 0x1f_ffff) * 2 ** 32 + uint32();\n\n return createRandomSource({\n float() {\n return uint53() / 2 ** 53;\n },\n integer(maxExclusive: number) {\n const range = 2 ** 53;\n const upperLimit = range - (range % maxExclusive);\n let value = uint53();\n while (value >= upperLimit) value = uint53();\n return value % maxExclusive;\n },\n bytes: randomBytes,\n });\n}\n\n/** Services supplied by the engine. Implementations must not use global randomness. */\nexport type GenerationContext = {\n readonly random: RandomSource;\n readonly generateChild: <Output>(\n definition: GeneratorDefinition<Output>,\n ) => Output;\n};\n\nexport type GeneratorDependency = {\n readonly typeId: string;\n readonly path: ValidationPath;\n};\n\nexport type GeneratorImplementation<\n Definition extends GeneratorDefinition<Output>,\n Output,\n> = {\n readonly type: string;\n readonly version: number;\n readonly validateDefinition: (\n definition: unknown,\n ) => readonly ValidationIssue[];\n readonly analyzeDependencies?: (\n definition: Definition,\n ) => readonly GeneratorDependency[];\n readonly generate: (input: {\n readonly definition: Definition;\n readonly context: GenerationContext;\n }) => Output;\n};\n\nexport type RegisteredGenerator = {\n readonly type: string;\n readonly version: number;\n};\n\nexport type GeneratorRegistrySnapshot = {\n readonly generators: readonly RegisteredGenerator[];\n readonly lookup: (\n type: string,\n path?: ValidationPath,\n ) => GeneratorImplementation<GeneratorDefinition, unknown>;\n};\n\nexport type GeneratorRegistry = {\n register: <Definition extends GeneratorDefinition<Output>, Output>(\n implementation: GeneratorImplementation<Definition, Output>,\n ) => void;\n replace: <Definition extends GeneratorDefinition<Output>, Output>(\n implementation: GeneratorImplementation<Definition, Output>,\n ) => void;\n lookup: (\n type: string,\n path?: ValidationPath,\n ) => GeneratorImplementation<GeneratorDefinition, unknown>;\n snapshot: () => GeneratorRegistrySnapshot;\n};\n\nconst RESERVED_GENERATOR_TYPE_IDS = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\",\n]);\n\n/** Creates advanced registry infrastructure. Normal factories do not require it. */\nexport function createRegistry(): GeneratorRegistry {\n const implementations = new Map<\n string,\n GeneratorImplementation<GeneratorDefinition, unknown>\n >();\n\n return {\n register(implementation) {\n assertRegistryImplementation(implementation);\n if (implementations.has(implementation.type)) {\n throw registryError(\n \"DUPLICATE_GENERATOR\",\n [\"type\"],\n `A generator with type \"${implementation.type}\" is already registered.`,\n );\n }\n implementations.set(\n implementation.type,\n freezeImplementation(\n implementation,\n ) as unknown as GeneratorImplementation<GeneratorDefinition, unknown>,\n );\n },\n replace(implementation) {\n assertRegistryImplementation(implementation);\n if (!implementations.has(implementation.type)) {\n throw registryError(\n \"UNKNOWN_GENERATOR\",\n [\"type\"],\n `No generator with type \"${implementation.type}\" is registered.`,\n );\n }\n implementations.set(\n implementation.type,\n freezeImplementation(\n implementation,\n ) as unknown as GeneratorImplementation<GeneratorDefinition, unknown>,\n );\n },\n lookup(type, path = []) {\n return lookupImplementation(implementations, type, path);\n },\n snapshot() {\n return createRegistrySnapshot(implementations);\n },\n };\n}\n\n/**\n * Defines a trusted, developer-authored generator implementation. This API has\n * no dependency on a particular validation library.\n */\nexport function defineGenerator<\n const Definition extends GeneratorDefinition<Output>,\n Output,\n>(\n implementation: GeneratorImplementation<Definition, Output>,\n): GeneratorImplementation<Definition, Output> {\n assertGeneratorImplementation(implementation);\n return implementation;\n}\n\n/** Builds a portable definition while preserving literal fields and output inference. */\nexport function createGeneratorDefinition<\n Output,\n const Definition extends GeneratorDefinition<Output>,\n>(definition: Definition): Definition {\n assertGeneratorDefinition(definition);\n return definition;\n}\n\n/**\n * Invokes one validated implementation. Registry lookup and dispatch are added\n * later; this function keeps validation and execution failure normalization in\n * the same shared contract today.\n */\nexport function invokeGeneratorImplementation<\n Definition extends GeneratorDefinition<Output>,\n Output,\n>(\n implementation: GeneratorImplementation<Definition, Output>,\n input: {\n readonly definition: Definition;\n readonly context: GenerationContext;\n readonly path?: ValidationPath;\n },\n): Output {\n const path = input.path ?? [];\n let issues: readonly ValidationIssue[];\n\n try {\n issues = implementation.validateDefinition(input.definition);\n } catch (cause) {\n throw normalizeConstructaError(cause, {\n kind: \"configuration\",\n code: \"INVALID_CONFIGURATION\",\n path,\n message: \"Generator definition validation failed.\",\n });\n }\n\n if (issues.length > 0) {\n const [issue] = issues;\n if (issue !== undefined) {\n throw new ConstructaError({\n kind: \"configuration\",\n code: \"INVALID_CONFIGURATION\",\n path: [...path, ...issue.path],\n message: issue.message,\n details: { issueCode: issue.code },\n });\n }\n\n throw new ConstructaError({\n kind: \"system\",\n code: \"EXECUTION_FAILED\",\n path,\n message: \"Generator validation returned an invalid result.\",\n });\n }\n\n try {\n return implementation.generate({\n definition: input.definition,\n context: input.context,\n });\n } catch (cause) {\n throw normalizeConstructaError(cause, {\n kind: \"execution\",\n code: \"EXECUTION_FAILED\",\n path,\n message: \"Generator execution failed.\",\n });\n }\n}\n\nfunction assertGeneratorImplementation(implementation: {\n readonly type: string;\n readonly version: number;\n readonly validateDefinition: unknown;\n readonly analyzeDependencies?: unknown;\n readonly generate: unknown;\n}): void {\n if (!isStableTypeId(implementation.type)) {\n throw new TypeError(\"generator type must be a stable lowercase identifier\");\n }\n if (\n !Number.isSafeInteger(implementation.version) ||\n implementation.version < 1\n ) {\n throw new TypeError(\"generator version must be a positive safe integer\");\n }\n if (typeof implementation.validateDefinition !== \"function\") {\n throw new TypeError(\"validateDefinition must be a function\");\n }\n if (typeof implementation.generate !== \"function\") {\n throw new TypeError(\"generate must be a function\");\n }\n if (\n implementation.analyzeDependencies !== undefined &&\n typeof implementation.analyzeDependencies !== \"function\"\n ) {\n throw new TypeError(\"analyzeDependencies must be a function when present\");\n }\n}\n\nfunction assertRegistryImplementation(implementation: {\n readonly type: string;\n readonly version: number;\n readonly validateDefinition: unknown;\n readonly analyzeDependencies?: unknown;\n readonly generate: unknown;\n}): void {\n try {\n assertGeneratorImplementation(implementation);\n } catch {\n throw registryError(\n \"INVALID_CONFIGURATION\",\n [\"implementation\"],\n \"Generator implementation is invalid.\",\n );\n }\n if (RESERVED_GENERATOR_TYPE_IDS.has(implementation.type)) {\n throw registryError(\n \"INVALID_CONFIGURATION\",\n [\"type\"],\n `Generator type \"${implementation.type}\" is reserved.`,\n );\n }\n}\n\nfunction freezeImplementation<\n Definition extends GeneratorDefinition<Output>,\n Output,\n>(\n implementation: GeneratorImplementation<Definition, Output>,\n): GeneratorImplementation<Definition, Output> {\n return Object.freeze({ ...implementation });\n}\n\nfunction createRegistrySnapshot(\n implementations: ReadonlyMap<\n string,\n GeneratorImplementation<GeneratorDefinition, unknown>\n >,\n): GeneratorRegistrySnapshot {\n const snapshotImplementations = new Map(implementations);\n const generators = [...snapshotImplementations.values()]\n .map(({ type, version }) => Object.freeze({ type, version }))\n .sort((left, right) => left.type.localeCompare(right.type));\n\n return Object.freeze({\n generators: Object.freeze(generators),\n lookup(type: string, path: ValidationPath = []) {\n return lookupImplementation(snapshotImplementations, type, path);\n },\n });\n}\n\nfunction lookupImplementation(\n implementations: ReadonlyMap<\n string,\n GeneratorImplementation<GeneratorDefinition, unknown>\n >,\n type: string,\n path: ValidationPath,\n): GeneratorImplementation<GeneratorDefinition, unknown> {\n const implementation = implementations.get(type);\n if (implementation !== undefined) return implementation;\n\n const registeredTypes = [...implementations.keys()].sort((left, right) =>\n left.localeCompare(right),\n );\n throw new ConstructaError({\n kind: \"dependency\",\n code: \"UNKNOWN_GENERATOR\",\n path: [...path, \"type\"],\n message: `No generator with type \"${type}\" is registered.`,\n details: { registeredTypes },\n });\n}\n\nfunction registryError(\n code: \"DUPLICATE_GENERATOR\" | \"INVALID_CONFIGURATION\" | \"UNKNOWN_GENERATOR\",\n path: ValidationPath,\n message: string,\n): ConstructaError {\n return new ConstructaError({ kind: \"configuration\", code, path, message });\n}\n\nfunction assertRandomSourceAdapter(adapter: RandomSourceAdapter): void {\n if (\n typeof adapter !== \"object\" ||\n adapter === null ||\n typeof adapter.float !== \"function\" ||\n typeof adapter.integer !== \"function\" ||\n typeof adapter.bytes !== \"function\"\n ) {\n throw invalidRandomSource(\n \"A random source must provide float(), integer(), and bytes() methods.\",\n );\n }\n}\n\nfunction assertRandomLength(value: number, name: string): void {\n if (!Number.isSafeInteger(value) || value < 1) {\n throw invalidRandomSource(`${name} must be a positive safe integer.`);\n }\n}\n\nfunction invalidRandomSource(message: string): ConstructaError {\n return new ConstructaError({\n kind: \"system\",\n code: \"INVALID_RANDOM_SOURCE\",\n path: [\"random\"],\n message,\n });\n}\n\nfunction isStableTypeId(value: string): boolean {\n return /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/u.test(value);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n assertGeneratorDefinition,\n ConstructaError,\n type GeneratorDefinition,\n normalizeConstructaError,\n type ValidationIssue,\n type ValidationPath,\n} from \"constructa-schema\";\n\nexport type {\n GeneratorDefinition,\n Infer,\n ValidationIssue,\n ValidationPath,\n} from \"constructa-schema\";\n\n/**\n * Random values are always half-open: `float()` returns [0, 1), while\n * `integer(maxExclusive)` returns an integer in [0, maxExclusive).\n */\nexport type RandomSource = {\n float(): number;\n integer(maxExclusive: number): number;\n bytes(length: number): Uint8Array;\n};\n\nexport type RandomSourceAdapter = RandomSource;\n\nexport type Seed = number | string;\n\nexport const SEEDED_RANDOM_ALGORITHM = \"mulberry32\";\nexport const SEEDED_RANDOM_ALGORITHM_VERSION = 1;\n\nexport type SeededRandomMetadata = {\n readonly algorithm: typeof SEEDED_RANDOM_ALGORITHM;\n readonly version: typeof SEEDED_RANDOM_ALGORITHM_VERSION;\n};\n\nexport type DeterminismCompatibility = {\n readonly engineVersion: string;\n readonly generatorImplementationVersion: number;\n readonly random: SeededRandomMetadata;\n readonly definition: GeneratorDefinition;\n readonly seed: Seed;\n readonly executionMode: string;\n};\n\nconst SEEDED_RANDOM_METADATA: SeededRandomMetadata = Object.freeze({\n algorithm: SEEDED_RANDOM_ALGORITHM,\n version: SEEDED_RANDOM_ALGORITHM_VERSION,\n});\n\n/**\n * Validates an injected source and guards every produced value. No fallback\n * randomness is used when an adapter violates its contract.\n */\nexport function createRandomSource(adapter: RandomSourceAdapter): RandomSource {\n assertRandomSourceAdapter(adapter);\n\n return Object.freeze({\n float() {\n const value = adapter.float();\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value < 0 ||\n value >= 1\n ) {\n throw invalidRandomSource(\n \"float() must return a finite number in [0, 1).\",\n );\n }\n return value;\n },\n integer(maxExclusive: number) {\n assertRandomLength(maxExclusive, \"maxExclusive\");\n const value = adapter.integer(maxExclusive);\n if (!Number.isSafeInteger(value) || value < 0 || value >= maxExclusive) {\n throw invalidRandomSource(\n \"integer(maxExclusive) must return a safe integer in [0, maxExclusive).\",\n );\n }\n return value;\n },\n bytes(length: number) {\n assertByteLength(length);\n const value = adapter.bytes(length);\n if (!(value instanceof Uint8Array) || value.length !== length) {\n throw invalidRandomSource(\n \"bytes(length) must return a Uint8Array with exactly length bytes.\",\n );\n }\n return value;\n },\n });\n}\n\n/** Creates the default platform-backed random source. It makes no security claim. */\nexport function createDefaultRandomSource(): RandomSource {\n const crypto = globalThis.crypto;\n if (crypto?.getRandomValues === undefined) {\n throw new ConstructaError({\n kind: \"system\",\n code: \"SYSTEM_RANDOM_UNAVAILABLE\",\n path: [],\n message: \"Platform cryptographic random values are unavailable.\",\n });\n }\n\n const randomBytes = (length: number) => {\n const bytes = new Uint8Array(length);\n for (let offset = 0; offset < length; offset += 65_536) {\n crypto.getRandomValues(bytes.subarray(offset, offset + 65_536));\n }\n return bytes;\n };\n const uint32 = () => new DataView(randomBytes(4).buffer).getUint32(0);\n const uint53 = () => (uint32() & 0x1f_ffff) * 2 ** 32 + uint32();\n\n return createRandomSource({\n float() {\n return uint53() / 2 ** 53;\n },\n integer(maxExclusive: number) {\n const range = 2 ** 53;\n const upperLimit = range - (range % maxExclusive);\n let value = uint53();\n while (value >= upperLimit) value = uint53();\n return value % maxExclusive;\n },\n bytes: randomBytes,\n });\n}\n\n/**\n * Returns a canonical seed representation. Strings use UTF-8 exactly; finite\n * numbers use their JavaScript numeric representation, with -0 normalized to 0.\n */\nexport function normalizeSeed(seed: Seed): string {\n if (typeof seed === \"string\") return `string:${seed}`;\n if (typeof seed === \"number\" && Number.isFinite(seed)) {\n return `number:${Object.is(seed, -0) ? \"0\" : String(seed)}`;\n }\n throw new ConstructaError({\n kind: \"configuration\",\n code: \"INVALID_SEED\",\n path: [\"seed\"],\n message: \"seed must be a string or finite number.\",\n });\n}\n\n/** Creates an isolated deterministic random source for the current algorithm version. */\nexport function createSeededRandom(seed: Seed): RandomSource {\n let state = hashSeed(normalizeSeed(seed));\n const uint32 = () => {\n state = (state + 0x6d2b_79f5) >>> 0;\n let value = state;\n value = Math.imul(value ^ (value >>> 15), value | 1);\n value ^= value + Math.imul(value ^ (value >>> 7), value | 61);\n return (value ^ (value >>> 14)) >>> 0;\n };\n const uint53 = () => (uint32() & 0x1f_ffff) * 2 ** 32 + uint32();\n\n return createRandomSource({\n float() {\n return uint53() / 2 ** 53;\n },\n integer(maxExclusive: number) {\n const range = 2 ** 53;\n const upperLimit = range - (range % maxExclusive);\n let value = uint53();\n while (value >= upperLimit) value = uint53();\n return value % maxExclusive;\n },\n bytes(length: number) {\n const bytes = new Uint8Array(length);\n for (let index = 0; index < length; index += 1) {\n if (index % 4 === 0) {\n const value = uint32();\n bytes[index] = value & 0xff;\n if (index + 1 < length) bytes[index + 1] = (value >>> 8) & 0xff;\n if (index + 2 < length) bytes[index + 2] = (value >>> 16) & 0xff;\n if (index + 3 < length) bytes[index + 3] = value >>> 24;\n }\n }\n return bytes;\n },\n });\n}\n\n/** Metadata for reproducibility diagnostics. It intentionally contains no seed. */\nexport function getSeededRandomMetadata(): SeededRandomMetadata {\n return SEEDED_RANDOM_METADATA;\n}\n\n/** Services supplied by the engine. Implementations must not use global randomness. */\nexport type GenerationContext = {\n readonly random: RandomSource;\n readonly generateChild: <Output>(\n definition: GeneratorDefinition<Output>,\n ) => Output;\n};\n\nexport type GeneratorDependency = {\n readonly typeId: string;\n readonly path: ValidationPath;\n};\n\nexport type GeneratorImplementation<\n Definition extends GeneratorDefinition<Output>,\n Output,\n> = {\n readonly type: string;\n readonly version: number;\n readonly validateDefinition: (\n definition: unknown,\n ) => readonly ValidationIssue[];\n readonly analyzeDependencies?: (\n definition: Definition,\n ) => readonly GeneratorDependency[];\n readonly generate: (input: {\n readonly definition: Definition;\n readonly context: GenerationContext;\n }) => Output;\n};\n\nexport type RegisteredGenerator = {\n readonly type: string;\n readonly version: number;\n};\n\nexport type GeneratorRegistrySnapshot = {\n readonly generators: readonly RegisteredGenerator[];\n readonly lookup: (\n type: string,\n path?: ValidationPath,\n ) => GeneratorImplementation<GeneratorDefinition, unknown>;\n};\n\nexport type GeneratorRegistry = {\n register: <Definition extends GeneratorDefinition<Output>, Output>(\n implementation: GeneratorImplementation<Definition, Output>,\n ) => void;\n replace: <Definition extends GeneratorDefinition<Output>, Output>(\n implementation: GeneratorImplementation<Definition, Output>,\n ) => void;\n lookup: (\n type: string,\n path?: ValidationPath,\n ) => GeneratorImplementation<GeneratorDefinition, unknown>;\n snapshot: () => GeneratorRegistrySnapshot;\n};\n\nconst RESERVED_GENERATOR_TYPE_IDS = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\",\n]);\n\n/** Creates advanced registry infrastructure. Normal factories do not require it. */\nexport function createRegistry(): GeneratorRegistry {\n const implementations = new Map<\n string,\n GeneratorImplementation<GeneratorDefinition, unknown>\n >();\n\n return {\n register(implementation) {\n assertRegistryImplementation(implementation);\n if (implementations.has(implementation.type)) {\n throw registryError(\n \"DUPLICATE_GENERATOR\",\n [\"type\"],\n `A generator with type \"${implementation.type}\" is already registered.`,\n );\n }\n implementations.set(\n implementation.type,\n freezeImplementation(\n implementation,\n ) as unknown as GeneratorImplementation<GeneratorDefinition, unknown>,\n );\n },\n replace(implementation) {\n assertRegistryImplementation(implementation);\n if (!implementations.has(implementation.type)) {\n throw registryError(\n \"UNKNOWN_GENERATOR\",\n [\"type\"],\n `No generator with type \"${implementation.type}\" is registered.`,\n );\n }\n implementations.set(\n implementation.type,\n freezeImplementation(\n implementation,\n ) as unknown as GeneratorImplementation<GeneratorDefinition, unknown>,\n );\n },\n lookup(type, path = []) {\n return lookupImplementation(implementations, type, path);\n },\n snapshot() {\n return createRegistrySnapshot(implementations);\n },\n };\n}\n\n/**\n * Defines a trusted, developer-authored generator implementation. This API has\n * no dependency on a particular validation library.\n */\nexport function defineGenerator<\n const Definition extends GeneratorDefinition<Output>,\n Output,\n>(\n implementation: GeneratorImplementation<Definition, Output>,\n): GeneratorImplementation<Definition, Output> {\n assertGeneratorImplementation(implementation);\n return implementation;\n}\n\n/** Builds a portable definition while preserving literal fields and output inference. */\nexport function createGeneratorDefinition<\n Output,\n const Definition extends GeneratorDefinition<Output>,\n>(definition: Definition): Definition {\n assertGeneratorDefinition(definition);\n return definition;\n}\n\n/**\n * Invokes one validated implementation. Registry lookup and dispatch are added\n * later; this function keeps validation and execution failure normalization in\n * the same shared contract today.\n */\nexport function invokeGeneratorImplementation<\n Definition extends GeneratorDefinition<Output>,\n Output,\n>(\n implementation: GeneratorImplementation<Definition, Output>,\n input: {\n readonly definition: Definition;\n readonly context: GenerationContext;\n readonly path?: ValidationPath;\n },\n): Output {\n const path = input.path ?? [];\n let issues: readonly ValidationIssue[];\n\n try {\n issues = implementation.validateDefinition(input.definition);\n } catch (cause) {\n throw normalizeConstructaError(cause, {\n kind: \"configuration\",\n code: \"INVALID_CONFIGURATION\",\n path,\n message: \"Generator definition validation failed.\",\n });\n }\n\n if (issues.length > 0) {\n const [issue] = issues;\n if (issue !== undefined) {\n throw new ConstructaError({\n kind: \"configuration\",\n code: \"INVALID_CONFIGURATION\",\n path: [...path, ...issue.path],\n message: issue.message,\n details: { issueCode: issue.code },\n });\n }\n\n throw new ConstructaError({\n kind: \"system\",\n code: \"EXECUTION_FAILED\",\n path,\n message: \"Generator validation returned an invalid result.\",\n });\n }\n\n try {\n return implementation.generate({\n definition: input.definition,\n context: input.context,\n });\n } catch (cause) {\n throw normalizeConstructaError(cause, {\n kind: \"execution\",\n code: \"EXECUTION_FAILED\",\n path,\n message: \"Generator execution failed.\",\n });\n }\n}\n\nfunction assertGeneratorImplementation(implementation: {\n readonly type: string;\n readonly version: number;\n readonly validateDefinition: unknown;\n readonly analyzeDependencies?: unknown;\n readonly generate: unknown;\n}): void {\n if (!isStableTypeId(implementation.type)) {\n throw new TypeError(\"generator type must be a stable lowercase identifier\");\n }\n if (\n !Number.isSafeInteger(implementation.version) ||\n implementation.version < 1\n ) {\n throw new TypeError(\"generator version must be a positive safe integer\");\n }\n if (typeof implementation.validateDefinition !== \"function\") {\n throw new TypeError(\"validateDefinition must be a function\");\n }\n if (typeof implementation.generate !== \"function\") {\n throw new TypeError(\"generate must be a function\");\n }\n if (\n implementation.analyzeDependencies !== undefined &&\n typeof implementation.analyzeDependencies !== \"function\"\n ) {\n throw new TypeError(\"analyzeDependencies must be a function when present\");\n }\n}\n\nfunction assertRegistryImplementation(implementation: {\n readonly type: string;\n readonly version: number;\n readonly validateDefinition: unknown;\n readonly analyzeDependencies?: unknown;\n readonly generate: unknown;\n}): void {\n try {\n assertGeneratorImplementation(implementation);\n } catch {\n throw registryError(\n \"INVALID_CONFIGURATION\",\n [\"implementation\"],\n \"Generator implementation is invalid.\",\n );\n }\n if (RESERVED_GENERATOR_TYPE_IDS.has(implementation.type)) {\n throw registryError(\n \"INVALID_CONFIGURATION\",\n [\"type\"],\n `Generator type \"${implementation.type}\" is reserved.`,\n );\n }\n}\n\nfunction freezeImplementation<\n Definition extends GeneratorDefinition<Output>,\n Output,\n>(\n implementation: GeneratorImplementation<Definition, Output>,\n): GeneratorImplementation<Definition, Output> {\n return Object.freeze({ ...implementation });\n}\n\nfunction createRegistrySnapshot(\n implementations: ReadonlyMap<\n string,\n GeneratorImplementation<GeneratorDefinition, unknown>\n >,\n): GeneratorRegistrySnapshot {\n const snapshotImplementations = new Map(implementations);\n const generators = [...snapshotImplementations.values()]\n .map(({ type, version }) => Object.freeze({ type, version }))\n .sort((left, right) => left.type.localeCompare(right.type));\n\n return Object.freeze({\n generators: Object.freeze(generators),\n lookup(type: string, path: ValidationPath = []) {\n return lookupImplementation(snapshotImplementations, type, path);\n },\n });\n}\n\nfunction lookupImplementation(\n implementations: ReadonlyMap<\n string,\n GeneratorImplementation<GeneratorDefinition, unknown>\n >,\n type: string,\n path: ValidationPath,\n): GeneratorImplementation<GeneratorDefinition, unknown> {\n const implementation = implementations.get(type);\n if (implementation !== undefined) return implementation;\n\n const registeredTypes = [...implementations.keys()].sort((left, right) =>\n left.localeCompare(right),\n );\n throw new ConstructaError({\n kind: \"dependency\",\n code: \"UNKNOWN_GENERATOR\",\n path: [...path, \"type\"],\n message: `No generator with type \"${type}\" is registered.`,\n details: { registeredTypes },\n });\n}\n\nfunction registryError(\n code: \"DUPLICATE_GENERATOR\" | \"INVALID_CONFIGURATION\" | \"UNKNOWN_GENERATOR\",\n path: ValidationPath,\n message: string,\n): ConstructaError {\n return new ConstructaError({ kind: \"configuration\", code, path, message });\n}\n\nfunction assertRandomSourceAdapter(adapter: RandomSourceAdapter): void {\n if (\n typeof adapter !== \"object\" ||\n adapter === null ||\n typeof adapter.float !== \"function\" ||\n typeof adapter.integer !== \"function\" ||\n typeof adapter.bytes !== \"function\"\n ) {\n throw invalidRandomSource(\n \"A random source must provide float(), integer(), and bytes() methods.\",\n );\n }\n}\n\nfunction assertRandomLength(value: number, name: string): void {\n if (!Number.isSafeInteger(value) || value < 1) {\n throw invalidRandomSource(`${name} must be a positive safe integer.`);\n }\n}\n\nfunction assertByteLength(length: number): void {\n if (!Number.isSafeInteger(length) || length < 0) {\n throw invalidRandomSource(\"length must be a non-negative safe integer.\");\n }\n}\n\nfunction invalidRandomSource(message: string): ConstructaError {\n return new ConstructaError({\n kind: \"system\",\n code: \"INVALID_RANDOM_SOURCE\",\n path: [\"random\"],\n message,\n });\n}\n\nfunction hashSeed(seed: string): number {\n let hash = 0x811c_9dc5;\n for (const byte of new TextEncoder().encode(seed)) {\n hash = Math.imul(hash ^ byte, 0x0100_0193) >>> 0;\n }\n return hash;\n}\n\nfunction isStableTypeId(value: string): boolean {\n return /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/u.test(value);\n}\n"],"mappings":"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"}
|