toolcraft 0.0.100 → 0.0.102

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 (36) hide show
  1. package/composition.json +6 -1
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +85 -18
  4. package/dist/composition.json +6 -1
  5. package/node_modules/toolcraft-design/dist/explorer/jobs.d.ts +1 -0
  6. package/node_modules/toolcraft-design/dist/explorer/jobs.js +28 -0
  7. package/node_modules/toolcraft-design/dist/explorer/render/detail.js +1 -1
  8. package/node_modules/toolcraft-design/dist/explorer/render/list.js +1 -1
  9. package/node_modules/toolcraft-schema/LICENSE +21 -0
  10. package/node_modules/toolcraft-schema/README.md +89 -0
  11. package/node_modules/toolcraft-schema/dist/index.compile-check.d.ts +1 -0
  12. package/node_modules/toolcraft-schema/dist/index.compile-check.js +17 -0
  13. package/node_modules/toolcraft-schema/dist/index.d.ts +182 -0
  14. package/node_modules/toolcraft-schema/dist/index.js +294 -0
  15. package/node_modules/toolcraft-schema/dist/json-schema-document.d.ts +14 -0
  16. package/node_modules/toolcraft-schema/dist/json-schema-document.js +17 -0
  17. package/node_modules/toolcraft-schema/dist/json.compile-check.d.ts +1 -0
  18. package/node_modules/toolcraft-schema/dist/json.compile-check.js +2 -0
  19. package/node_modules/toolcraft-schema/dist/json.d.ts +10 -0
  20. package/node_modules/toolcraft-schema/dist/json.js +5 -0
  21. package/node_modules/toolcraft-schema/dist/oneof.compile-check.d.ts +1 -0
  22. package/node_modules/toolcraft-schema/dist/oneof.compile-check.js +12 -0
  23. package/node_modules/toolcraft-schema/dist/oneof.d.ts +15 -0
  24. package/node_modules/toolcraft-schema/dist/oneof.js +18 -0
  25. package/node_modules/toolcraft-schema/dist/record.compile-check.d.ts +1 -0
  26. package/node_modules/toolcraft-schema/dist/record.compile-check.js +2 -0
  27. package/node_modules/toolcraft-schema/dist/record.d.ts +5 -0
  28. package/node_modules/toolcraft-schema/dist/record.js +6 -0
  29. package/node_modules/toolcraft-schema/dist/union.compile-check.d.ts +1 -0
  30. package/node_modules/toolcraft-schema/dist/union.compile-check.js +9 -0
  31. package/node_modules/toolcraft-schema/dist/union.d.ts +8 -0
  32. package/node_modules/toolcraft-schema/dist/union.js +45 -0
  33. package/node_modules/toolcraft-schema/dist/validate.d.ts +16 -0
  34. package/node_modules/toolcraft-schema/dist/validate.js +379 -0
  35. package/node_modules/toolcraft-schema/package.json +32 -0
  36. package/package.json +5 -4
