@livon/schema 0.27.0-rc.1
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/PROMPT.md +21 -0
- package/README.md +13 -0
- package/SCHEMA.md +13 -0
- package/dist/SchemaValidationError.cjs +41 -0
- package/dist/SchemaValidationError.d.ts +20 -0
- package/dist/SchemaValidationError.js +7 -0
- package/dist/SchemaValidationError.spec.cjs +65 -0
- package/dist/SchemaValidationError.spec.d.ts +1 -0
- package/dist/SchemaValidationError.spec.js +59 -0
- package/dist/after.cjs +36 -0
- package/dist/after.d.ts +30 -0
- package/dist/after.js +2 -0
- package/dist/after.spec.cjs +54 -0
- package/dist/after.spec.d.ts +1 -0
- package/dist/after.spec.js +48 -0
- package/dist/and.cjs +36 -0
- package/dist/and.d.ts +26 -0
- package/dist/and.js +2 -0
- package/dist/and.spec.cjs +57 -0
- package/dist/and.spec.d.ts +1 -0
- package/dist/and.spec.js +51 -0
- package/dist/api.cjs +317 -0
- package/dist/api.d.ts +107 -0
- package/dist/api.js +277 -0
- package/dist/api.spec.cjs +512 -0
- package/dist/api.spec.d.ts +1 -0
- package/dist/api.spec.js +506 -0
- package/dist/array.cjs +74 -0
- package/dist/array.d.ts +25 -0
- package/dist/array.js +40 -0
- package/dist/array.spec.cjs +167 -0
- package/dist/array.spec.d.ts +1 -0
- package/dist/array.spec.js +161 -0
- package/dist/before.cjs +36 -0
- package/dist/before.d.ts +30 -0
- package/dist/before.js +2 -0
- package/dist/before.spec.cjs +54 -0
- package/dist/before.spec.d.ts +1 -0
- package/dist/before.spec.js +48 -0
- package/dist/binary.cjs +53 -0
- package/dist/binary.d.ts +24 -0
- package/dist/binary.js +19 -0
- package/dist/binary.spec.cjs +107 -0
- package/dist/binary.spec.d.ts +1 -0
- package/dist/binary.spec.js +101 -0
- package/dist/boolean.cjs +53 -0
- package/dist/boolean.d.ts +24 -0
- package/dist/boolean.js +19 -0
- package/dist/boolean.spec.cjs +96 -0
- package/dist/boolean.spec.d.ts +1 -0
- package/dist/boolean.spec.js +90 -0
- package/dist/context.cjs +125 -0
- package/dist/context.d.ts +101 -0
- package/dist/context.js +76 -0
- package/dist/context.spec.cjs +244 -0
- package/dist/context.spec.d.ts +1 -0
- package/dist/context.spec.js +238 -0
- package/dist/date.cjs +53 -0
- package/dist/date.d.ts +24 -0
- package/dist/date.js +19 -0
- package/dist/date.spec.cjs +97 -0
- package/dist/date.spec.d.ts +1 -0
- package/dist/date.spec.js +91 -0
- package/dist/doc.cjs +54 -0
- package/dist/doc.d.ts +25 -0
- package/dist/doc.js +17 -0
- package/dist/doc.spec.cjs +99 -0
- package/dist/doc.spec.d.ts +1 -0
- package/dist/doc.spec.js +93 -0
- package/dist/enumeration.cjs +74 -0
- package/dist/enumeration.d.ts +50 -0
- package/dist/enumeration.js +40 -0
- package/dist/enumeration.spec.cjs +110 -0
- package/dist/enumeration.spec.d.ts +1 -0
- package/dist/enumeration.spec.js +104 -0
- package/dist/hydrate.cjs +18 -0
- package/dist/hydrate.d.ts +1 -0
- package/dist/hydrate.js +0 -0
- package/dist/index.cjs +145 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +24 -0
- package/dist/index.spec.cjs +43 -0
- package/dist/index.spec.d.ts +1 -0
- package/dist/index.spec.js +37 -0
- package/dist/literal.cjs +55 -0
- package/dist/literal.d.ts +25 -0
- package/dist/literal.js +21 -0
- package/dist/literal.spec.cjs +93 -0
- package/dist/literal.spec.d.ts +1 -0
- package/dist/literal.spec.js +87 -0
- package/dist/number.cjs +89 -0
- package/dist/number.d.ts +84 -0
- package/dist/number.js +55 -0
- package/dist/number.spec.cjs +155 -0
- package/dist/number.spec.d.ts +1 -0
- package/dist/number.spec.js +149 -0
- package/dist/object.cjs +66 -0
- package/dist/object.d.ts +37 -0
- package/dist/object.js +32 -0
- package/dist/object.spec.cjs +171 -0
- package/dist/object.spec.d.ts +1 -0
- package/dist/object.spec.js +165 -0
- package/dist/operation.cjs +182 -0
- package/dist/operation.d.ts +197 -0
- package/dist/operation.js +133 -0
- package/dist/operation.spec.cjs +454 -0
- package/dist/operation.spec.d.ts +1 -0
- package/dist/operation.spec.js +448 -0
- package/dist/or.cjs +85 -0
- package/dist/or.d.ts +37 -0
- package/dist/or.js +51 -0
- package/dist/or.spec.cjs +204 -0
- package/dist/or.spec.d.ts +1 -0
- package/dist/or.spec.js +198 -0
- package/dist/schema.cjs +285 -0
- package/dist/schema.d.ts +132 -0
- package/dist/schema.js +233 -0
- package/dist/schema.spec.cjs +587 -0
- package/dist/schema.spec.d.ts +1 -0
- package/dist/schema.spec.js +581 -0
- package/dist/schemaFactory.cjs +125 -0
- package/dist/schemaFactory.d.ts +97 -0
- package/dist/schemaFactory.js +88 -0
- package/dist/schemaFactory.spec.cjs +197 -0
- package/dist/schemaFactory.spec.d.ts +1 -0
- package/dist/schemaFactory.spec.js +191 -0
- package/dist/schemaModule.cjs +280 -0
- package/dist/schemaModule.d.ts +97 -0
- package/dist/schemaModule.js +243 -0
- package/dist/schemaModule.spec.cjs +355 -0
- package/dist/schemaModule.spec.d.ts +1 -0
- package/dist/schemaModule.spec.js +349 -0
- package/dist/string.cjs +93 -0
- package/dist/string.d.ts +85 -0
- package/dist/string.js +59 -0
- package/dist/string.spec.cjs +158 -0
- package/dist/string.spec.d.ts +1 -0
- package/dist/string.spec.js +152 -0
- package/dist/testing/mocks/assertions.mock.cjs +48 -0
- package/dist/testing/mocks/assertions.mock.d.ts +5 -0
- package/dist/testing/mocks/assertions.mock.js +14 -0
- package/dist/testing/mocks/index.cjs +52 -0
- package/dist/testing/mocks/index.d.ts +4 -0
- package/dist/testing/mocks/index.js +3 -0
- package/dist/testing/mocks/schema.mock.cjs +120 -0
- package/dist/testing/mocks/schema.mock.d.ts +37 -0
- package/dist/testing/mocks/schema.mock.js +74 -0
- package/dist/tuple.cjs +58 -0
- package/dist/tuple.d.ts +33 -0
- package/dist/tuple.js +24 -0
- package/dist/tuple.spec.cjs +162 -0
- package/dist/tuple.spec.d.ts +1 -0
- package/dist/tuple.spec.js +156 -0
- package/dist/typeGuards.cjs +60 -0
- package/dist/typeGuards.d.ts +93 -0
- package/dist/typeGuards.js +8 -0
- package/dist/typeGuards.spec.cjs +101 -0
- package/dist/typeGuards.spec.d.ts +1 -0
- package/dist/typeGuards.spec.js +95 -0
- package/dist/types.cjs +18 -0
- package/dist/types.d.ts +289 -0
- package/dist/types.js +0 -0
- package/dist/union.cjs +74 -0
- package/dist/union.d.ts +33 -0
- package/dist/union.js +40 -0
- package/dist/union.spec.cjs +159 -0
- package/dist/union.spec.d.ts +1 -0
- package/dist/union.spec.js +153 -0
- package/package.json +47 -0
package/dist/api.spec.js
ADDED
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { api, composeApi, subscription } from "./api.js";
|
|
3
|
+
import { fieldOperation, operation } from "./operation.js";
|
|
4
|
+
import { createBaseSchemaMock } from "./testing/mocks/index.js";
|
|
5
|
+
describe('api utilities', ()=>{
|
|
6
|
+
beforeAll(()=>{
|
|
7
|
+
vi.useRealTimers();
|
|
8
|
+
});
|
|
9
|
+
beforeEach(()=>{
|
|
10
|
+
vi.clearAllMocks();
|
|
11
|
+
});
|
|
12
|
+
afterEach(()=>{
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
afterAll(()=>{
|
|
16
|
+
vi.restoreAllMocks();
|
|
17
|
+
});
|
|
18
|
+
describe("subscription()", ()=>{
|
|
19
|
+
describe('happy', ()=>{
|
|
20
|
+
it('should include optional fields when optional values are provided', ()=>{
|
|
21
|
+
const payload = createBaseSchemaMock({
|
|
22
|
+
name: 'Payload',
|
|
23
|
+
type: 'payload'
|
|
24
|
+
});
|
|
25
|
+
const input = createBaseSchemaMock({
|
|
26
|
+
name: 'Input',
|
|
27
|
+
type: 'input'
|
|
28
|
+
});
|
|
29
|
+
const output = createBaseSchemaMock({
|
|
30
|
+
name: 'Output',
|
|
31
|
+
type: 'output'
|
|
32
|
+
});
|
|
33
|
+
const filter = vi.fn(()=>true);
|
|
34
|
+
const exec = vi.fn(()=>({
|
|
35
|
+
event: 'created'
|
|
36
|
+
}));
|
|
37
|
+
const result = subscription({
|
|
38
|
+
name: 'userCreated',
|
|
39
|
+
input,
|
|
40
|
+
payload,
|
|
41
|
+
output,
|
|
42
|
+
filter,
|
|
43
|
+
exec,
|
|
44
|
+
doc: {
|
|
45
|
+
summary: "subscription doc"
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
expect(result.type).toBe("subscription");
|
|
49
|
+
expect(result.name).toBe('userCreated');
|
|
50
|
+
expect(result.input).toBe(input);
|
|
51
|
+
expect(result.payload).toBe(payload);
|
|
52
|
+
expect(result.output).toBe(output);
|
|
53
|
+
expect(result.filter).toBe(filter);
|
|
54
|
+
expect(result.exec).toBe(exec);
|
|
55
|
+
expect(result.doc).toEqual({
|
|
56
|
+
summary: "subscription doc"
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('sad', ()=>{
|
|
61
|
+
it('should only include mandatory payload when optional values are omitted', ()=>{
|
|
62
|
+
const payload = createBaseSchemaMock({
|
|
63
|
+
name: 'Payload',
|
|
64
|
+
type: 'payload'
|
|
65
|
+
});
|
|
66
|
+
const result = subscription({
|
|
67
|
+
payload
|
|
68
|
+
});
|
|
69
|
+
expect(result.type).toBe("subscription");
|
|
70
|
+
expect(result.payload).toBe(payload);
|
|
71
|
+
expect(result.input).toBeUndefined();
|
|
72
|
+
expect(result.output).toBeUndefined();
|
|
73
|
+
expect(result.filter).toBeUndefined();
|
|
74
|
+
expect(result.exec).toBeUndefined();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe('api()', ()=>{
|
|
79
|
+
describe('happy', ()=>{
|
|
80
|
+
it('should merge explicit and inline operations when both are provided', ()=>{
|
|
81
|
+
const inputSchema = createBaseSchemaMock({
|
|
82
|
+
name: 'Input',
|
|
83
|
+
type: 'input',
|
|
84
|
+
outputValue: 'x'
|
|
85
|
+
});
|
|
86
|
+
const explicitOperation = operation({
|
|
87
|
+
input: inputSchema,
|
|
88
|
+
exec: async ()=>'explicit'
|
|
89
|
+
});
|
|
90
|
+
const inlineOperation = operation({
|
|
91
|
+
input: inputSchema,
|
|
92
|
+
exec: async ()=>'inline'
|
|
93
|
+
});
|
|
94
|
+
const result = api({
|
|
95
|
+
operations: {
|
|
96
|
+
explicit: explicitOperation
|
|
97
|
+
},
|
|
98
|
+
inline: inlineOperation
|
|
99
|
+
});
|
|
100
|
+
const operations = result.operations;
|
|
101
|
+
expect(operations.explicit?.name).toBe('explicit');
|
|
102
|
+
expect(operations.explicit?.exec).toBe(explicitOperation.exec);
|
|
103
|
+
expect(operations.inline?.name).toBe('inline');
|
|
104
|
+
expect(operations.inline?.exec).toBe(inlineOperation.exec);
|
|
105
|
+
});
|
|
106
|
+
it("should assign missing names to operations field operations and subscriptions", ()=>{
|
|
107
|
+
const entity = createBaseSchemaMock({
|
|
108
|
+
name: 'User',
|
|
109
|
+
type: 'object',
|
|
110
|
+
astNode: {
|
|
111
|
+
type: 'object',
|
|
112
|
+
name: 'User'
|
|
113
|
+
},
|
|
114
|
+
outputValue: {
|
|
115
|
+
id: 'u-1'
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
const inputSchema = createBaseSchemaMock({
|
|
119
|
+
name: 'Input',
|
|
120
|
+
type: 'input',
|
|
121
|
+
outputValue: 'x'
|
|
122
|
+
});
|
|
123
|
+
const payloadSchema = createBaseSchemaMock({
|
|
124
|
+
name: 'Payload',
|
|
125
|
+
type: 'payload'
|
|
126
|
+
});
|
|
127
|
+
const op = operation({
|
|
128
|
+
input: inputSchema,
|
|
129
|
+
exec: async ()=>'ok'
|
|
130
|
+
});
|
|
131
|
+
const fieldOp = fieldOperation({
|
|
132
|
+
dependsOn: entity,
|
|
133
|
+
exec: async ()=>'field'
|
|
134
|
+
});
|
|
135
|
+
const sub = subscription({
|
|
136
|
+
payload: payloadSchema
|
|
137
|
+
});
|
|
138
|
+
const result = api({
|
|
139
|
+
type: entity,
|
|
140
|
+
operations: {
|
|
141
|
+
createUser: op
|
|
142
|
+
},
|
|
143
|
+
fieldOperations: {
|
|
144
|
+
displayName: fieldOp
|
|
145
|
+
},
|
|
146
|
+
subscriptions: {
|
|
147
|
+
userCreated: sub
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
expect(result.operations.createUser.name).toBe('createUser');
|
|
151
|
+
expect(result.fieldOperations.displayName.name).toBe('displayName');
|
|
152
|
+
expect(result.subscriptions.userCreated?.name).toBe('userCreated');
|
|
153
|
+
});
|
|
154
|
+
it("should build ast with operation subscription and field nodes when ast is requested", ()=>{
|
|
155
|
+
const entity = createBaseSchemaMock({
|
|
156
|
+
name: 'User',
|
|
157
|
+
type: 'object',
|
|
158
|
+
astNode: {
|
|
159
|
+
type: 'object',
|
|
160
|
+
name: 'User'
|
|
161
|
+
},
|
|
162
|
+
outputValue: {
|
|
163
|
+
id: 'u-1'
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
const operationInput = createBaseSchemaMock({
|
|
167
|
+
name: 'OperationInput',
|
|
168
|
+
type: 'operation-input',
|
|
169
|
+
astNode: {
|
|
170
|
+
type: 'object',
|
|
171
|
+
name: 'OperationInput'
|
|
172
|
+
},
|
|
173
|
+
outputValue: {
|
|
174
|
+
name: 'Alice'
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
const operationOutput = createBaseSchemaMock({
|
|
178
|
+
name: 'OperationOutput',
|
|
179
|
+
type: 'operation-output',
|
|
180
|
+
astNode: {
|
|
181
|
+
type: 'object',
|
|
182
|
+
name: 'OperationOutput'
|
|
183
|
+
},
|
|
184
|
+
outputValue: {
|
|
185
|
+
id: 'u-1'
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
const subscriptionPayload = createBaseSchemaMock({
|
|
189
|
+
name: "SubscriptionPayload",
|
|
190
|
+
type: "subscription-payload",
|
|
191
|
+
astNode: {
|
|
192
|
+
type: 'object',
|
|
193
|
+
name: "SubscriptionPayload"
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
const fieldInput = createBaseSchemaMock({
|
|
197
|
+
name: 'FieldInput',
|
|
198
|
+
type: 'field-input',
|
|
199
|
+
astNode: {
|
|
200
|
+
type: 'object',
|
|
201
|
+
name: 'FieldInput'
|
|
202
|
+
},
|
|
203
|
+
outputValue: {
|
|
204
|
+
locale: 'en'
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
const fieldOutput = createBaseSchemaMock({
|
|
208
|
+
name: 'FieldOutput',
|
|
209
|
+
type: 'field-output',
|
|
210
|
+
astNode: {
|
|
211
|
+
type: 'object',
|
|
212
|
+
name: 'FieldOutput'
|
|
213
|
+
},
|
|
214
|
+
outputValue: {
|
|
215
|
+
value: 'Alice'
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
const createUser = operation({
|
|
219
|
+
input: operationInput,
|
|
220
|
+
output: operationOutput,
|
|
221
|
+
exec: async ()=>({
|
|
222
|
+
id: 'u-1'
|
|
223
|
+
}),
|
|
224
|
+
publish: {
|
|
225
|
+
userCreated: ()=>({
|
|
226
|
+
id: 'u-1'
|
|
227
|
+
})
|
|
228
|
+
},
|
|
229
|
+
ack: {
|
|
230
|
+
required: true,
|
|
231
|
+
mode: 'handled'
|
|
232
|
+
},
|
|
233
|
+
doc: {
|
|
234
|
+
summary: 'create user'
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
const displayName = fieldOperation({
|
|
238
|
+
dependsOn: entity,
|
|
239
|
+
input: fieldInput,
|
|
240
|
+
output: fieldOutput,
|
|
241
|
+
exec: async ()=>({
|
|
242
|
+
value: 'Alice'
|
|
243
|
+
}),
|
|
244
|
+
doc: {
|
|
245
|
+
summary: 'field doc'
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
const userCreated = subscription({
|
|
249
|
+
payload: subscriptionPayload,
|
|
250
|
+
doc: {
|
|
251
|
+
summary: 'sub doc'
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
const result = api({
|
|
255
|
+
doc: {
|
|
256
|
+
summary: 'api doc'
|
|
257
|
+
},
|
|
258
|
+
type: entity,
|
|
259
|
+
operations: {
|
|
260
|
+
createUser
|
|
261
|
+
},
|
|
262
|
+
fieldOperations: {
|
|
263
|
+
displayName
|
|
264
|
+
},
|
|
265
|
+
subscriptions: {
|
|
266
|
+
userCreated
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
const ast = result.ast();
|
|
270
|
+
const operationNode = ast.children?.find((node)=>'operation' === node.type);
|
|
271
|
+
const subscriptionNode = ast.children?.find((node)=>"subscription" === node.type);
|
|
272
|
+
const fieldNode = ast.children?.find((node)=>'field' === node.type);
|
|
273
|
+
expect(ast.type).toBe('api');
|
|
274
|
+
expect(ast.doc).toEqual({
|
|
275
|
+
summary: 'api doc'
|
|
276
|
+
});
|
|
277
|
+
expect(operationNode).toMatchObject({
|
|
278
|
+
name: 'createUser',
|
|
279
|
+
constraints: {
|
|
280
|
+
publish: [
|
|
281
|
+
'userCreated'
|
|
282
|
+
],
|
|
283
|
+
ack: {
|
|
284
|
+
required: true,
|
|
285
|
+
mode: 'handled'
|
|
286
|
+
},
|
|
287
|
+
request: 'OperationInput',
|
|
288
|
+
response: 'OperationOutput'
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
expect(subscriptionNode).toMatchObject({
|
|
292
|
+
name: 'userCreated',
|
|
293
|
+
constraints: {
|
|
294
|
+
payload: "SubscriptionPayload",
|
|
295
|
+
output: "SubscriptionPayload"
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
expect(fieldNode).toMatchObject({
|
|
299
|
+
name: 'User.displayName',
|
|
300
|
+
constraints: {
|
|
301
|
+
owner: 'User',
|
|
302
|
+
field: 'displayName',
|
|
303
|
+
request: 'FieldInput',
|
|
304
|
+
response: 'FieldOutput',
|
|
305
|
+
dependsOn: 'User'
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
describe('sad', ()=>{
|
|
311
|
+
it('should throw when field operations are provided without entity type', ()=>{
|
|
312
|
+
const fieldOp = fieldOperation({
|
|
313
|
+
dependsOn: createBaseSchemaMock({
|
|
314
|
+
outputValue: {
|
|
315
|
+
id: 'u-1'
|
|
316
|
+
}
|
|
317
|
+
}),
|
|
318
|
+
exec: async ()=>'field'
|
|
319
|
+
});
|
|
320
|
+
expect(()=>api({
|
|
321
|
+
fieldOperations: {
|
|
322
|
+
displayName: fieldOp
|
|
323
|
+
}
|
|
324
|
+
})).toThrowError('api.type is required when fieldOperations are provided.');
|
|
325
|
+
});
|
|
326
|
+
it("should throw when operation publishes topic without matching subscription", ()=>{
|
|
327
|
+
const inputSchema = createBaseSchemaMock({
|
|
328
|
+
outputValue: 'x'
|
|
329
|
+
});
|
|
330
|
+
const publishingOperation = operation({
|
|
331
|
+
input: inputSchema,
|
|
332
|
+
exec: async ()=>'ok',
|
|
333
|
+
publish: {
|
|
334
|
+
missingTopic: ()=>({
|
|
335
|
+
payload: true
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
expect(()=>api({
|
|
340
|
+
operations: {
|
|
341
|
+
createUser: publishingOperation
|
|
342
|
+
}
|
|
343
|
+
})).toThrowError('api: operation "createUser" publishes "missingTopic" but no subscription with that name exists.');
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
describe('composeApi()', ()=>{
|
|
348
|
+
describe('happy', ()=>{
|
|
349
|
+
it("should compose operations field operations and subscriptions from multiple apis", ()=>{
|
|
350
|
+
const userSchema = createBaseSchemaMock({
|
|
351
|
+
name: 'User',
|
|
352
|
+
type: 'object',
|
|
353
|
+
astNode: {
|
|
354
|
+
type: 'object',
|
|
355
|
+
name: 'User'
|
|
356
|
+
},
|
|
357
|
+
outputValue: {
|
|
358
|
+
id: 'u-1'
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
const inputSchema = createBaseSchemaMock({
|
|
362
|
+
outputValue: 'x'
|
|
363
|
+
});
|
|
364
|
+
const payloadSchema = createBaseSchemaMock({
|
|
365
|
+
name: 'Payload',
|
|
366
|
+
type: 'payload'
|
|
367
|
+
});
|
|
368
|
+
const usersApi = api({
|
|
369
|
+
type: userSchema,
|
|
370
|
+
operations: {
|
|
371
|
+
createUser: operation({
|
|
372
|
+
input: inputSchema,
|
|
373
|
+
exec: async ()=>'ok'
|
|
374
|
+
})
|
|
375
|
+
},
|
|
376
|
+
fieldOperations: {
|
|
377
|
+
displayName: fieldOperation({
|
|
378
|
+
dependsOn: userSchema,
|
|
379
|
+
exec: async ()=>'name'
|
|
380
|
+
})
|
|
381
|
+
},
|
|
382
|
+
subscriptions: {
|
|
383
|
+
userCreated: subscription({
|
|
384
|
+
payload: payloadSchema
|
|
385
|
+
})
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
const systemApi = api({
|
|
389
|
+
operations: {
|
|
390
|
+
health: operation({
|
|
391
|
+
input: inputSchema,
|
|
392
|
+
exec: async ()=>'ok'
|
|
393
|
+
})
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
const composed = composeApi({
|
|
397
|
+
users: usersApi,
|
|
398
|
+
system: systemApi
|
|
399
|
+
});
|
|
400
|
+
const ast = composed.ast();
|
|
401
|
+
expect(composed.type).toBe('api-composed');
|
|
402
|
+
expect(Object.keys(composed.operations)).toEqual([
|
|
403
|
+
'createUser',
|
|
404
|
+
'health'
|
|
405
|
+
]);
|
|
406
|
+
expect(Object.keys(composed.fieldOperations)).toEqual([
|
|
407
|
+
'User.displayName'
|
|
408
|
+
]);
|
|
409
|
+
expect(Object.keys(composed.subscriptions)).toEqual([
|
|
410
|
+
'userCreated'
|
|
411
|
+
]);
|
|
412
|
+
expect(ast.type).toBe('api-composed');
|
|
413
|
+
expect(ast.children?.map((node)=>node.name)).toEqual([
|
|
414
|
+
'users',
|
|
415
|
+
'system'
|
|
416
|
+
]);
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
describe('sad', ()=>{
|
|
420
|
+
it('should throw when two apis contain duplicate operation names', ()=>{
|
|
421
|
+
const inputSchema = createBaseSchemaMock({
|
|
422
|
+
outputValue: 'x'
|
|
423
|
+
});
|
|
424
|
+
const left = api({
|
|
425
|
+
operations: {
|
|
426
|
+
ping: operation({
|
|
427
|
+
input: inputSchema,
|
|
428
|
+
exec: async ()=>'left'
|
|
429
|
+
})
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
const right = api({
|
|
433
|
+
operations: {
|
|
434
|
+
ping: operation({
|
|
435
|
+
input: inputSchema,
|
|
436
|
+
exec: async ()=>'right'
|
|
437
|
+
})
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
expect(()=>composeApi({
|
|
441
|
+
left,
|
|
442
|
+
right
|
|
443
|
+
})).toThrowError('composeApi: duplicate operation name "ping"');
|
|
444
|
+
});
|
|
445
|
+
it('should throw when two apis contain duplicate field operation names', ()=>{
|
|
446
|
+
const owner = createBaseSchemaMock({
|
|
447
|
+
name: 'User',
|
|
448
|
+
type: 'object',
|
|
449
|
+
astNode: {
|
|
450
|
+
type: 'object',
|
|
451
|
+
name: 'User'
|
|
452
|
+
},
|
|
453
|
+
outputValue: {
|
|
454
|
+
id: 'u-1'
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
const left = api({
|
|
458
|
+
type: owner,
|
|
459
|
+
fieldOperations: {
|
|
460
|
+
displayName: fieldOperation({
|
|
461
|
+
dependsOn: owner,
|
|
462
|
+
exec: async ()=>'left'
|
|
463
|
+
})
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
const right = api({
|
|
467
|
+
type: owner,
|
|
468
|
+
fieldOperations: {
|
|
469
|
+
displayName: fieldOperation({
|
|
470
|
+
dependsOn: owner,
|
|
471
|
+
exec: async ()=>'right'
|
|
472
|
+
})
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
expect(()=>composeApi({
|
|
476
|
+
left,
|
|
477
|
+
right
|
|
478
|
+
})).toThrowError('composeApi: duplicate field operation name "User.displayName"');
|
|
479
|
+
});
|
|
480
|
+
it("should throw when two apis contain duplicate subscription names", ()=>{
|
|
481
|
+
const payload = createBaseSchemaMock({
|
|
482
|
+
name: 'Payload',
|
|
483
|
+
type: 'payload'
|
|
484
|
+
});
|
|
485
|
+
const left = api({
|
|
486
|
+
subscriptions: {
|
|
487
|
+
userCreated: subscription({
|
|
488
|
+
payload
|
|
489
|
+
})
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
const right = api({
|
|
493
|
+
subscriptions: {
|
|
494
|
+
userCreated: subscription({
|
|
495
|
+
payload
|
|
496
|
+
})
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
expect(()=>composeApi({
|
|
500
|
+
left,
|
|
501
|
+
right
|
|
502
|
+
})).toThrowError('composeApi: duplicate subscription name "userCreated"');
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
});
|
package/dist/array.cjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
array: ()=>array
|
|
28
|
+
});
|
|
29
|
+
const external_schemaFactory_cjs_namespaceObject = require("./schemaFactory.cjs");
|
|
30
|
+
const external_typeGuards_cjs_namespaceObject = require("./typeGuards.cjs");
|
|
31
|
+
const array = ({ name, item, doc })=>(0, external_schemaFactory_cjs_namespaceObject.schemaFactory)({
|
|
32
|
+
name,
|
|
33
|
+
type: `array<${item.name}>`,
|
|
34
|
+
doc,
|
|
35
|
+
ast: (ctx)=>{
|
|
36
|
+
const build = ctx.getBuildContext();
|
|
37
|
+
return {
|
|
38
|
+
type: 'array',
|
|
39
|
+
name,
|
|
40
|
+
children: [
|
|
41
|
+
item.ast(build ?? void 0)
|
|
42
|
+
]
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
validate: (input, ctx)=>{
|
|
46
|
+
if (!(0, external_typeGuards_cjs_namespaceObject.isArray)()(input)) throw {
|
|
47
|
+
message: 'Expected array',
|
|
48
|
+
code: 'array.type'
|
|
49
|
+
};
|
|
50
|
+
const values = [];
|
|
51
|
+
input.forEach((value, index)=>{
|
|
52
|
+
try {
|
|
53
|
+
values.push(item.parse(value, ctx));
|
|
54
|
+
} catch (error) {
|
|
55
|
+
throw {
|
|
56
|
+
message: `Invalid item at index ${index}`,
|
|
57
|
+
code: 'array.item',
|
|
58
|
+
context: {
|
|
59
|
+
index,
|
|
60
|
+
error
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return values;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
exports.array = __webpack_exports__.array;
|
|
69
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
70
|
+
"array"
|
|
71
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
72
|
+
Object.defineProperty(exports, '__esModule', {
|
|
73
|
+
value: true
|
|
74
|
+
});
|
package/dist/array.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Schema, SchemaDoc } from './types.js';
|
|
2
|
+
export interface ArraySchemaInput<T> {
|
|
3
|
+
name: string;
|
|
4
|
+
item: Schema<T>;
|
|
5
|
+
doc?: SchemaDoc;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* array is part of the public LIVON API.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* Parameter and return types are defined in the TypeScript signature.
|
|
12
|
+
*
|
|
13
|
+
* @see https://live-input-vector-output-node.github.io/livon-ts/docs/schema/array
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Creates an array schema that validates string items.
|
|
17
|
+
* const Tags = array({ name: 'tags', item: string() });
|
|
18
|
+
* Tags.parse(['typescript', 'schema']);
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Extends the array schema to also allow undefined.
|
|
22
|
+
* const OptionalTags = array({ name: 'tags', item: string() }).optional();
|
|
23
|
+
* OptionalTags.parse(undefined);
|
|
24
|
+
*/
|
|
25
|
+
export declare const array: <T>({ name, item, doc }: ArraySchemaInput<T>) => import("./schemaFactory.js").SchemaWithChain<T[], import("./schemaFactory.js").SchemaFactoryChainDefinition<T[]>>;
|
package/dist/array.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { schemaFactory } from "./schemaFactory.js";
|
|
2
|
+
import { isArray } from "./typeGuards.js";
|
|
3
|
+
const array = ({ name, item, doc })=>schemaFactory({
|
|
4
|
+
name,
|
|
5
|
+
type: `array<${item.name}>`,
|
|
6
|
+
doc,
|
|
7
|
+
ast: (ctx)=>{
|
|
8
|
+
const build = ctx.getBuildContext();
|
|
9
|
+
return {
|
|
10
|
+
type: 'array',
|
|
11
|
+
name,
|
|
12
|
+
children: [
|
|
13
|
+
item.ast(build ?? void 0)
|
|
14
|
+
]
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
validate: (input, ctx)=>{
|
|
18
|
+
if (!isArray()(input)) throw {
|
|
19
|
+
message: 'Expected array',
|
|
20
|
+
code: 'array.type'
|
|
21
|
+
};
|
|
22
|
+
const values = [];
|
|
23
|
+
input.forEach((value, index)=>{
|
|
24
|
+
try {
|
|
25
|
+
values.push(item.parse(value, ctx));
|
|
26
|
+
} catch (error) {
|
|
27
|
+
throw {
|
|
28
|
+
message: `Invalid item at index ${index}`,
|
|
29
|
+
code: 'array.item',
|
|
30
|
+
context: {
|
|
31
|
+
index,
|
|
32
|
+
error
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return values;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
export { array };
|