@notionhq/workers 0.0.82
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 +22 -0
- package/dist/block.d.ts +321 -0
- package/dist/block.d.ts.map +1 -0
- package/dist/block.js +0 -0
- package/dist/builder.d.ts +117 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +168 -0
- package/dist/capabilities/automation.d.ts +91 -0
- package/dist/capabilities/automation.d.ts.map +1 -0
- package/dist/capabilities/automation.js +40 -0
- package/dist/capabilities/automation.test.d.ts +2 -0
- package/dist/capabilities/automation.test.d.ts.map +1 -0
- package/dist/capabilities/context.d.ts +7 -0
- package/dist/capabilities/context.d.ts.map +1 -0
- package/dist/capabilities/context.js +15 -0
- package/dist/capabilities/oauth.d.ts +120 -0
- package/dist/capabilities/oauth.d.ts.map +1 -0
- package/dist/capabilities/oauth.js +55 -0
- package/dist/capabilities/oauth.test.d.ts +2 -0
- package/dist/capabilities/oauth.test.d.ts.map +1 -0
- package/dist/capabilities/sync.d.ts +180 -0
- package/dist/capabilities/sync.d.ts.map +1 -0
- package/dist/capabilities/sync.js +84 -0
- package/dist/capabilities/sync.test.d.ts +2 -0
- package/dist/capabilities/sync.test.d.ts.map +1 -0
- package/dist/capabilities/tool.d.ts +68 -0
- package/dist/capabilities/tool.d.ts.map +1 -0
- package/dist/capabilities/tool.js +174 -0
- package/dist/capabilities/tool.test.d.ts +2 -0
- package/dist/capabilities/tool.test.d.ts.map +1 -0
- package/dist/error.d.ts +12 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +15 -0
- package/dist/icon-names.d.ts +6 -0
- package/dist/icon-names.d.ts.map +1 -0
- package/dist/icon-names.js +0 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/json-schema.d.ts +117 -0
- package/dist/json-schema.d.ts.map +1 -0
- package/dist/json-schema.js +0 -0
- package/dist/json-schema.test.d.ts +2 -0
- package/dist/json-schema.test.d.ts.map +1 -0
- package/dist/schema.d.ts +178 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +66 -0
- package/dist/types.d.ts +206 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/dist/worker.d.ts +177 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +219 -0
- package/package.json +68 -0
- package/src/block.ts +529 -0
- package/src/builder.ts +299 -0
- package/src/capabilities/automation.test.ts +152 -0
- package/src/capabilities/automation.ts +130 -0
- package/src/capabilities/context.ts +23 -0
- package/src/capabilities/oauth.test.ts +52 -0
- package/src/capabilities/oauth.ts +157 -0
- package/src/capabilities/sync.test.ts +104 -0
- package/src/capabilities/sync.ts +311 -0
- package/src/capabilities/tool.test.ts +351 -0
- package/src/capabilities/tool.ts +279 -0
- package/src/error.ts +19 -0
- package/src/icon-names.ts +890 -0
- package/src/index.ts +34 -0
- package/src/json-schema.test.ts +719 -0
- package/src/json-schema.ts +179 -0
- package/src/schema.ts +241 -0
- package/src/types.ts +272 -0
- package/src/worker.ts +285 -0
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-level invariant tests for JSONSchema.
|
|
3
|
+
*
|
|
4
|
+
* Most assertions here are compile-time: a `satisfies` proves a schema is
|
|
5
|
+
* accepted, and `@ts-expect-error` proves an invalid schema is rejected.
|
|
6
|
+
* The runtime `it()` blocks exist only to give structure and make vitest
|
|
7
|
+
* report each invariant by name.
|
|
8
|
+
*
|
|
9
|
+
* For negative tests with multi-line object literals, we use two patterns:
|
|
10
|
+
* 1. Place `@ts-expect-error` directly above the offending property line.
|
|
11
|
+
* 2. Construct the bad value first, then assign on a single line.
|
|
12
|
+
* This ensures tsc sees the error on the line immediately after the directive.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, expect, it } from "vitest";
|
|
15
|
+
import type {
|
|
16
|
+
AnyJSONSchema,
|
|
17
|
+
JSONSchema,
|
|
18
|
+
JSONSchemaAnyOfDef,
|
|
19
|
+
JSONSchemaArrayDef,
|
|
20
|
+
JSONSchemaBooleanDef,
|
|
21
|
+
JSONSchemaNullDef,
|
|
22
|
+
JSONSchemaNumberDef,
|
|
23
|
+
JSONSchemaObjectDef,
|
|
24
|
+
JSONSchemaRefDef,
|
|
25
|
+
JSONSchemaStringDef,
|
|
26
|
+
JSONSchemaStringFormat,
|
|
27
|
+
} from "./json-schema.js";
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Helpers
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/** Compile-time assertion that U is assignable to T. */
|
|
34
|
+
type Assert<_T, _U extends _T> = true;
|
|
35
|
+
|
|
36
|
+
/** Compile-time assertion that U is NOT assignable to T. */
|
|
37
|
+
type AssertNot<_T, _U> = _U extends _T ? never : true;
|
|
38
|
+
|
|
39
|
+
// Suppress "unused" warnings — these are compile-time only.
|
|
40
|
+
type _UseAssert = Assert<unknown, unknown>;
|
|
41
|
+
type _UseAssertNot = AssertNot<never, unknown>;
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// 1. Primitive schemas resolve correctly
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
describe("JSONSchema — primitive resolution", () => {
|
|
48
|
+
it("string → JSONSchemaStringDef", () => {
|
|
49
|
+
const _s = {
|
|
50
|
+
type: "string",
|
|
51
|
+
} as const satisfies JSONSchema<string>;
|
|
52
|
+
|
|
53
|
+
// Ref is always acceptable in any position
|
|
54
|
+
const _r = { $ref: "#/$defs/Name" } as const satisfies JSONSchema<string>;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("string with format", () => {
|
|
58
|
+
const _s = {
|
|
59
|
+
type: "string",
|
|
60
|
+
format: "date-time",
|
|
61
|
+
} as const satisfies JSONSchema<string>;
|
|
62
|
+
|
|
63
|
+
const _s2 = {
|
|
64
|
+
type: "string",
|
|
65
|
+
format: "uuid",
|
|
66
|
+
} as const satisfies JSONSchema<string>;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("string with enum", () => {
|
|
70
|
+
const _s = {
|
|
71
|
+
type: "string",
|
|
72
|
+
enum: ["a", "b", "c"] as const,
|
|
73
|
+
} as const satisfies JSONSchema<"a" | "b" | "c">;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("number → JSONSchemaNumberDef", () => {
|
|
77
|
+
const _n = {
|
|
78
|
+
type: "number",
|
|
79
|
+
} as const satisfies JSONSchema<number>;
|
|
80
|
+
|
|
81
|
+
const _i = {
|
|
82
|
+
type: "integer",
|
|
83
|
+
} as const satisfies JSONSchema<number>;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("number with enum", () => {
|
|
87
|
+
const _n = {
|
|
88
|
+
type: "number",
|
|
89
|
+
enum: [1, 2, 3] as const,
|
|
90
|
+
} as const satisfies JSONSchema<1 | 2 | 3>;
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("boolean → JSONSchemaBooleanDef", () => {
|
|
94
|
+
const _b = {
|
|
95
|
+
type: "boolean",
|
|
96
|
+
} as const satisfies JSONSchema<boolean>;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("null → JSONSchemaNullDef", () => {
|
|
100
|
+
const _n = {
|
|
101
|
+
type: "null",
|
|
102
|
+
} as const satisfies AnyJSONSchema;
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// 2. Object schemas enforce structural invariants
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
describe("JSONSchema — object invariants", () => {
|
|
111
|
+
it("valid object schema compiles", () => {
|
|
112
|
+
const _o = {
|
|
113
|
+
type: "object",
|
|
114
|
+
properties: {
|
|
115
|
+
name: { type: "string" },
|
|
116
|
+
age: { type: "number" },
|
|
117
|
+
},
|
|
118
|
+
required: ["name", "age"],
|
|
119
|
+
additionalProperties: false,
|
|
120
|
+
} as const satisfies JSONSchema<{ name: string; age: number }>;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("object with nested object compiles", () => {
|
|
124
|
+
const _o = {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: {
|
|
127
|
+
address: {
|
|
128
|
+
type: "object",
|
|
129
|
+
properties: {
|
|
130
|
+
street: { type: "string" },
|
|
131
|
+
zip: { type: "string" },
|
|
132
|
+
},
|
|
133
|
+
required: ["street", "zip"],
|
|
134
|
+
additionalProperties: false,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
required: ["address"],
|
|
138
|
+
additionalProperties: false,
|
|
139
|
+
} as const satisfies JSONSchema<{
|
|
140
|
+
address: { street: string; zip: string };
|
|
141
|
+
}>;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("object with $defs compiles", () => {
|
|
145
|
+
const _o = {
|
|
146
|
+
type: "object",
|
|
147
|
+
properties: {
|
|
148
|
+
name: { $ref: "#/$defs/Name" },
|
|
149
|
+
},
|
|
150
|
+
required: ["name"],
|
|
151
|
+
additionalProperties: false,
|
|
152
|
+
$defs: {
|
|
153
|
+
Name: { type: "string" },
|
|
154
|
+
},
|
|
155
|
+
} as const satisfies JSONSchema<{ name: string }>;
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("empty object compiles", () => {
|
|
159
|
+
const _o = {
|
|
160
|
+
type: "object",
|
|
161
|
+
properties: {},
|
|
162
|
+
required: [],
|
|
163
|
+
additionalProperties: false,
|
|
164
|
+
} as const satisfies JSONSchema<Record<string, never>>;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("rejects object without additionalProperties: false", () => {
|
|
168
|
+
const bad = {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: { name: { type: "string" } },
|
|
171
|
+
required: ["name"],
|
|
172
|
+
} as const;
|
|
173
|
+
// @ts-expect-error — additionalProperties is missing
|
|
174
|
+
const _: JSONSchema<{ name: string }> = bad;
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("rejects object with additionalProperties: true", () => {
|
|
178
|
+
const bad = {
|
|
179
|
+
type: "object",
|
|
180
|
+
properties: { name: { type: "string" } },
|
|
181
|
+
required: ["name"],
|
|
182
|
+
additionalProperties: true,
|
|
183
|
+
} as const;
|
|
184
|
+
// @ts-expect-error — additionalProperties must be literally `false`
|
|
185
|
+
const _: JSONSchema<{ name: string }> = bad;
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("rejects missing property in properties", () => {
|
|
189
|
+
const bad = {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: { name: { type: "string" } },
|
|
192
|
+
required: ["name"],
|
|
193
|
+
additionalProperties: false,
|
|
194
|
+
} as const;
|
|
195
|
+
// @ts-expect-error — properties must include all keys of T
|
|
196
|
+
const _: JSONSchema<{ name: string; age: number }> = bad;
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("rejects wrong schema type for a property", () => {
|
|
200
|
+
const bad = {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: { name: { type: "number" } },
|
|
203
|
+
required: ["name"],
|
|
204
|
+
additionalProperties: false,
|
|
205
|
+
} as const;
|
|
206
|
+
// @ts-expect-error — `name` should be string schema, not number
|
|
207
|
+
const _: JSONSchema<{ name: string }> = bad;
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
// 3. Nullable types use anyOf pattern
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
describe("JSONSchema — nullable types", () => {
|
|
216
|
+
it("string | null → anyOf with null", () => {
|
|
217
|
+
const _s = {
|
|
218
|
+
anyOf: [{ type: "string" }, { type: "null" }],
|
|
219
|
+
} as const satisfies JSONSchema<string | null>;
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("number | null → anyOf with null", () => {
|
|
223
|
+
const _n = {
|
|
224
|
+
anyOf: [{ type: "number" }, { type: "null" }],
|
|
225
|
+
} as const satisfies JSONSchema<number | null>;
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("object | null → anyOf with null", () => {
|
|
229
|
+
const _o = {
|
|
230
|
+
anyOf: [
|
|
231
|
+
{
|
|
232
|
+
type: "object",
|
|
233
|
+
properties: { id: { type: "number" } },
|
|
234
|
+
required: ["id"],
|
|
235
|
+
additionalProperties: false,
|
|
236
|
+
},
|
|
237
|
+
{ type: "null" },
|
|
238
|
+
],
|
|
239
|
+
} as const satisfies JSONSchema<{ id: number } | null>;
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("rejects bare type for nullable", () => {
|
|
243
|
+
// @ts-expect-error — string | null must use anyOf pattern, not bare string
|
|
244
|
+
const _bad: JSONSchema<string | null> = { type: "string" };
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
// 4. Array schemas
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
|
|
252
|
+
describe("JSONSchema — array schemas", () => {
|
|
253
|
+
it("string[] compiles", () => {
|
|
254
|
+
const _a = {
|
|
255
|
+
type: "array",
|
|
256
|
+
items: { type: "string" },
|
|
257
|
+
} as const satisfies JSONSchema<string[]>;
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("number[] compiles", () => {
|
|
261
|
+
const _a = {
|
|
262
|
+
type: "array",
|
|
263
|
+
items: { type: "number" },
|
|
264
|
+
} as const satisfies JSONSchema<number[]>;
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("object[] compiles", () => {
|
|
268
|
+
const _a = {
|
|
269
|
+
type: "array",
|
|
270
|
+
items: {
|
|
271
|
+
type: "object",
|
|
272
|
+
properties: { name: { type: "string" } },
|
|
273
|
+
required: ["name"],
|
|
274
|
+
additionalProperties: false,
|
|
275
|
+
},
|
|
276
|
+
} as const satisfies JSONSchema<{ name: string }[]>;
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("minItems 0 or 1 compiles", () => {
|
|
280
|
+
const _a0 = {
|
|
281
|
+
type: "array",
|
|
282
|
+
items: { type: "string" },
|
|
283
|
+
minItems: 0,
|
|
284
|
+
} as const satisfies JSONSchema<string[]>;
|
|
285
|
+
|
|
286
|
+
const _a1 = {
|
|
287
|
+
type: "array",
|
|
288
|
+
items: { type: "string" },
|
|
289
|
+
minItems: 1,
|
|
290
|
+
} as const satisfies JSONSchema<string[]>;
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("rejects minItems > 1", () => {
|
|
294
|
+
const bad = {
|
|
295
|
+
type: "array",
|
|
296
|
+
items: { type: "string" },
|
|
297
|
+
minItems: 2,
|
|
298
|
+
} as const;
|
|
299
|
+
// @ts-expect-error — minItems can only be 0 or 1
|
|
300
|
+
const _: JSONSchema<string[]> = bad;
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("rejects wrong item type", () => {
|
|
304
|
+
const bad = { type: "array", items: { type: "number" } } as const;
|
|
305
|
+
// @ts-expect-error — items should be string schema for string[]
|
|
306
|
+
const _: JSONSchema<string[]> = bad;
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
311
|
+
// 5. Forbidden composition keywords
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
describe("JSONSchema — forbidden composition keywords", () => {
|
|
315
|
+
it("rejects allOf on string def", () => {
|
|
316
|
+
const _bad: JSONSchemaStringDef = {
|
|
317
|
+
type: "string",
|
|
318
|
+
// @ts-expect-error — allOf is forbidden
|
|
319
|
+
allOf: [{ type: "string" }],
|
|
320
|
+
};
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("rejects not on number def", () => {
|
|
324
|
+
const _bad: JSONSchemaNumberDef = {
|
|
325
|
+
type: "number",
|
|
326
|
+
// @ts-expect-error — not is forbidden
|
|
327
|
+
not: { type: "string" },
|
|
328
|
+
};
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("rejects if/then/else on boolean def", () => {
|
|
332
|
+
const _bad: JSONSchemaBooleanDef = {
|
|
333
|
+
type: "boolean",
|
|
334
|
+
// @ts-expect-error — if is forbidden
|
|
335
|
+
if: { type: "string" },
|
|
336
|
+
};
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it("rejects allOf on object def", () => {
|
|
340
|
+
const _bad: JSONSchemaObjectDef<{ x: number }> = {
|
|
341
|
+
type: "object",
|
|
342
|
+
properties: { x: { type: "number" } },
|
|
343
|
+
required: ["x"],
|
|
344
|
+
additionalProperties: false,
|
|
345
|
+
// @ts-expect-error — allOf is forbidden
|
|
346
|
+
allOf: [],
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it("rejects anyOf on concrete string def", () => {
|
|
351
|
+
const _bad: JSONSchemaStringDef = {
|
|
352
|
+
type: "string",
|
|
353
|
+
// @ts-expect-error — anyOf is forbidden on concrete defs
|
|
354
|
+
anyOf: [{ type: "string" }],
|
|
355
|
+
};
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it("rejects allOf on array def", () => {
|
|
359
|
+
const _bad: JSONSchemaArrayDef = {
|
|
360
|
+
type: "array",
|
|
361
|
+
items: { type: "string" },
|
|
362
|
+
// @ts-expect-error — allOf is forbidden
|
|
363
|
+
allOf: [],
|
|
364
|
+
};
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("rejects allOf on null def", () => {
|
|
368
|
+
const _bad: JSONSchemaNullDef = {
|
|
369
|
+
type: "null",
|
|
370
|
+
// @ts-expect-error — allOf is forbidden
|
|
371
|
+
allOf: [],
|
|
372
|
+
};
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it("rejects allOf on ref def", () => {
|
|
376
|
+
const _bad: JSONSchemaRefDef = {
|
|
377
|
+
$ref: "#/$defs/Foo",
|
|
378
|
+
// @ts-expect-error — allOf is forbidden
|
|
379
|
+
allOf: [],
|
|
380
|
+
};
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// ---------------------------------------------------------------------------
|
|
385
|
+
// 6. String format is restricted
|
|
386
|
+
// ---------------------------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
describe("JSONSchema — string format restrictions", () => {
|
|
389
|
+
it("accepts all valid formats", () => {
|
|
390
|
+
const formats: JSONSchemaStringFormat[] = [
|
|
391
|
+
"date-time",
|
|
392
|
+
"time",
|
|
393
|
+
"date",
|
|
394
|
+
"duration",
|
|
395
|
+
"email",
|
|
396
|
+
"hostname",
|
|
397
|
+
"ipv4",
|
|
398
|
+
"ipv6",
|
|
399
|
+
"uuid",
|
|
400
|
+
];
|
|
401
|
+
expect(formats).toHaveLength(9);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it("rejects invalid formats", () => {
|
|
405
|
+
// @ts-expect-error — "uri" is not a supported format
|
|
406
|
+
const _bad: JSONSchemaStringDef = { type: "string", format: "uri" };
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it("rejects uri-reference format", () => {
|
|
410
|
+
const _bad: JSONSchemaStringDef = {
|
|
411
|
+
type: "string",
|
|
412
|
+
// @ts-expect-error — "uri-reference" is not supported
|
|
413
|
+
format: "uri-reference",
|
|
414
|
+
};
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// ---------------------------------------------------------------------------
|
|
419
|
+
// 7. Cross-kind type mismatches are rejected
|
|
420
|
+
// ---------------------------------------------------------------------------
|
|
421
|
+
|
|
422
|
+
describe("JSONSchema — cross-kind type mismatches", () => {
|
|
423
|
+
it("rejects number schema for string type", () => {
|
|
424
|
+
// @ts-expect-error — cannot use number schema for string
|
|
425
|
+
const _bad: JSONSchema<string> = { type: "number" };
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it("rejects string schema for number type", () => {
|
|
429
|
+
// @ts-expect-error — cannot use string schema for number
|
|
430
|
+
const _bad: JSONSchema<number> = { type: "string" };
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it("rejects boolean schema for string type", () => {
|
|
434
|
+
// @ts-expect-error — cannot use boolean schema for string
|
|
435
|
+
const _bad: JSONSchema<string> = { type: "boolean" };
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it("rejects object schema for string type", () => {
|
|
439
|
+
const bad = {
|
|
440
|
+
type: "object",
|
|
441
|
+
properties: {},
|
|
442
|
+
required: [],
|
|
443
|
+
additionalProperties: false,
|
|
444
|
+
} as const;
|
|
445
|
+
// @ts-expect-error — cannot use object schema for string
|
|
446
|
+
const _: JSONSchema<string> = bad;
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it("rejects array schema for number type", () => {
|
|
450
|
+
const bad = { type: "array", items: { type: "string" } } as const;
|
|
451
|
+
// @ts-expect-error — cannot use array schema for number
|
|
452
|
+
const _: JSONSchema<number> = bad;
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it("rejects string schema for object type", () => {
|
|
456
|
+
// @ts-expect-error — cannot use string schema for object
|
|
457
|
+
const _bad: JSONSchema<{ x: number }> = { type: "string" };
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it("rejects string schema for array type", () => {
|
|
461
|
+
// @ts-expect-error — cannot use string schema for string[]
|
|
462
|
+
const _bad: JSONSchema<string[]> = { type: "string" };
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// ---------------------------------------------------------------------------
|
|
467
|
+
// 8. $ref is valid in any position
|
|
468
|
+
// ---------------------------------------------------------------------------
|
|
469
|
+
|
|
470
|
+
describe("JSONSchema — $ref universality", () => {
|
|
471
|
+
it("$ref satisfies string schema", () => {
|
|
472
|
+
const _r = {
|
|
473
|
+
$ref: "#/$defs/Foo",
|
|
474
|
+
} as const satisfies JSONSchema<string>;
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it("$ref satisfies number schema", () => {
|
|
478
|
+
const _r = {
|
|
479
|
+
$ref: "#/$defs/Foo",
|
|
480
|
+
} as const satisfies JSONSchema<number>;
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it("$ref satisfies boolean schema", () => {
|
|
484
|
+
const _r = {
|
|
485
|
+
$ref: "#/$defs/Foo",
|
|
486
|
+
} as const satisfies JSONSchema<boolean>;
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it("$ref satisfies object schema", () => {
|
|
490
|
+
const _r = {
|
|
491
|
+
$ref: "#/$defs/Foo",
|
|
492
|
+
} as const satisfies JSONSchema<{ x: number }>;
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it("$ref satisfies array schema", () => {
|
|
496
|
+
const _r = {
|
|
497
|
+
$ref: "#/$defs/Foo",
|
|
498
|
+
} as const satisfies JSONSchema<string[]>;
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it("$ref with description compiles", () => {
|
|
502
|
+
const _r = {
|
|
503
|
+
$ref: "#/$defs/Foo",
|
|
504
|
+
description: "A reference",
|
|
505
|
+
} as const satisfies JSONSchema<string>;
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// ---------------------------------------------------------------------------
|
|
510
|
+
// 9. AnyJSONSchema (T = unknown) accepts all valid forms
|
|
511
|
+
// ---------------------------------------------------------------------------
|
|
512
|
+
|
|
513
|
+
describe("JSONSchema — unknown/AnyJSONSchema", () => {
|
|
514
|
+
it("accepts all primitive schemas", () => {
|
|
515
|
+
const schemas: AnyJSONSchema[] = [
|
|
516
|
+
{ type: "string" },
|
|
517
|
+
{ type: "number" },
|
|
518
|
+
{ type: "integer" },
|
|
519
|
+
{ type: "boolean" },
|
|
520
|
+
{ type: "null" },
|
|
521
|
+
];
|
|
522
|
+
expect(schemas).toHaveLength(5);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it("accepts ref schema", () => {
|
|
526
|
+
const _r: AnyJSONSchema = { $ref: "#/$defs/Foo" };
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it("accepts anyOf schema", () => {
|
|
530
|
+
const _a: AnyJSONSchema = {
|
|
531
|
+
anyOf: [{ type: "string" }, { type: "null" }],
|
|
532
|
+
};
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it("accepts array schema", () => {
|
|
536
|
+
const _a: AnyJSONSchema = {
|
|
537
|
+
type: "array",
|
|
538
|
+
items: { type: "string" },
|
|
539
|
+
};
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it("accepts object schema", () => {
|
|
543
|
+
const _o: AnyJSONSchema = {
|
|
544
|
+
type: "object",
|
|
545
|
+
properties: { x: { type: "number" } },
|
|
546
|
+
required: ["x"],
|
|
547
|
+
additionalProperties: false,
|
|
548
|
+
};
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it("JSONSchema<unknown> equals AnyJSONSchema", () => {
|
|
552
|
+
type _A = Assert<JSONSchema<unknown>, AnyJSONSchema>;
|
|
553
|
+
type _B = Assert<AnyJSONSchema, JSONSchema<unknown>>;
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// ---------------------------------------------------------------------------
|
|
558
|
+
// 10. Complex / edge-case schemas
|
|
559
|
+
// ---------------------------------------------------------------------------
|
|
560
|
+
|
|
561
|
+
describe("JSONSchema — complex schemas", () => {
|
|
562
|
+
it("deeply nested objects", () => {
|
|
563
|
+
type Deep = {
|
|
564
|
+
level1: {
|
|
565
|
+
level2: {
|
|
566
|
+
value: string;
|
|
567
|
+
};
|
|
568
|
+
};
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
const _d = {
|
|
572
|
+
type: "object",
|
|
573
|
+
properties: {
|
|
574
|
+
level1: {
|
|
575
|
+
type: "object",
|
|
576
|
+
properties: {
|
|
577
|
+
level2: {
|
|
578
|
+
type: "object",
|
|
579
|
+
properties: {
|
|
580
|
+
value: { type: "string" },
|
|
581
|
+
},
|
|
582
|
+
required: ["value"],
|
|
583
|
+
additionalProperties: false,
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
required: ["level2"],
|
|
587
|
+
additionalProperties: false,
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
required: ["level1"],
|
|
591
|
+
additionalProperties: false,
|
|
592
|
+
} as const satisfies JSONSchema<Deep>;
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it("object with array property", () => {
|
|
596
|
+
type WithArray = { tags: string[] };
|
|
597
|
+
|
|
598
|
+
const _s = {
|
|
599
|
+
type: "object",
|
|
600
|
+
properties: {
|
|
601
|
+
tags: {
|
|
602
|
+
type: "array",
|
|
603
|
+
items: { type: "string" },
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
required: ["tags"],
|
|
607
|
+
additionalProperties: false,
|
|
608
|
+
} as const satisfies JSONSchema<WithArray>;
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it("object with nullable property", () => {
|
|
612
|
+
type WithNullable = { name: string; nickname: string | null };
|
|
613
|
+
|
|
614
|
+
const _s = {
|
|
615
|
+
type: "object",
|
|
616
|
+
properties: {
|
|
617
|
+
name: { type: "string" },
|
|
618
|
+
nickname: {
|
|
619
|
+
anyOf: [{ type: "string" }, { type: "null" }],
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
required: ["name", "nickname"],
|
|
623
|
+
additionalProperties: false,
|
|
624
|
+
} as const satisfies JSONSchema<WithNullable>;
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
it("object with enum property", () => {
|
|
628
|
+
type WithEnum = { status: "active" | "inactive" };
|
|
629
|
+
|
|
630
|
+
const _s = {
|
|
631
|
+
type: "object",
|
|
632
|
+
properties: {
|
|
633
|
+
status: {
|
|
634
|
+
type: "string",
|
|
635
|
+
enum: ["active", "inactive"],
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
required: ["status"],
|
|
639
|
+
additionalProperties: false,
|
|
640
|
+
} as const satisfies JSONSchema<WithEnum>;
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it("object with boolean and number properties", () => {
|
|
644
|
+
type Mixed = { enabled: boolean; count: number };
|
|
645
|
+
|
|
646
|
+
const _s = {
|
|
647
|
+
type: "object",
|
|
648
|
+
properties: {
|
|
649
|
+
enabled: { type: "boolean" },
|
|
650
|
+
count: { type: "number" },
|
|
651
|
+
},
|
|
652
|
+
required: ["enabled", "count"],
|
|
653
|
+
additionalProperties: false,
|
|
654
|
+
} as const satisfies JSONSchema<Mixed>;
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it("array of arrays", () => {
|
|
658
|
+
const _a = {
|
|
659
|
+
type: "array",
|
|
660
|
+
items: {
|
|
661
|
+
type: "array",
|
|
662
|
+
items: { type: "number" },
|
|
663
|
+
},
|
|
664
|
+
} as const satisfies JSONSchema<number[][]>;
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
// ---------------------------------------------------------------------------
|
|
669
|
+
// 11. Type-level assertions (no runtime, purely structural)
|
|
670
|
+
// ---------------------------------------------------------------------------
|
|
671
|
+
|
|
672
|
+
describe("JSONSchema — structural type assertions", () => {
|
|
673
|
+
it("JSONSchemaObjectDef requires additionalProperties to be false", () => {
|
|
674
|
+
type _Proof = Assert<
|
|
675
|
+
JSONSchemaObjectDef<{ x: number }>["additionalProperties"],
|
|
676
|
+
false
|
|
677
|
+
>;
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
it("JSONSchemaArrayDef minItems is restricted to 0 | 1", () => {
|
|
681
|
+
type MinItems = NonNullable<JSONSchemaArrayDef["minItems"]>;
|
|
682
|
+
type _Proof = Assert<MinItems, 0 | 1>;
|
|
683
|
+
// And the reverse — 0 | 1 is exactly MinItems
|
|
684
|
+
type _Reverse = Assert<0 | 1, MinItems>;
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
it("JSONSchemaStringFormat covers exactly 9 formats", () => {
|
|
688
|
+
type _1 = Assert<JSONSchemaStringFormat, "date-time">;
|
|
689
|
+
type _2 = Assert<JSONSchemaStringFormat, "time">;
|
|
690
|
+
type _3 = Assert<JSONSchemaStringFormat, "date">;
|
|
691
|
+
type _4 = Assert<JSONSchemaStringFormat, "duration">;
|
|
692
|
+
type _5 = Assert<JSONSchemaStringFormat, "email">;
|
|
693
|
+
type _6 = Assert<JSONSchemaStringFormat, "hostname">;
|
|
694
|
+
type _7 = Assert<JSONSchemaStringFormat, "ipv4">;
|
|
695
|
+
type _8 = Assert<JSONSchemaStringFormat, "ipv6">;
|
|
696
|
+
type _9 = Assert<JSONSchemaStringFormat, "uuid">;
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it("JSONSchemaAnyOfDef does not extend ForbiddenCompositionKeywords", () => {
|
|
700
|
+
const _a: JSONSchemaAnyOfDef = {
|
|
701
|
+
anyOf: [{ type: "string" }],
|
|
702
|
+
};
|
|
703
|
+
const _b: JSONSchemaAnyOfDef = {
|
|
704
|
+
anyOf: [{ type: "string" }],
|
|
705
|
+
description: "test",
|
|
706
|
+
};
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
it("JSONSchemaRefDef requires $ref string", () => {
|
|
710
|
+
type _Proof = Assert<JSONSchemaRefDef["$ref"], string>;
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
it("JSONSchemaObjectDef properties keys match T keys", () => {
|
|
714
|
+
type Schema = JSONSchemaObjectDef<{ a: string; b: number }>;
|
|
715
|
+
type PropKeys = keyof Schema["properties"];
|
|
716
|
+
type _Proof = Assert<PropKeys, "a" | "b">;
|
|
717
|
+
type _Reverse = Assert<"a" | "b", PropKeys>;
|
|
718
|
+
});
|
|
719
|
+
});
|