@@ -0,0 +1,294 @@
1
+ import { Json } from "./json.js";
2
+ import { createJsonSchemaDocument } from "./json-schema-document.js";
3
+ import { OneOf } from "./oneof.js";
4
+ import { Record as RecordBuilder } from "./record.js";
5
+ import { Union } from "./union.js";
6
+ import { validate } from "./validate.js";
7
+ function withMetadata(schema, jsonSchema) {
8
+ if (schema.description !== undefined) {
9
+ jsonSchema.description = schema.description;
10
+ }
11
+ if (schema.default !== undefined) {
12
+ jsonSchema.default = schema.default;
13
+ }
14
+ if (schema.nullable === true) {
15
+ jsonSchema.nullable = true;
16
+ }
17
+ return jsonSchema;
18
+ }
19
+ function withStringMetadata(schema, jsonSchema) {
20
+ if (schema.minLength !== undefined) {
21
+ jsonSchema.minLength = schema.minLength;
22
+ }
23
+ if (schema.maxLength !== undefined) {
24
+ jsonSchema.maxLength = schema.maxLength;
25
+ }
26
+ if (schema.pattern !== undefined) {
27
+ jsonSchema.pattern = schema.pattern;
28
+ }
29
+ if (schema.format !== undefined) {
30
+ jsonSchema.format = schema.format;
31
+ }
32
+ return withMetadata(schema, jsonSchema);
33
+ }
34
+ function withNumberMetadata(schema, jsonSchema) {
35
+ if (schema.minimum !== undefined) {
36
+ jsonSchema.minimum = schema.minimum;
37
+ }
38
+ if (schema.maximum !== undefined) {
39
+ jsonSchema.maximum = schema.maximum;
40
+ }
41
+ return withMetadata(schema, jsonSchema);
42
+ }
43
+ function withArrayMetadata(schema, jsonSchema) {
44
+ if (schema.minItems !== undefined) {
45
+ jsonSchema.minItems = schema.minItems;
46
+ }
47
+ if (schema.maxItems !== undefined) {
48
+ jsonSchema.maxItems = schema.maxItems;
49
+ }
50
+ return withMetadata(schema, jsonSchema);
51
+ }
52
+ function withObjectMetadata(schema, jsonSchema) {
53
+ jsonSchema.additionalProperties = schema.additionalProperties ?? false;
54
+ return withMetadata(schema, jsonSchema);
55
+ }
56
+ function getEnumJsonType(values) {
57
+ const [firstValue] = values;
58
+ if (firstValue === undefined) {
59
+ return undefined;
60
+ }
61
+ const firstType = typeof firstValue;
62
+ const isSinglePrimitiveType = values.every((value) => typeof value === firstType);
63
+ if (!isSinglePrimitiveType) {
64
+ return undefined;
65
+ }
66
+ if (firstType === "string" || firstType === "number" || firstType === "boolean") {
67
+ return firstType;
68
+ }
69
+ return undefined;
70
+ }
71
+ function isOptionalSchema(schema) {
72
+ return schema.kind === "optional";
73
+ }
74
+ function assertValidEnumValues(values) {
75
+ if (values.length === 0) {
76
+ throw new Error("Enum schema requires at least one value");
77
+ }
78
+ const uniqueValues = new Set(values);
79
+ if (uniqueValues.size !== values.length) {
80
+ throw new Error("Enum schema values must be unique");
81
+ }
82
+ if (values.some((value) => typeof value === "number" && !Number.isFinite(value))) {
83
+ throw new Error("Enum schema numeric values must be finite");
84
+ }
85
+ }
86
+ function assertNonNegativeInteger(value, name) {
87
+ if (value !== undefined && (!Number.isInteger(value) || value < 0)) {
88
+ throw new Error(`${name} must be a non-negative integer`);
89
+ }
90
+ }
91
+ function assertFiniteNumber(value, name) {
92
+ if (value !== undefined && !Number.isFinite(value)) {
93
+ throw new Error(`${name} must be finite`);
94
+ }
95
+ }
96
+ function assertMinMaxOrder(minimum, maximum, minimumName, maximumName) {
97
+ if (minimum !== undefined && maximum !== undefined && minimum > maximum) {
98
+ throw new Error(`${minimumName} must be less than or equal to ${maximumName}`);
99
+ }
100
+ }
101
+ function assertValidDefault(schema) {
102
+ if (schema.default === undefined) {
103
+ return;
104
+ }
105
+ const result = validate(schema, schema.default);
106
+ if (!result.ok) {
107
+ throw new Error(`default must satisfy schema: ${result.issues[0]?.message ?? "invalid default"}`);
108
+ }
109
+ }
110
+ function assertPattern(pattern) {
111
+ if (pattern === undefined) {
112
+ return;
113
+ }
114
+ try {
115
+ new RegExp(pattern);
116
+ }
117
+ catch {
118
+ throw new Error("pattern must be a valid regular expression");
119
+ }
120
+ }
121
+ function unwrapOptional(schema) {
122
+ if (isOptionalSchema(schema)) {
123
+ return unwrapOptional(schema.inner);
124
+ }
125
+ return schema;
126
+ }
127
+ function withInjectedDiscriminator(schema, discriminator, branchName) {
128
+ const branchJsonSchema = toJsonSchema(schema);
129
+ const properties = {
130
+ ...(branchJsonSchema.properties ?? {}),
131
+ [discriminator]: {
132
+ type: "string",
133
+ enum: [branchName]
134
+ }
135
+ };
136
+ const required = [...new Set([...(branchJsonSchema.required ?? []), discriminator])];
137
+ return {
138
+ ...branchJsonSchema,
139
+ type: "object",
140
+ properties,
141
+ required
142
+ };
143
+ }
144
+ export const S = {
145
+ String(options = {}) {
146
+ assertNonNegativeInteger(options.minLength, "minLength");
147
+ assertNonNegativeInteger(options.maxLength, "maxLength");
148
+ assertMinMaxOrder(options.minLength, options.maxLength, "minLength", "maxLength");
149
+ assertPattern(options.pattern);
150
+ const schema = {
151
+ kind: "string",
152
+ ...options
153
+ };
154
+ assertValidDefault(schema);
155
+ return schema;
156
+ },
157
+ Number(options = {}) {
158
+ assertFiniteNumber(options.minimum, "minimum");
159
+ assertFiniteNumber(options.maximum, "maximum");
160
+ assertMinMaxOrder(options.minimum, options.maximum, "minimum", "maximum");
161
+ assertFiniteNumber(options.default, "default");
162
+ if (options.jsonType === "integer" &&
163
+ options.default !== undefined &&
164
+ !Number.isInteger(options.default)) {
165
+ throw new Error("default must be an integer");
166
+ }
167
+ const schema = {
168
+ kind: "number",
169
+ ...options
170
+ };
171
+ assertValidDefault(schema);
172
+ return schema;
173
+ },
174
+ Boolean(options = {}) {
175
+ const schema = {
176
+ kind: "boolean",
177
+ ...options
178
+ };
179
+ assertValidDefault(schema);
180
+ return schema;
181
+ },
182
+ Enum(values, options = {}) {
183
+ assertValidEnumValues(values);
184
+ if (options.jsonType === "integer" &&
185
+ values.some((value) => typeof value !== "number" || !Number.isInteger(value))) {
186
+ throw new Error("Integer enum values must be integers");
187
+ }
188
+ const schema = {
189
+ kind: "enum",
190
+ values,
191
+ ...options
192
+ };
193
+ assertValidDefault(schema);
194
+ return schema;
195
+ },
196
+ Array(item, options = {}) {
197
+ assertNonNegativeInteger(options.minItems, "minItems");
198
+ assertNonNegativeInteger(options.maxItems, "maxItems");
199
+ assertMinMaxOrder(options.minItems, options.maxItems, "minItems", "maxItems");
200
+ const schema = {
201
+ kind: "array",
202
+ item,
203
+ ...options
204
+ };
205
+ assertValidDefault(schema);
206
+ return schema;
207
+ },
208
+ Object(shape, options = {}) {
209
+ const schema = {
210
+ kind: "object",
211
+ shape,
212
+ ...options
213
+ };
214
+ assertValidDefault(schema);
215
+ return schema;
216
+ },
217
+ Optional(inner) {
218
+ return {
219
+ kind: "optional",
220
+ inner
221
+ };
222
+ },
223
+ OneOf,
224
+ Union,
225
+ Record: RecordBuilder,
226
+ Json
227
+ };
228
+ export function toJsonSchema(schema) {
229
+ const unwrappedSchema = unwrapOptional(schema);
230
+ switch (unwrappedSchema.kind) {
231
+ case "string":
232
+ return withStringMetadata(unwrappedSchema, { type: "string" });
233
+ case "number":
234
+ return withNumberMetadata(unwrappedSchema, { type: unwrappedSchema.jsonType ?? "number" });
235
+ case "boolean":
236
+ return withMetadata(unwrappedSchema, { type: "boolean" });
237
+ case "enum": {
238
+ const jsonSchema = {
239
+ enum: unwrappedSchema.nullable === true
240
+ ? [...unwrappedSchema.values, null]
241
+ : [...unwrappedSchema.values]
242
+ };
243
+ const enumType = unwrappedSchema.jsonType ?? getEnumJsonType(unwrappedSchema.values);
244
+ if (enumType !== undefined) {
245
+ jsonSchema.type = enumType;
246
+ }
247
+ return withMetadata(unwrappedSchema, jsonSchema);
248
+ }
249
+ case "array":
250
+ return withArrayMetadata(unwrappedSchema, {
251
+ type: "array",
252
+ items: toJsonSchema(unwrappedSchema.item)
253
+ });
254
+ case "object": {
255
+ const properties = {};
256
+ const required = [];
257
+ for (const [key, propertySchema] of Object.entries(unwrappedSchema.shape)) {
258
+ Object.defineProperty(properties, key, {
259
+ enumerable: true,
260
+ configurable: true,
261
+ writable: true,
262
+ value: toJsonSchema(propertySchema)
263
+ });
264
+ if (!isOptionalSchema(propertySchema)) {
265
+ required.push(key);
266
+ }
267
+ }
268
+ return withObjectMetadata(unwrappedSchema, {
269
+ type: "object",
270
+ properties,
271
+ required
272
+ });
273
+ }
274
+ case "oneOf":
275
+ return withMetadata(unwrappedSchema, {
276
+ oneOf: Object.entries(unwrappedSchema.branches).map(([branchName, branchSchema]) => withInjectedDiscriminator(branchSchema, unwrappedSchema.discriminator, branchName))
277
+ });
278
+ case "union":
279
+ return withMetadata(unwrappedSchema, {
280
+ oneOf: unwrappedSchema.branches.map((branchSchema) => toJsonSchema(branchSchema))
281
+ });
282
+ case "record":
283
+ return withMetadata(unwrappedSchema, {
284
+ type: "object",
285
+ additionalProperties: toJsonSchema(unwrappedSchema.value)
286
+ });
287
+ case "json":
288
+ return withMetadata(unwrappedSchema, {});
289
+ }
290
+ }
291
+ export function toJsonSchemaDocument(schema, options = {}) {
292
+ return createJsonSchemaDocument(toJsonSchema(schema), options);
293
+ }
294
+ export { Json, OneOf, RecordBuilder as Record, Union, validate };
@@ -0,0 +1,14 @@
1
+ import type { JsonSchema } from "./index.js";
2
+ export interface JsonSchemaDocumentOptions {
3
+ id?: string;
4
+ title?: string;
5
+ description?: string;
6
+ schema?: string;
7
+ }
8
+ export type JsonSchemaDocument = JsonSchema & {
9
+ $schema: string;
10
+ $id?: string;
11
+ title?: string;
12
+ description?: string;
13
+ };
14
+ export declare function createJsonSchemaDocument(jsonSchema: JsonSchema, options?: JsonSchemaDocumentOptions): JsonSchemaDocument;
@@ -0,0 +1,17 @@
1
+ export function createJsonSchemaDocument(jsonSchema, options = {}) {
2
+ const { id, schema: schemaUri = "https://json-schema.org/draft/2020-12/schema", ...metadata } = options;
3
+ const document = {
4
+ $schema: schemaUri,
5
+ ...jsonSchema
6
+ };
7
+ if (id !== undefined) {
8
+ document.$id = id;
9
+ }
10
+ if (metadata.title !== undefined) {
11
+ document.title = metadata.title;
12
+ }
13
+ if (metadata.description !== undefined) {
14
+ document.description = metadata.description;
15
+ }
16
+ return document;
17
+ }
@@ -0,0 +1,2 @@
1
+ import { S } from "./index.js";
2
+ const ignoredSchema = S.Json();
@@ -0,0 +1,10 @@
1
+ import type { SchemaBase } from "./index.js";
2
+ type JsonPrimitive = string | number | boolean | null;
3
+ export type JsonValue = JsonPrimitive | {
4
+ [key: string]: JsonValue;
5
+ } | JsonValue[];
6
+ export interface JsonValueSchema extends SchemaBase<"json", JsonValue> {
7
+ readonly kind: "json";
8
+ }
9
+ export declare function Json(): JsonValueSchema;
10
+ export {};
@@ -0,0 +1,5 @@
1
+ export function Json() {
2
+ return {
3
+ kind: "json",
4
+ };
5
+ }
@@ -0,0 +1,12 @@
1
+ import { S } from "./index.js";
2
+ const ignoredSchema = S.OneOf({
3
+ discriminator: "kind",
4
+ branches: {
5
+ text: S.Object({
6
+ value: S.String(),
7
+ }),
8
+ count: S.Object({
9
+ value: S.Number(),
10
+ }),
11
+ },
12
+ });
@@ -0,0 +1,15 @@
1
+ import type { ObjectSchema, SchemaBase, Static } from "./index.js";
2
+ type OneOfStatic<TBranches extends Record<string, ObjectSchema<any>>, TDiscriminator extends string> = {
3
+ [TBranchName in keyof TBranches & string]: Omit<Static<TBranches[TBranchName]>, TDiscriminator> & {
4
+ [TFieldName in TDiscriminator]: TBranchName;
5
+ };
6
+ }[keyof TBranches & string];
7
+ export interface OneOfSchema<TBranches extends Record<string, ObjectSchema<any>>, TDiscriminator extends string = string> extends SchemaBase<"oneOf", OneOfStatic<TBranches, TDiscriminator>> {
8
+ readonly discriminator: TDiscriminator;
9
+ readonly branches: TBranches;
10
+ }
11
+ export declare function OneOf<TDiscriminator extends string, TBranches extends Record<string, ObjectSchema<any>>>(config: {
12
+ discriminator: TDiscriminator;
13
+ branches: TBranches;
14
+ }): OneOfSchema<TBranches, TDiscriminator>;
15
+ export {};
@@ -0,0 +1,18 @@
1
+ function assertValidBranches(branches, discriminator) {
2
+ if (Object.keys(branches).length === 0) {
3
+ throw new Error("OneOf schema requires at least one branch");
4
+ }
5
+ for (const [branchName, branch] of Object.entries(branches)) {
6
+ if (Object.prototype.hasOwnProperty.call(branch.shape, discriminator)) {
7
+ throw new Error(`OneOf branch "${branchName}" must not declare discriminator field "${discriminator}".`);
8
+ }
9
+ }
10
+ }
11
+ export function OneOf(config) {
12
+ assertValidBranches(config.branches, config.discriminator);
13
+ return {
14
+ kind: "oneOf",
15
+ discriminator: config.discriminator,
16
+ branches: config.branches,
17
+ };
18
+ }
@@ -0,0 +1,2 @@
1
+ import { S } from "./index.js";
2
+ const ignoredSchema = S.Record(S.String());
@@ -0,0 +1,5 @@
1
+ import type { AnySchema, SchemaBase, Static } from "./index.js";
2
+ export interface RecordSchema<TValue extends AnySchema> extends SchemaBase<"record", Record<string, Static<TValue>>> {
3
+ readonly value: TValue;
4
+ }
5
+ export declare function Record<TValue extends AnySchema>(value: TValue): RecordSchema<TValue>;
@@ -0,0 +1,6 @@
1
+ export function Record(value) {
2
+ return {
3
+ kind: "record",
4
+ value,
5
+ };
6
+ }
@@ -0,0 +1,9 @@
1
+ import { S } from "./index.js";
2
+ const ignoredSchema = S.Union([
3
+ S.Object({
4
+ email: S.String(),
5
+ }),
6
+ S.Object({
7
+ phone: S.String(),
8
+ }),
9
+ ]);
@@ -0,0 +1,8 @@
1
+ import type { ObjectSchema, SchemaBase, Static } from "./index.js";
2
+ type UnionStatic<TBranches extends readonly ObjectSchema<any>[]> = Static<TBranches[number]>;
3
+ export interface UnionSchema<TBranches extends readonly ObjectSchema<any>[]> extends SchemaBase<"union", UnionStatic<TBranches>> {
4
+ readonly branches: TBranches;
5
+ }
6
+ export declare function getRequiredKeyFingerprint(schema: ObjectSchema<any>): string;
7
+ export declare function Union<const TBranches extends readonly ObjectSchema<any>[]>(branches: TBranches): UnionSchema<TBranches>;
8
+ export {};
@@ -0,0 +1,45 @@
1
+ function isOptionalSchema(schema) {
2
+ return schema.kind === "optional";
3
+ }
4
+ function getRequiredKeys(schema) {
5
+ return Object.keys(schema.shape)
6
+ .filter((key) => !isOptionalSchema(schema.shape[key]))
7
+ .sort();
8
+ }
9
+ export function getRequiredKeyFingerprint(schema) {
10
+ return getRequiredKeys(schema).join("+");
11
+ }
12
+ function assertUniqueRequiredKeyFingerprints(branches) {
13
+ const fingerprints = new Map();
14
+ branches.forEach((branch, index) => {
15
+ const requiredKeys = getRequiredKeys(branch);
16
+ const fingerprint = JSON.stringify(requiredKeys);
17
+ const existing = fingerprints.get(fingerprint);
18
+ if (existing === undefined) {
19
+ fingerprints.set(fingerprint, {
20
+ display: requiredKeys.join("+"),
21
+ indices: [index],
22
+ });
23
+ return;
24
+ }
25
+ existing.indices.push(index);
26
+ });
27
+ for (const { display, indices } of fingerprints.values()) {
28
+ if (indices.length > 1) {
29
+ throw new Error(`Union branches [${indices.join(", ")}] share required-key fingerprint "${display}". Each branch must require a distinct set of keys.`);
30
+ }
31
+ }
32
+ }
33
+ function assertValidBranches(branches) {
34
+ if (branches.length === 0) {
35
+ throw new Error("Union schema requires at least one branch");
36
+ }
37
+ assertUniqueRequiredKeyFingerprints(branches);
38
+ }
39
+ export function Union(branches) {
40
+ assertValidBranches(branches);
41
+ return {
42
+ kind: "union",
43
+ branches,
44
+ };
45
+ }
@@ -0,0 +1,16 @@
1
+ import type { AnySchema, Static } from "./index.js";
2
+ export type SchemaDescriptor = AnySchema;
3
+ export type ValidationIssue = {
4
+ path: readonly string[];
5
+ expected: string;
6
+ received: string;
7
+ message: string;
8
+ };
9
+ export type ValidationResult<T> = {
10
+ ok: true;
11
+ value: T;
12
+ } | {
13
+ ok: false;
14
+ issues: readonly ValidationIssue[];
15
+ };
16
+ export declare function validate<S extends SchemaDescriptor>(schema: S, value: unknown): ValidationResult<Static<S>>;