nervur 0.14.0 → 0.16.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/blueprint.mjs DELETED
@@ -1,512 +0,0 @@
1
- import { Kind, buildSchema, coerceInputValue, parse, print } from 'graphql';
2
- import { Tools } from './tools.mjs';
3
-
4
- const STRUCTURAL = ['SELF', 'STRANGER'];
5
-
6
- const CLASSES = [
7
- 'bindings',
8
- 'chain',
9
- 'config',
10
- 'data',
11
- 'handles',
12
- 'identity',
13
- 'keyring',
14
- 'program',
15
- 'refs',
16
- ];
17
-
18
- const OPEN = ['chain', 'keyring', 'program'];
19
-
20
- const CORE_ONLY = ['chain', 'identity', 'keyring', 'program'];
21
-
22
- const CORE = [
23
- ['chain/', 'chain'],
24
- ['keyring/', 'keyring'],
25
- ['refs/', 'refs'],
26
- ['handles/', 'handles'],
27
- ['bindings/', 'bindings'],
28
- ['identity/', 'identity'],
29
- ['config', 'config'],
30
- ['program', 'program'],
31
- ];
32
-
33
- const coreClass = (key) =>
34
- CORE.find(([mark]) => (mark.endsWith('/') ? key.startsWith(mark) : key === mark))?.[1];
35
-
36
- const declaredClass = (memory, key) => {
37
- const held = memory[key];
38
- if (held !== undefined) {
39
- return held.class;
40
- }
41
- const under = Object.keys(memory)
42
- .filter((one) => one.endsWith('/*'))
43
- .find((one) => key.startsWith(one.slice(0, -1)));
44
- return under === undefined ? undefined : memory[under].class;
45
- };
46
-
47
- const AMBIENT = [
48
- 'globalThis',
49
- 'process',
50
- 'require',
51
- 'import',
52
- 'eval',
53
- 'Function',
54
- 'fetch',
55
- 'XMLHttpRequest',
56
- 'WebAssembly',
57
- 'SharedArrayBuffer',
58
- 'Atomics',
59
- 'Buffer',
60
- 'crypto',
61
- 'Date',
62
- 'setTimeout',
63
- 'setInterval',
64
- 'queueMicrotask',
65
- ];
66
-
67
- const REACHES = [
68
- ['constructor', /\bconstructor\b/],
69
- ['__proto__', /\b__proto__\b/],
70
- ['prototype', /\bprototype\b/],
71
- ['Math.random', /\bMath\s*\.\s*random\b/],
72
- ];
73
-
74
- const FORBIDDEN = [
75
- ...AMBIENT.map((name) => [name, new RegExp(`(?<![.$\\w])${name}\\b(?!\\s*:)`)]),
76
- ...REACHES,
77
- ];
78
-
79
- const RESERVED = ['eval', 'import', '__proto__', 'prototype', 'Math.random'];
80
-
81
- const SHADOWED = FORBIDDEN.map(([name]) => name).filter((name) => !RESERVED.includes(name));
82
-
83
- const doorHolder = (one) =>
84
- one.kind === Kind.OBJECT_TYPE_DEFINITION || one.kind === Kind.OBJECT_TYPE_EXTENSION;
85
-
86
- const RIGHTS_DIRECTIVE = 'directive @rights(is: [Rights!]!) on FIELD_DEFINITION';
87
-
88
- const INVITES_DIRECTIVE = 'directive @invites(count: Int!, within: Int!) on ENUM_VALUE';
89
-
90
- const DEFAULT_RIGHTS = `enum Rights { SELF MEMBER STRANGER }`;
91
-
92
- const codeOnly = (source) => {
93
- const out = [];
94
- const modes = [{ depth: 0, kind: 'code' }];
95
- let at = 0;
96
- while (at < source.length) {
97
- const mode = modes[modes.length - 1];
98
- const one = source[at];
99
- if (mode.kind !== 'code') {
100
- if (one === '\\') {
101
- out.push(' ');
102
- at += 2;
103
- } else if (one === mode.mark) {
104
- modes.pop();
105
- out.push(' ');
106
- at += 1;
107
- } else if (mode.kind === 'template' && one === '$' && source[at + 1] === '{') {
108
- modes.push({ depth: 0, kind: 'code' });
109
- out.push(' ');
110
- at += 2;
111
- } else {
112
- out.push(one === '\n' ? '\n' : ' ');
113
- at += 1;
114
- }
115
- continue;
116
- }
117
- if (one === "'" || one === '"') {
118
- modes.push({ kind: 'quote', mark: one });
119
- } else if (one === '`') {
120
- modes.push({ kind: 'template', mark: '`' });
121
- } else if (one === '{') {
122
- mode.depth += 1;
123
- } else if (one === '}' && mode.depth === 0 && modes.length > 1) {
124
- modes.pop();
125
- out.push(' ');
126
- at += 1;
127
- continue;
128
- } else if (one === '}') {
129
- mode.depth -= 1;
130
- }
131
- out.push(one === "'" || one === '"' || one === '`' ? ' ' : one);
132
- at += 1;
133
- }
134
- return out.join('');
135
- };
136
-
137
- const reads = (source) => {
138
- const code = codeOnly(source);
139
- return FORBIDDEN.filter(([, mark]) => mark.test(code)).map(([name]) => name);
140
- };
141
-
142
- const evaluated = (source) => {
143
- const body = `"use strict"; return (${source});`;
144
- return new Function(...SHADOWED, body)(...SHADOWED.map(() => undefined));
145
- };
146
-
147
- const rightsOf = (field) => {
148
- const marked = field.directives?.find((one) => one.name.value === 'rights');
149
- const listed = marked?.arguments?.find((one) => one.name.value === 'is');
150
- return listed === undefined
151
- ? undefined
152
- : listed.value.values.map((one) => one.value ?? one.name?.value);
153
- };
154
-
155
- const whole = (one) => Number.isSafeInteger(one) && one >= 0;
156
-
157
- const ceilingOf = (value) => {
158
- const marked = value.directives?.find((one) => one.name.value === 'invites');
159
- if (marked === undefined) {
160
- return undefined;
161
- }
162
- const argOf = (name) => {
163
- const given = marked.arguments?.find((one) => one.name.value === name)?.value?.value;
164
- return given === undefined ? undefined : Number(given);
165
- };
166
- return { count: argOf('count'), within: argOf('within') };
167
- };
168
-
169
- const ceilingsIn = (document) =>
170
- new Map(
171
- document.definitions
172
- .filter((one) => one.kind === Kind.ENUM_TYPE_DEFINITION && one.name.value === 'Rights')
173
- .flatMap((one) => one.values ?? [])
174
- .map((value) => [value.name.value, ceilingOf(value)])
175
- .filter(([, ceiling]) => ceiling !== undefined)
176
- .map(([name, ceiling]) => [name, Object.freeze(ceiling)]),
177
- );
178
-
179
- const resolverSources = (resolvers) =>
180
- Object.keys(resolvers)
181
- .sort()
182
- .flatMap((type) =>
183
- Object.keys(resolvers[type])
184
- .sort()
185
- .map((field) => `${type}.${field}=${resolvers[type][field].toString()}`),
186
- );
187
-
188
- const merged = (base, added) =>
189
- Object.fromEntries(
190
- [...new Set([...Object.keys(base ?? {}), ...Object.keys(added ?? {})])].map((key) => [
191
- key,
192
- { ...(base ?? {})[key], ...(added ?? {})[key] },
193
- ]),
194
- );
195
-
196
- // Bare names, not Type.field pairs: X's "if create answers, it created"
197
- // makes Query.create and Mutation.create one reserved word.
198
- const doorsIn = (sdl) =>
199
- parse(sdl)
200
- .definitions.filter((one) => doorHolder(one) && ['Mutation', 'Query'].includes(one.name.value))
201
- .flatMap((one) => (one.fields ?? []).map((field) => field.name.value));
202
-
203
- const rootsIn = (document, schema) => {
204
- const roots = [
205
- schema.getQueryType()?.name,
206
- schema.getMutationType()?.name,
207
- schema.getSubscriptionType()?.name,
208
- ].filter((one) => one !== undefined);
209
- return document.definitions
210
- .filter((one) => doorHolder(one) && roots.includes(one.name.value))
211
- .flatMap((one) => one.fields ?? []);
212
- };
213
-
214
- // Names why Blueprint would refuse; Blueprint itself rules, and never reads
215
- // this. The bench binds the two: every cause named here is one it refuses.
216
- const faults = (source) => {
217
- const trespass = reads(source);
218
- if (trespass.length > 0) {
219
- return [`it reads ambient power no blueprint may hold: ${trespass.join(', ')} (VIII)`];
220
- }
221
- let written;
222
- try {
223
- written = evaluated(source);
224
- } catch (error) {
225
- return [`it is not an expression settling a blueprint object: ${error.message} (VIII)`];
226
- }
227
- if (written === null || typeof written !== 'object') {
228
- return ['it settles no blueprint object (VIII)'];
229
- }
230
- const declares = /enum\s+Rights\b/.test(written.interface) ? '' : DEFAULT_RIGHTS;
231
- const sdl = [RIGHTS_DIRECTIVE, INVITES_DIRECTIVE, declares, written.interface]
232
- .filter(Boolean)
233
- .join('\n');
234
- let document;
235
- let schema;
236
- try {
237
- document = parse(sdl);
238
- schema = buildSchema(sdl);
239
- } catch (error) {
240
- return [`its schema does not compile: ${error.message} (VIII)`];
241
- }
242
- const declaredNames = (schema.getType('Rights')?.getValues() ?? []).map((one) => one.name);
243
- const rights = [...new Set([...STRUCTURAL, ...declaredNames])];
244
- const found = [];
245
- if (!declares && STRUCTURAL.some((one) => !declaredNames.includes(one))) {
246
- found.push(
247
- `the Rights enum does not name ${STRUCTURAL.filter((one) => !declaredNames.includes(one)).join(', ')}, and every Rights enum names both SELF and STRANGER (VI)`,
248
- );
249
- }
250
- for (const [key, cell] of Object.entries(written.memory ?? {})) {
251
- if (coreClass(key) !== undefined) {
252
- found.push(`the cell ${key} is the core's own, and no program declares it (VIII)`);
253
- } else if (!CLASSES.includes(cell?.class)) {
254
- found.push(`the cell ${key} names no class the memory law holds: ${cell?.class} (VIII)`);
255
- }
256
- }
257
- const bare = [];
258
- const outside = new Map();
259
- for (const field of rootsIn(document, schema)) {
260
- const allowed = rightsOf(field);
261
- if (allowed === undefined) {
262
- bare.push(field.name.value);
263
- continue;
264
- }
265
- for (const held of allowed.filter((one) => !rights.includes(one))) {
266
- outside.set(held, [...(outside.get(held) ?? []), field.name.value]);
267
- }
268
- }
269
- if (bare.length > 0) {
270
- found.push(`the door ${bare.join(', ')} declares no rights, and every door declares them (VI)`);
271
- }
272
- for (const [right, ceiling] of ceilingsIn(document)) {
273
- if (STRUCTURAL.includes(right)) {
274
- found.push(
275
- `${right} carries an @invites ceiling, and a structural right is never invited into (VI)`,
276
- );
277
- } else if (!whole(ceiling.count) || !whole(ceiling.within)) {
278
- found.push(
279
- `${right}'s @invites ceiling is not two whole numbers of zero or more (VI): count ${ceiling.count}, within ${ceiling.within}`,
280
- );
281
- }
282
- }
283
- for (const [held, doors] of outside) {
284
- found.push(
285
- `the Rights enum does not name ${held}, which ${doors.length} door${doors.length === 1 ? '' : 's'} stand behind: ${doors.join(', ')} (VI)`,
286
- );
287
- }
288
- return found;
289
- };
290
-
291
- const composition = (baseSource, addedSource) => {
292
- const refused = (reason) => ({ refused: reason });
293
- try {
294
- const base = evaluated(baseSource);
295
- const added = evaluated(addedSource);
296
- if (added === null || typeof added !== 'object') {
297
- return refused('the part settles no blueprint object (VIII)');
298
- }
299
- const standard = doorsIn(base.interface);
300
- const collides = [...new Set(doorsIn(added.interface).filter((one) => standard.includes(one)))];
301
- if (collides.length > 0) {
302
- return refused(
303
- `the part declares the door ${collides.join(', ')}, which the administration already names — a standard door name is reserved on both roots (X)`,
304
- );
305
- }
306
- const kept = Object.keys(base.memory ?? {}).filter(
307
- (key) => (added.memory ?? {})[key] !== undefined,
308
- );
309
- if (kept.length > 0) {
310
- return refused(
311
- `the part declares the cell ${kept.join(', ')}, which the administration already keeps (X)`,
312
- );
313
- }
314
- if (added.needs?.config !== undefined && base.needs?.config !== undefined) {
315
- return refused(
316
- 'the part declares needs.config, and the program it composes with declares one too — a program holds one config (X)',
317
- );
318
- }
319
- const slots = [...(base.needs?.modules ?? []), ...(added.needs?.modules ?? [])];
320
- const twice = [
321
- ...new Set(
322
- slots.map((one) => one.module).filter((name, at, all) => all.indexOf(name) !== at),
323
- ),
324
- ];
325
- if (twice.length > 0) {
326
- return refused(
327
- `the part declares the module slot ${twice.join(', ')}, which the administration already holds (X)`,
328
- );
329
- }
330
- const cells = { ...(base.memory ?? {}), ...(added.memory ?? {}) };
331
- const needs = {
332
- ...(base.needs ?? {}),
333
- ...(added.needs ?? {}),
334
- caller: base.needs?.caller === true || added.needs?.caller === true,
335
- modules: slots,
336
- };
337
- const doors = merged(base.resolvers, added.resolvers);
338
- const written = Object.entries(doors)
339
- .map(
340
- ([type, fields]) =>
341
- `${type}: { ${Object.entries(fields)
342
- .map(([field, one]) => `${field}: ${one.toString()}`)
343
- .join(', ')} }`,
344
- )
345
- .join(', ');
346
- const source = `({
347
- name: ${JSON.stringify(added.name)},
348
- needs: ${JSON.stringify(needs)},
349
- memory: ${JSON.stringify(cells)},
350
- interface: ${JSON.stringify(`${base.interface}\n${added.interface}`)},
351
- resolvers: { ${written} },
352
- })`;
353
- const found = faults(source);
354
- return found.length > 0 ? refused(found.join('; ')) : { source };
355
- } catch (error) {
356
- return refused(`the part does not compose: ${error.message} (VIII)`);
357
- }
358
- };
359
-
360
- const composed = (baseSource, addedSource) => composition(baseSource, addedSource).source;
361
-
362
- const inputCheck = (schema, typeName, given) => {
363
- const type = schema.getType(typeName);
364
- if (type?.getFields === undefined) {
365
- return undefined;
366
- }
367
- let failed = false;
368
- const taken = coerceInputValue(given ?? {}, type, () => {
369
- failed = true;
370
- });
371
- return failed || taken === undefined
372
- ? undefined
373
- : Object.freeze(JSON.parse(JSON.stringify(taken)));
374
- };
375
-
376
- const Blueprint = (source) => {
377
- const trespass = reads(source);
378
- if (trespass.length > 0) {
379
- return { trespass };
380
- }
381
- try {
382
- const written = evaluated(source);
383
- const declares = /enum\s+Rights\b/.test(written.interface) ? '' : DEFAULT_RIGHTS;
384
- const sdl = [RIGHTS_DIRECTIVE, INVITES_DIRECTIVE, declares, written.interface]
385
- .filter(Boolean)
386
- .join('\n');
387
- const document = parse(sdl);
388
- const schema = buildSchema(sdl);
389
- const declaredNames = (schema.getType('Rights')?.getValues() ?? []).map((one) => one.name);
390
- if (!declares && STRUCTURAL.some((one) => !declaredNames.includes(one))) {
391
- return undefined;
392
- }
393
- const rights = [...new Set([...STRUCTURAL, ...declaredNames])];
394
-
395
- const admits = (typeName, fieldName, held) => {
396
- const field = document.definitions
397
- .filter((one) => doorHolder(one) && one.name.value === typeName)
398
- .flatMap((one) => one.fields ?? [])
399
- .find((one) => one.name.value === fieldName);
400
- const allowed = field === undefined ? undefined : rightsOf(field);
401
- return allowed === undefined ? true : allowed.includes(held);
402
- };
403
-
404
- const needs = written.needs ?? {};
405
- const modules = needs.modules ?? [];
406
- const memory = written.memory ?? {};
407
- const misdeclared = Object.entries(memory).filter(
408
- ([key, cell]) => !CLASSES.includes(cell?.class) || coreClass(key) !== undefined,
409
- );
410
- if (misdeclared.length > 0) {
411
- return undefined;
412
- }
413
- const undeclared = rootsIn(document, schema).filter((one) => {
414
- const allowed = rightsOf(one);
415
- return allowed === undefined || allowed.some((held) => !rights.includes(held));
416
- });
417
- if (undeclared.length > 0) {
418
- return undefined;
419
- }
420
- const ceilings = ceilingsIn(document);
421
- const misinvited = [...ceilings].filter(
422
- ([right, ceiling]) =>
423
- STRUCTURAL.includes(right) || !whole(ceiling.count) || !whole(ceiling.within),
424
- );
425
- if (misinvited.length > 0) {
426
- return undefined;
427
- }
428
-
429
- return Object.freeze({
430
- admits,
431
- invites: (held) => ceilings.get(held),
432
- bindings: (given) =>
433
- modules
434
- .filter((one) => one.binding !== undefined)
435
- .reduce((taken, one) => {
436
- if (taken === undefined) {
437
- return undefined;
438
- }
439
- const checked = inputCheck(schema, one.binding, given?.[one.module]);
440
- return checked === undefined ? undefined : { ...taken, [one.module]: checked };
441
- }, {}),
442
- classOf: (key) => coreClass(key) ?? declaredClass(memory, key),
443
- declares: (key) => declaredClass(memory, key),
444
- config: (given) =>
445
- needs.config === undefined ? Object.freeze({}) : inputCheck(schema, needs.config, given),
446
- digest: Tools.digest(
447
- Tools.canon({
448
- interface: sdl,
449
- memory,
450
- name: written.name,
451
- needs,
452
- resolvers: resolverSources(written.resolvers ?? {}),
453
- }),
454
- ),
455
- caller: needs.caller === true,
456
- memory: Object.freeze(memory),
457
- modules,
458
- name: written.name,
459
- resolvers: written.resolvers ?? {},
460
- rights,
461
- schema,
462
- // The directive definition stays: pointer.mjs runs buildSchema on this output.
463
- sdlFor: (held) => {
464
- const kept = document.definitions
465
- .map((one) =>
466
- doorHolder(one)
467
- ? {
468
- ...one,
469
- fields: (one.fields ?? []).filter((field) => {
470
- const allowed = rightsOf(field);
471
- return allowed === undefined || allowed.includes(held);
472
- }),
473
- }
474
- : one,
475
- )
476
- .filter((one) => !doorHolder(one) || one.fields.length > 0);
477
- const defined = new Set(
478
- kept
479
- .filter((one) => one.kind === Kind.OBJECT_TYPE_DEFINITION)
480
- .map((one) => one.name.value),
481
- );
482
- // A filtered view where only a part's extension survives must still
483
- // stand alone as a schema, or the right it serves opens nothing.
484
- return print({
485
- ...document,
486
- definitions: kept.map((one) =>
487
- one.kind === Kind.OBJECT_TYPE_EXTENSION && !defined.has(one.name.value)
488
- ? { ...one, kind: Kind.OBJECT_TYPE_DEFINITION }
489
- : one,
490
- ),
491
- });
492
- },
493
- trespass: [],
494
- });
495
- } catch {
496
- return undefined;
497
- }
498
- };
499
-
500
- export {
501
- Blueprint,
502
- CLASSES,
503
- CORE_ONLY,
504
- FORBIDDEN,
505
- OPEN,
506
- STRUCTURAL,
507
- composed,
508
- composition,
509
- coreClass,
510
- faults,
511
- reads,
512
- };
package/client.mjs DELETED
@@ -1,70 +0,0 @@
1
- import { Stamp, Tools } from './tools.mjs';
2
-
3
- const speaking = (born, at, succession) => {
4
- if (succession === undefined) {
5
- return born;
6
- }
7
- if (!Array.isArray(succession)) {
8
- return undefined;
9
- }
10
- let pk = born;
11
- for (const step of succession) {
12
- const next = typeof step?.next === 'string' ? Tools.pkOf(step.next) : null;
13
- if (next === null) {
14
- return undefined;
15
- }
16
- if (!Tools.verifies(pk, Tools.canon({ at, next: step.next, v: step.v }), step.sig)) {
17
- return undefined;
18
- }
19
- pk = next;
20
- }
21
- return pk;
22
- };
23
-
24
- const Client = ({ voice, deliver, resolve = (one) => one }) => {
25
- const stamp = Stamp();
26
- const lanes = new Map();
27
- const send = async (to, op) => {
28
- const at = resolve(to);
29
- const born = at === undefined ? null : Tools.pkOf(at);
30
- if (born === null) {
31
- return undefined;
32
- }
33
- const turn = (lanes.get(at) ?? Promise.resolve()).then(async () => {
34
- const envelope = await Tools.envelope(voice, at, op, stamp());
35
- const claim = Tools.digest(
36
- Tools.canon({ from: envelope.from, op: envelope.op, seq: envelope.seq, to: envelope.to }),
37
- );
38
- const reply = await deliver(envelope);
39
- const answering = speaking(born, at, reply?.succession);
40
- return reply?.body === undefined ||
41
- answering === undefined ||
42
- !Tools.verifies(answering, Tools.canon({ body: reply.body, claim }), reply.sig)
43
- ? undefined
44
- : reply;
45
- });
46
- const settled = turn.then(
47
- () => undefined,
48
- () => undefined,
49
- );
50
- lanes.set(at, settled);
51
- settled.then(() => {
52
- if (lanes.get(at) === settled) {
53
- lanes.delete(at);
54
- }
55
- });
56
- return turn;
57
- };
58
-
59
- return Object.freeze({
60
- address: Tools.address(voice.pk),
61
- ask: (to, source, variables) => send(to, { ask: { source, variables } }),
62
- describe: (to) => send(to, { describe: {} }),
63
- introspect: (to) => send(to, { introspect: {} }),
64
- pk: voice.pk,
65
- release: (to) => send(to, { release: {} }),
66
- send,
67
- });
68
- };
69
-
70
- export { Client, speaking };
package/contract.mjs DELETED
@@ -1,98 +0,0 @@
1
- import { buildSchema, isEnumType, isInputObjectType, specifiedScalarTypes } from 'graphql';
2
- import { Tools } from './tools.mjs';
3
-
4
- const argsAgree = (wanted, given) => {
5
- for (const arg of wanted.args ?? []) {
6
- const same = (given.args ?? []).find((one) => one.name === arg.name);
7
- if (same === undefined || String(same.type) !== String(arg.type)) {
8
- return false;
9
- }
10
- }
11
- return (given.args ?? [])
12
- .filter((one) => !(wanted.args ?? []).some((asked) => asked.name === one.name))
13
- .every((one) => !String(one.type).endsWith('!') || one.defaultValue !== undefined);
14
- };
15
-
16
- const Contract = (name, sdl) => {
17
- try {
18
- const schema = buildSchema(sdl);
19
- return Object.freeze({
20
- digest: Tools.digest(Tools.canon({ interface: sdl, name })),
21
- name,
22
- satisfies: (offered) => {
23
- const theirs = (() => {
24
- try {
25
- return buildSchema(offered);
26
- } catch {
27
- return undefined;
28
- }
29
- })();
30
- if (theirs === undefined) {
31
- return false;
32
- }
33
- for (const [typeName, wanted] of Object.entries(schema.getTypeMap())) {
34
- if (typeName.startsWith('__')) {
35
- continue;
36
- }
37
- const given = theirs.getTypeMap()[typeName];
38
- if (given === undefined && !specifiedScalarTypes.some((one) => one.name === typeName)) {
39
- return false;
40
- }
41
- if (isEnumType(wanted)) {
42
- if (
43
- !isEnumType(given) ||
44
- !wanted.getValues().every((one) => given.getValue(one.name) !== undefined)
45
- ) {
46
- return false;
47
- }
48
- continue;
49
- }
50
- if (wanted.getFields === undefined) {
51
- continue;
52
- }
53
- if (given?.getFields === undefined) {
54
- return false;
55
- }
56
- for (const [field, held] of Object.entries(wanted.getFields())) {
57
- const mine = given.getFields()[field];
58
- if (mine === undefined || String(mine.type) !== String(held.type)) {
59
- return false;
60
- }
61
- if (!argsAgree(held, mine)) {
62
- return false;
63
- }
64
- }
65
- if (isInputObjectType(wanted)) {
66
- const demanded = Object.entries(given.getFields())
67
- .filter(([field]) => wanted.getFields()[field] === undefined)
68
- .filter(
69
- ([, one]) => String(one.type).endsWith('!') && one.defaultValue === undefined,
70
- );
71
- if (demanded.length > 0) {
72
- return false;
73
- }
74
- }
75
- }
76
- return true;
77
- },
78
- sdl,
79
- });
80
- } catch {
81
- return undefined;
82
- }
83
- };
84
-
85
- const Contracts = (written) => {
86
- const held = Object.entries(written).map(([name, sdl]) => Contract(name, sdl));
87
- if (held.some((one) => one === undefined)) {
88
- return undefined;
89
- }
90
- const byName = new Map(held.map((one) => [one.name, one]));
91
- return Object.freeze({
92
- digests: () => Object.fromEntries(held.map((one) => [one.name, one.digest])),
93
- get: (name) => byName.get(name),
94
- has: (name) => byName.has(name),
95
- });
96
- };
97
-
98
- export { Contract, Contracts };