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/ground.mjs DELETED
@@ -1,382 +0,0 @@
1
- import { ADMIN } from './admin.mjs';
2
- import {
3
- Being,
4
- Compartment,
5
- Registry,
6
- SUCCESSION,
7
- make,
8
- rootFor,
9
- spend,
10
- successionOf,
11
- } from './being.mjs';
12
- import { Blueprint, composed, coreClass } from './blueprint.mjs';
13
- import { Client } from './client.mjs';
14
- import { Contracts } from './contract.mjs';
15
- import { Memory } from './modules.mjs';
16
- import { Mcp, Pointer } from './pointer.mjs';
17
- import { Program } from './program.mjs';
18
- import { Bytes, Stamp, Tools } from './tools.mjs';
19
- import { Voice } from './voice.mjs';
20
-
21
- const GROUND = 'Ground';
22
-
23
- const FLOOR = [
24
- 'blueprints',
25
- 'cells',
26
- 'cipher',
27
- 'contracts',
28
- 'keychain',
29
- 'moved',
30
- 'paths',
31
- 'watch',
32
- 'wire',
33
- ];
34
-
35
- const UNPROVEN = 'ground/unproven/';
36
-
37
- const HEIR = 'ground/heir';
38
-
39
- const commits = (cells, bundle) => {
40
- const boxPk = bundle?.heirBoxPk;
41
- try {
42
- if (typeof boxPk !== 'string' || Bytes.unbase64(boxPk).length !== 32) {
43
- return undefined;
44
- }
45
- } catch {
46
- return undefined;
47
- }
48
- cells.put(HEIR, { boxPk, nextPkHash: bundle.nextPkHash });
49
- return undefined;
50
- };
51
-
52
- const unprovenAt = (cells) => (at) => {
53
- try {
54
- const stood = cells.peek(`${UNPROVEN}${at}`);
55
- const seen = Date.now();
56
- cells.put(`${UNPROVEN}${at}`, {
57
- at,
58
- last: seen,
59
- seen: (stood?.seen ?? 0) + 1,
60
- since: stood?.since ?? seen,
61
- });
62
- } catch {
63
- return undefined;
64
- }
65
- };
66
-
67
- const unprovenRows = (cells) => cells.keys(UNPROVEN).map((row) => cells.peek(row));
68
-
69
- const install = ({ cells, keychain, program, bundle, cipher }) => {
70
- if (cells.peek('ground/admin') !== undefined || bundle?.next !== undefined) {
71
- return undefined;
72
- }
73
- const voice = Voice.revive(bundle ?? {});
74
- if (voice === undefined) {
75
- return undefined;
76
- }
77
- const memory = Memory(cells);
78
- const address = Tools.address(voice.pk);
79
- const sealed = keychain.seal(voice.pack());
80
- if (sealed === undefined) {
81
- return undefined;
82
- }
83
- make({ memory, voice, address, program, cipher });
84
- cells.put('ground/admin', { address, sealed });
85
- commits(cells, bundle);
86
- return address;
87
- };
88
-
89
- const succeeds = ({ adminMine, adminVoice, address, bundle, cells, cipher, keychain, memory }) => {
90
- if (bundle?.next !== undefined) {
91
- return undefined;
92
- }
93
- const heir = Voice.revive(bundle ?? {});
94
- const committed = memory.read(address, 'chain/nextPkHash');
95
- if (heir === undefined || committed?.value !== Tools.digest(heir.pk)) {
96
- return undefined;
97
- }
98
- const root = rootFor({ memory, voice: adminVoice, address, cipher });
99
- const sealed = root === undefined ? undefined : keychain.seal(heir.pack());
100
- if (sealed === undefined) {
101
- return undefined;
102
- }
103
- const rewrapped = adminMine.write(`keyring/${Tools.address(heir.pk)}`, {
104
- boxPk: Bytes.base64(heir.boxPk),
105
- wrapped: Voice.sealTo(heir.boxPk, Bytes.base64(root), cipher),
106
- });
107
- if (rewrapped === undefined) {
108
- return undefined;
109
- }
110
- const version = committed.version + 1;
111
- const certV = successionOf(memory, address).length + 1;
112
- const handover = { at: address, next: Tools.address(heir.pk), v: certV };
113
- const certified = adminMine.write(`${SUCCESSION}${certV}`, {
114
- ...handover,
115
- sig: adminVoice.sign(Tools.canon(handover)),
116
- });
117
- if (certified === undefined) {
118
- return undefined;
119
- }
120
- const claim = {
121
- address,
122
- nextPkHash: heir.nextPkHash,
123
- pk: Bytes.hex(heir.pk),
124
- version,
125
- };
126
- if (memory.replace({ ...claim, sig: heir.sign(Tools.canon(claim)) }) === undefined) {
127
- return undefined;
128
- }
129
- cells.put('ground/admin', { address, sealed });
130
- commits(cells, bundle);
131
- return Tools.address(heir.pk);
132
- };
133
-
134
- // Never a key in the modules object: everything there that is not in FLOOR
135
- // becomes a module handed to programs under that name.
136
- const Ground = (modules = {}, { span } = {}) => {
137
- const cells = modules.cells;
138
- const keychain = modules.keychain;
139
- const cipher = modules.cipher;
140
- if (
141
- cells === undefined ||
142
- keychain?.seal === undefined ||
143
- keychain?.unseal === undefined ||
144
- cipher?.seal === undefined ||
145
- cipher?.unseal === undefined
146
- ) {
147
- return undefined;
148
- }
149
-
150
- const written = modules.contracts ?? Contracts({});
151
- const extra = Object.fromEntries(
152
- Object.entries(modules).filter(([name]) => !FLOOR.includes(name)),
153
- );
154
- const bound = Object.entries(extra).filter(([, one]) => one?.at !== undefined);
155
- if (bound.some(([, one]) => !written.has(one.contract))) {
156
- return undefined;
157
- }
158
-
159
- const extension = modules.blueprints?.ground();
160
- const program = extension === undefined ? ADMIN : composed(ADMIN, extension);
161
- const blueprint = program === undefined ? undefined : Blueprint(program);
162
- if (blueprint?.digest === undefined) {
163
- return undefined;
164
- }
165
- const installed = cells.peek('ground/admin');
166
- if (installed === undefined) {
167
- return Object.freeze({
168
- init: (bundle) => install({ cells, keychain, program, bundle, cipher: modules.cipher }),
169
- });
170
- }
171
- const bundle = keychain.unseal(installed.sealed);
172
- const adminVoice = bundle === undefined ? undefined : Voice.revive(bundle);
173
- if (adminVoice === undefined) {
174
- return undefined;
175
- }
176
-
177
- // Never leaves this closure — nothing outside may be handed it.
178
- const vouched = Symbol('vouched');
179
-
180
- const watch = (fact) => {
181
- try {
182
- modules.watch?.(Object.freeze(fact));
183
- } catch {
184
- return undefined;
185
- }
186
- };
187
-
188
- const address = installed.address;
189
- const memory = Memory(cells, unprovenAt(cells), modules.moved);
190
- const paths = modules.paths ?? Object.freeze({ find: () => undefined, publish: () => undefined });
191
- const wire =
192
- modules.wire ??
193
- Object.freeze({
194
- carry: async (endpoint, envelope) =>
195
- typeof endpoint === 'function' ? endpoint(envelope) : undefined,
196
- });
197
-
198
- const stamp = Stamp();
199
- const boundFor = (voice) =>
200
- Object.fromEntries(
201
- bound.map(([name, one]) => [
202
- name,
203
- Pointer(written.get(one.contract).sdl, async (source, variables) =>
204
- deliver(await Tools.envelope(voice, one.at, { ask: { source, variables } }, stamp())),
205
- ),
206
- ]),
207
- );
208
- const introduced = (unscoped, address) =>
209
- Object.fromEntries(
210
- Object.entries(unscoped).map(([name, one]) => [
211
- name,
212
- typeof one?.scope === 'function' ? one.scope(address) : one,
213
- ]),
214
- );
215
- const declaredOnly = (obj, declared) =>
216
- Object.fromEntries(Object.entries(obj).filter(([name]) => declared.includes(name)));
217
- const modulesFor = (voice, declared = []) =>
218
- Object.freeze(
219
- introduced(
220
- {
221
- ...declaredOnly(extra, declared),
222
- ...declaredOnly(boundFor(voice), declared),
223
- memory,
224
- },
225
- Tools.address(voice.pk),
226
- ),
227
- );
228
- const offeredNames = new Set([...Object.keys(extra), ...bound.map(([name]) => name), 'memory']);
229
- const adminDeclared = () =>
230
- (Program(memory.read(address, 'program')?.value)?.modules ?? blueprint.modules).map(
231
- (one) => one.module,
232
- );
233
-
234
- const adminMine = Compartment({
235
- memory,
236
- voice: adminVoice,
237
- address,
238
- root: rootFor({ memory, voice: adminVoice, address, cipher: modules.cipher }),
239
- classOf: (key) =>
240
- Program(memory.read(address, 'program')?.value)?.classOf(key) ?? coreClass(key),
241
- cipher: modules.cipher,
242
- });
243
-
244
- const receive = async (envelope, token) => {
245
- const at = envelope?.to;
246
- const door = at === address ? admin : registry.activate(at);
247
- if (door === undefined) {
248
- watch({
249
- from: envelope?.from,
250
- kind: envelope?.op === undefined ? undefined : Object.keys(envelope.op)[0],
251
- outcome: 'no-door',
252
- to: at,
253
- });
254
- return undefined;
255
- }
256
- return door(envelope, token);
257
- };
258
- const announce = (at) => paths.publish(at, receive);
259
- const forget = (at, voice) => {
260
- paths.publish(at, undefined);
261
- if (voice === undefined) {
262
- cells.drop(`${at}/`);
263
- modules.moved?.(at);
264
- return undefined;
265
- }
266
- return spend({ memory, voice, address: at });
267
- };
268
-
269
- const attest = async (name) => {
270
- const one = extra[name];
271
- if (one === undefined) {
272
- return false;
273
- }
274
- if (one.at === undefined) {
275
- return null;
276
- }
277
- const seen = await deliver(await Tools.envelope(adminVoice, one.at, { describe: {} }, stamp()));
278
- return seen?.body?.sdl === undefined
279
- ? false
280
- : written.get(one.contract).satisfies(seen.body.sdl);
281
- };
282
-
283
- let adminDoor;
284
- const buildAdmin = () =>
285
- Being({
286
- address,
287
- administration: true,
288
- cipher: modules.cipher,
289
- deliver: (envelope) => deliver(envelope),
290
- modules: modulesFor(adminVoice, adminDeclared()),
291
- registry,
292
- span,
293
- vouched,
294
- voice: adminVoice,
295
- watch,
296
- });
297
- const registry = Registry({
298
- memory,
299
- adminMine,
300
- address,
301
- cipher: modules.cipher,
302
- deliver: (envelope) => deliver(envelope),
303
- modules: offeredNames,
304
- modulesFor,
305
- announce,
306
- forget,
307
- span,
308
- vouched,
309
- attest,
310
- watch,
311
- extended: () => {
312
- adminDoor = undefined;
313
- },
314
- });
315
- const admin = (envelope, token) => {
316
- if (adminDoor === undefined) {
317
- adminDoor = buildAdmin();
318
- }
319
- return adminDoor(envelope, token);
320
- };
321
-
322
- announce(address);
323
- for (const key of adminMine.list('identity/custody/')) {
324
- announce(key.slice('identity/custody/'.length));
325
- }
326
-
327
- const held = (at) => at === address || adminMine.read(`identity/custody/${at}`) !== undefined;
328
- const deliver = async (envelope) =>
329
- held(envelope.to) ? receive(envelope, vouched) : wire.carry(paths.find(envelope.to), envelope);
330
-
331
- // Client edge only — a resolver must never be handed this.
332
- const resolve = (one) => (one === GROUND ? address : (adminMine.read(`aliases/${one}`) ?? one));
333
-
334
- return Object.freeze({
335
- address,
336
- receive,
337
- rotate: (bundle) =>
338
- succeeds({
339
- adminMine,
340
- adminVoice,
341
- address,
342
- bundle,
343
- cells,
344
- cipher: modules.cipher,
345
- keychain,
346
- memory,
347
- }),
348
- serve: (voice) => {
349
- const client = Client({ voice, deliver, resolve });
350
- const sdlOf = async (at) => (await client.describe(at))?.body?.sdl;
351
- const asker = (at) => (source, variables) => client.ask(at, source, variables);
352
- return Object.freeze({
353
- address: client.address,
354
- client,
355
- gql: (at) =>
356
- Object.freeze({
357
- ask: asker(at),
358
- introspect: () => client.introspect(at),
359
- sdl: () => sdlOf(at),
360
- }),
361
- mcp: async (at) => {
362
- const sdl = await sdlOf(at);
363
- return sdl === undefined ? undefined : Mcp(sdl, asker(at));
364
- },
365
- pointer: async (at) => {
366
- const sdl = await sdlOf(at);
367
- return sdl === undefined ? undefined : Pointer(sdl, asker(at));
368
- },
369
- });
370
- },
371
- seated: (at, from) => {
372
- if (at === from) {
373
- return true;
374
- }
375
- const row = held(at) ? memory.read(at, `refs/${from}`)?.value : undefined;
376
- return row !== undefined && row !== null;
377
- },
378
- unproven: () => unprovenRows(cells),
379
- });
380
- };
381
-
382
- export { FLOOR, GROUND, Ground };
package/index.mjs DELETED
@@ -1,20 +0,0 @@
1
- export { ADMIN } from './admin.mjs';
2
- export {
3
- Blueprint,
4
- CLASSES,
5
- CORE_ONLY,
6
- OPEN,
7
- STRUCTURAL,
8
- composition,
9
- coreClass,
10
- faults,
11
- reads,
12
- } from './blueprint.mjs';
13
- export { Client, speaking } from './client.mjs';
14
- export { Contract, Contracts } from './contract.mjs';
15
- export { FLOOR, GROUND, Ground } from './ground.mjs';
16
- export { Memory } from './modules.mjs';
17
- export { Mcp, Pointer } from './pointer.mjs';
18
- export { Program } from './program.mjs';
19
- export { Bytes, MEASURE, SPAN, Stamp, Tools } from './tools.mjs';
20
- export { Voice } from './voice.mjs';
package/modules.mjs DELETED
@@ -1,55 +0,0 @@
1
- import { Bytes, Tools } from './tools.mjs';
2
-
3
- // The ground's own machinery over its cells. Every implementation a host
4
- // supplies — cells, keychain, cipher, paths, wire — belongs to that host's
5
- // floor and never to the core (X, Everything is a module).
6
- const Memory = (cells, unproven, moved) => {
7
- const held = (address, key) => cells.peek(`${address}/${key}`);
8
- const writerAt = (address) => {
9
- const chain = held(address, 'chain/pk');
10
- return chain === undefined ? Tools.pkOf(address) : Bytes.unhex(chain.value);
11
- };
12
- return Object.freeze({
13
- list: (address, prefix) =>
14
- cells.keys(`${address}/${prefix}`).map((cell) => cell.slice(address.length + 1)),
15
-
16
- read: (address, key) => held(address, key),
17
-
18
- replace: ({ address, pk, nextPkHash, sig }) => {
19
- const committed = held(address, 'chain/nextPkHash');
20
- if (committed === undefined || Tools.digest(Bytes.unhex(pk)) !== committed.value) {
21
- return undefined;
22
- }
23
- const version = committed.version + 1;
24
- const claim = { address, nextPkHash, pk, version };
25
- if (!Tools.verifies(Bytes.unhex(pk), Tools.canon(claim), sig)) {
26
- return undefined;
27
- }
28
- cells.put(`${address}/chain/pk`, { value: pk, version });
29
- cells.put(`${address}/chain/nextPkHash`, { value: nextPkHash, version });
30
- moved?.(address);
31
- return version;
32
- },
33
-
34
- write: ({ address, key, value, version, sig }) => {
35
- if (key.startsWith('chain/') && version !== 1) {
36
- return undefined;
37
- }
38
- const writer = writerAt(address);
39
- if (writer === null || version !== (held(address, key)?.version ?? 0) + 1) {
40
- return undefined;
41
- }
42
- if (!Tools.verifies(writer, Tools.canon({ address, key, value, version }), sig)) {
43
- unproven?.(address);
44
- return undefined;
45
- }
46
- cells.put(`${address}/${key}`, { value, version });
47
- if (!key.startsWith('refs/')) {
48
- moved?.(address);
49
- }
50
- return version;
51
- },
52
- });
53
- };
54
-
55
- export { Memory };
package/pointer.mjs DELETED
@@ -1,79 +0,0 @@
1
- import { buildSchema, getNamedType, isAbstractType, isObjectType } from 'graphql';
2
-
3
- const JSON_TYPE = { Boolean: 'boolean', Float: 'number', ID: 'string', Int: 'integer' };
4
-
5
- const selection = (type, depth) => {
6
- const named = getNamedType(type);
7
- if (!isObjectType(named) || depth === 0) {
8
- return '';
9
- }
10
- const fields = Object.values(named.getFields())
11
- .filter(
12
- (one) =>
13
- !one.args.some((arg) => String(arg.type).endsWith('!') && arg.defaultValue === undefined),
14
- )
15
- .filter((one) => !isAbstractType(getNamedType(one.type)))
16
- .filter((one) => depth > 1 || !isObjectType(getNamedType(one.type)))
17
- .map((one) => `${one.name}${selection(one.type, depth - 1)}`);
18
- return fields.length === 0 ? '' : ` { ${fields.join(' ')} }`;
19
- };
20
-
21
- const document = (kind, field, args) => {
22
- const used = field.args.filter((one) => args[one.name] !== undefined);
23
- const names = used.map((one) => `$${one.name}: ${one.type}`).join(', ');
24
- const pass = used.map((one) => `${one.name}: $${one.name}`).join(', ');
25
- return `${kind}${names ? `(${names})` : ''} { ${field.name}${
26
- pass ? `(${pass})` : ''
27
- }${selection(field.type, 3)} }`;
28
- };
29
-
30
- const roots = (schema) =>
31
- ['query', 'mutation'].flatMap((kind) => {
32
- const type = kind === 'query' ? schema.getQueryType() : schema.getMutationType();
33
- return type === undefined || type === null
34
- ? []
35
- : Object.values(type.getFields()).map((field) => ({ field, kind }));
36
- });
37
-
38
- const calls = (sdl, ask) => {
39
- const schema = buildSchema(sdl);
40
- return roots(schema).map(({ field, kind }) => ({
41
- field,
42
- kind,
43
- run: async (args = {}) => {
44
- const answered = await ask(document(kind, field, args), args);
45
- return answered?.body?.data?.[field.name];
46
- },
47
- }));
48
- };
49
-
50
- const Pointer = (sdl, ask) =>
51
- Object.freeze(Object.fromEntries(calls(sdl, ask).map((one) => [one.field.name, one.run])));
52
-
53
- const Mcp = (sdl, ask) => {
54
- const held = calls(sdl, ask);
55
- return Object.freeze({
56
- call: (name, args) => held.find((one) => one.field.name === name)?.run(args),
57
- tools: held.map(({ field, kind }) =>
58
- Object.freeze({
59
- annotations: { readOnlyHint: kind === 'query' },
60
- description: field.description ?? undefined,
61
- inputSchema: {
62
- properties: Object.fromEntries(
63
- field.args.map((one) => [
64
- one.name,
65
- { type: JSON_TYPE[getNamedType(one.type).name] ?? 'string' },
66
- ]),
67
- ),
68
- required: field.args
69
- .filter((one) => String(one.type).endsWith('!'))
70
- .map((one) => one.name),
71
- type: 'object',
72
- },
73
- name: field.name,
74
- }),
75
- ),
76
- });
77
- };
78
-
79
- export { Mcp, Pointer };
package/program.mjs DELETED
@@ -1,119 +0,0 @@
1
- import {
2
- buildSchema,
3
- defaultFieldResolver,
4
- getIntrospectionQuery,
5
- graphql,
6
- isObjectType,
7
- parse,
8
- validate,
9
- } from 'graphql';
10
- import { Blueprint } from './blueprint.mjs';
11
-
12
- const bound = (blueprint) => {
13
- const schema = blueprint.schema;
14
- for (const type of Object.values(schema.getTypeMap())) {
15
- if (!isObjectType(type) || type.name.startsWith('__')) {
16
- continue;
17
- }
18
- for (const [field, held] of Object.entries(type.getFields())) {
19
- const resolver = blueprint.resolvers[type.name]?.[field] ?? defaultFieldResolver;
20
- held.resolve = (source, args, context, info) =>
21
- blueprint.admits(type.name, field, context.rights)
22
- ? resolver(source, args, context, info)
23
- : null;
24
- }
25
- }
26
- return schema;
27
- };
28
-
29
- const Program = (source) => {
30
- const blueprint = Blueprint(source);
31
- if (blueprint === undefined || blueprint.trespass.length > 0) {
32
- return undefined;
33
- }
34
- const schema = bound(blueprint);
35
- const curtained = new Map();
36
- const seenBy = (held) => {
37
- if (!curtained.has(held)) {
38
- try {
39
- curtained.set(held, buildSchema(blueprint.sdlFor(held)));
40
- } catch {
41
- curtained.set(held, undefined);
42
- }
43
- }
44
- return curtained.get(held);
45
- };
46
- // graphql's validator demands a Query root even to judge a mutation, so an
47
- // all-mutation filtered world validates against a copy wearing an empty one
48
- // — never printed, never executed against.
49
- const askable = new Map();
50
- const askableBy = (held) => {
51
- if (!askable.has(held)) {
52
- const seen = seenBy(held);
53
- try {
54
- askable.set(
55
- held,
56
- seen === undefined || seen.getQueryType() != null
57
- ? seen
58
- : buildSchema(`${blueprint.sdlFor(held)}\ntype Query { _stands: Boolean }`),
59
- );
60
- } catch {
61
- askable.set(held, undefined);
62
- }
63
- }
64
- return askable.get(held);
65
- };
66
- return Object.freeze({
67
- ask: async ({ source: asked, variables, context }) => {
68
- const seen = askableBy(context.rights);
69
- if (seen === undefined) {
70
- return undefined;
71
- }
72
- try {
73
- if (validate(seen, parse(asked)).length > 0) {
74
- return undefined;
75
- }
76
- } catch {
77
- return undefined;
78
- }
79
- const answered = await graphql({
80
- contextValue: context,
81
- schema,
82
- source: asked,
83
- variableValues: variables,
84
- });
85
- return answered.errors === undefined ? { data: answered.data } : undefined;
86
- },
87
- bindings: blueprint.bindings,
88
- caller: blueprint.caller,
89
- classOf: blueprint.classOf,
90
- config: blueprint.config,
91
- declares: blueprint.declares,
92
- digest: blueprint.digest,
93
- invites: blueprint.invites,
94
- introspect: async (held) => {
95
- const filtered = seenBy(held);
96
- if (filtered === undefined) {
97
- return undefined;
98
- }
99
- const answered = await graphql({ schema: filtered, source: getIntrospectionQuery() });
100
- return answered.errors === undefined ? answered.data : undefined;
101
- },
102
- modules: blueprint.modules,
103
- name: blueprint.name,
104
- rights: blueprint.rights,
105
- sdlFor: blueprint.sdlFor,
106
- visible: (held) => {
107
- const filtered = seenBy(held);
108
- if (filtered === undefined) {
109
- return false;
110
- }
111
- const rooted = [filtered.getQueryType(), filtered.getMutationType()].filter(
112
- (one) => one !== undefined && one !== null,
113
- );
114
- return rooted.some((one) => Object.keys(one.getFields()).length > 0);
115
- },
116
- });
117
- };
118
-
119
- export { Program };