@lapxo/head 0.1.5 → 0.1.6
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 +6 -4
- package/TARGET.bound +58 -27
- package/dist/src/answers/answered.d.ts +4 -0
- package/dist/src/answers/answered.js +22 -0
- package/dist/src/compile/pin.js +2 -6
- package/dist/src/core/device.d.ts +1 -4
- package/dist/src/core/device.js +2 -5
- package/dist/src/flows/act.d.ts +1 -5
- package/dist/src/flows/act.js +1 -5
- package/dist/src/flows/call.d.ts +2 -9
- package/dist/src/flows/call.js +12 -12
- package/dist/src/flows/read.d.ts +1 -7
- package/dist/src/flows/read.js +2 -8
- package/dist/src/index.js +6 -1
- package/dist/src/links/inspect.d.ts +1 -0
- package/dist/src/links/inspect.js +3 -0
- package/dist/src/onboard/first.js +1 -1
- package/dist/src/places/admit.js +1 -1
- package/dist/src/places/found.js +5 -9
- package/dist/src/ports/adapters/request.d.ts +1 -0
- package/dist/src/ports/adapters/request.js +2 -2
- package/dist/src/ports/bound.d.ts +6 -8
- package/dist/src/ports/bound.js +13 -15
- package/dist/src/tools/look.js +5 -5
- package/dist/src/use/named.d.ts +12 -0
- package/dist/src/use/named.js +25 -0
- package/dist/src/verbs/ask.d.ts +1 -9
- package/dist/src/verbs/ask.js +2 -10
- package/dist/src/verbs/card.d.ts +2 -2
- package/dist/src/verbs/card.js +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Asked, Context } from '../core/answer.ts';
|
|
2
|
+
import type { Frame } from '../flows/call.ts';
|
|
3
|
+
/** Distinguish an empty response from missing transport bytes; only the latter leaves a named need. */
|
|
4
|
+
export declare const answering: (context: Context, place: string, asked: Asked, frame: Frame) => Frame;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { canonical } from '@lapxo/topos/wire';
|
|
2
|
+
import { valueOf } from "../core/lock.js";
|
|
3
|
+
import { rootIn } from "../flows/device.js";
|
|
4
|
+
/** A body that answers nothing: no byte of it, or a list with no member. */
|
|
5
|
+
const nothing = (bytes) => ((text) => text === '' || /^\[\s*\]$/.test(text))(new TextDecoder().decode(bytes).trim());
|
|
6
|
+
/** Distinguish an empty response from missing transport bytes; only the latter leaves a named need. */
|
|
7
|
+
export const answering = (context, place, asked, frame) => {
|
|
8
|
+
const need = `${valueOf(context.lock, 'place/needs')}/bytes/${frame.source}/${frame.operation}`, held = frame.lines.find((l) => l.scope === need && l.role === 'demands' && l.value !== 'withdraw');
|
|
9
|
+
const owe = (line) => ((root) => ('said' in root ? undefined : context.landRoot(place, [line], root.account)))(rootIn(context, place));
|
|
10
|
+
const withdrawn = ({ about: _about, sig: _sig, epoch: _epoch, ...l }) => canonical({ ...l, by: 'target', value: 'withdraw' });
|
|
11
|
+
return { ...frame,
|
|
12
|
+
typed: (status, bytes) => (nothing(bytes) ? Promise.resolve(frame.say('answered-nothing', { source: frame.source, operation: frame.operation, status, at: new Date(context.now()).toISOString() })) : frame.typed(status, bytes)),
|
|
13
|
+
send: async (attachment) => {
|
|
14
|
+
const got = await frame.send(attachment);
|
|
15
|
+
if (asked['ahead'] !== 'true' && got.kind === 'failed' && held === undefined)
|
|
16
|
+
await owe(canonical({ at: `policy:head/owed/${new Date(context.now()).toISOString()}`, by: 'target', form: 'alphabet', measure: 'id', role: 'demands', scope: need, value: frame.plan.origin }));
|
|
17
|
+
if (got.kind === 'got' && held !== undefined)
|
|
18
|
+
await owe(withdrawn(held));
|
|
19
|
+
return got;
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
};
|
package/dist/src/compile/pin.js
CHANGED
|
@@ -4,11 +4,7 @@ import { members, under, valueOf } from "../core/lock.js";
|
|
|
4
4
|
import { fieldsOf, vocabOf } from "../core/read.js";
|
|
5
5
|
import { sayIn } from "../core/surface.js";
|
|
6
6
|
import { digestIn, linesOf, pinnedOf } from "../core/worlds.js";
|
|
7
|
-
/**
|
|
8
|
-
* One compile, by the fold: the place view asked once, this source's lines kept with a region of its own — and the flow
|
|
9
|
-
* of each issuer the source declares, so a flow is pinned once, by whoever declares it — and the standing bound selects
|
|
10
|
-
* from them pinned and laid where its input names it.
|
|
11
|
-
*/
|
|
7
|
+
/** Pin the compiled source and adopt only the declared outputs. */
|
|
12
8
|
const compiledOf = async (context, place, source, file) => {
|
|
13
9
|
const say = sayIn(context.lock), v = vocabOf(context.lock);
|
|
14
10
|
const compiled = await context.world(place, valueOf(context.lock, 'place/view'));
|
|
@@ -53,7 +49,7 @@ export const pinIn = (context, place, lands, line) => async (source, read) => {
|
|
|
53
49
|
if ('said' in pinned || (old?.needs === file && old.value === pinned.pin))
|
|
54
50
|
return pinned;
|
|
55
51
|
const withdraw = old === undefined ? [] : [canonical({ ...Object.fromEntries(Object.entries(old).filter(([k]) => !['sig', 'epoch'].includes(k))), by: 'target', value: 'withdraw' })];
|
|
56
|
-
const landed = await lands([...withdraw, line(`uses/${source}`, 'digest', pinned.pin, { needs: file, at: `policy:
|
|
52
|
+
const landed = await lands([...withdraw, line(`uses/${source}`, 'digest', pinned.pin, { needs: file, at: `policy:person/connect/${new Date(context.now()).toISOString()}` })]);
|
|
57
53
|
return 'refused' in landed ? { said: [say('not-landed', { why: landed.refused })] } : pinned;
|
|
58
54
|
};
|
|
59
55
|
/**
|
|
@@ -5,10 +5,7 @@ export interface Device {
|
|
|
5
5
|
readonly signer: string;
|
|
6
6
|
}
|
|
7
7
|
export declare const deviceOf: (lock: readonly Said[], role?: string) => Device;
|
|
8
|
-
/**
|
|
9
|
-
* The drafted lines a place signs to admit a key: each device/<role>/<measure> line of head-s lock, said of keys/<id>, and
|
|
10
|
-
* its public half. The id is the role's own unless whoever admits names it; so is the coverage, unless they give it.
|
|
11
|
-
*/
|
|
8
|
+
/** Derive device authority from the admitted lock. */
|
|
12
9
|
export declare const admissionOf: (lock: readonly Said[], publicKey: string, role?: string, as?: {
|
|
13
10
|
readonly id?: string;
|
|
14
11
|
readonly coverage?: string;
|
package/dist/src/core/device.js
CHANGED
|
@@ -3,14 +3,11 @@ import { members, said, valueOf } from "./lock.js";
|
|
|
3
3
|
import { sayIn } from "./surface.js";
|
|
4
4
|
const HEAD = 'head';
|
|
5
5
|
export const deviceOf = (lock, role = HEAD) => ({ account: valueOf(lock, `device/${role}/account`), service: valueOf(lock, 'device/service'), signer: valueOf(lock, `device/${role}/signer`) });
|
|
6
|
-
/**
|
|
7
|
-
* The drafted lines a place signs to admit a key: each device/<role>/<measure> line of head-s lock, said of keys/<id>, and
|
|
8
|
-
* its public half. The id is the role's own unless whoever admits names it; so is the coverage, unless they give it.
|
|
9
|
-
*/
|
|
6
|
+
/** Derive device authority from the admitted lock. */
|
|
10
7
|
export const admissionOf = (lock, publicKey, role = HEAD, as = {}) => ((id, at) => [
|
|
11
8
|
...['class', 'coverage', 'resolution', 'signer'].flatMap((measure) => ((line) => (line === undefined ? [] : [canonical({ at, by: 'target', form: line.form ?? 'alphabet', measure, role: 'writes', scope: `keys/${id}`, value: measure === 'coverage' && as.coverage ? as.coverage : line.value ?? '' })]))(said(lock, `device/${role}/${measure}`))),
|
|
12
9
|
canonical({ at, by: 'target', form: 'alphabet', measure: 'public-key', role: 'writes', scope: `keys/${id}`, value: publicKey }),
|
|
13
|
-
])(as.id ?? valueOf(lock, `device/${role}/account`), as.at ?? 'policy:
|
|
10
|
+
])(as.id ?? valueOf(lock, `device/${role}/account`), as.at ?? 'policy:person/device');
|
|
14
11
|
export const algorithmOf = (lines) => ((era) => (lines.find((l) => l.scope === 'wire/signature-algorithms')?.value ?? '').split('|').find((one) => one.endsWith(`:${era}`)) ?? '')(valueOf(lines, 'wire/era'));
|
|
15
12
|
/** The id a place admits a public key under, as its own lines say: a key is who the place says it is, and a key it never admitted is no one there. */
|
|
16
13
|
export const idIn = (lines, publicKey) => publicKey === undefined ? undefined : lines.find((l) => l.scope?.startsWith('keys/') && l.measure === 'public-key' && l.value === publicKey)?.scope.slice('keys/'.length);
|
package/dist/src/flows/act.d.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
1
|
import type { Asked, Context } from '../core/answer.ts';
|
|
2
|
-
/**
|
|
3
|
-
* One offer that is not of the class head reads: within a cap the person signed it is called as far as the cap goes;
|
|
4
|
-
* without one, each call asks the person once, and their yes is signed for exactly that request. The intent lands before
|
|
5
|
-
* the call, the receipt after; a call that never answered stays open until a look of what it would have changed closes it.
|
|
6
|
-
*/
|
|
2
|
+
/** Run one action within its declared authority, with the person's approval when required. */
|
|
7
3
|
export declare const act: (asked: Asked, context: Context) => Promise<readonly string[]>;
|
package/dist/src/flows/act.js
CHANGED
|
@@ -4,11 +4,7 @@ import { draftOf } from "../core/receipt.js";
|
|
|
4
4
|
import { sayIn } from "../core/surface.js";
|
|
5
5
|
import { canonical } from '@lapxo/topos/wire';
|
|
6
6
|
import { called } from "./call.js";
|
|
7
|
-
/**
|
|
8
|
-
* One offer that is not of the class head reads: within a cap the person signed it is called as far as the cap goes;
|
|
9
|
-
* without one, each call asks the person once, and their yes is signed for exactly that request. The intent lands before
|
|
10
|
-
* the call, the receipt after; a call that never answered stays open until a look of what it would have changed closes it.
|
|
11
|
-
*/
|
|
7
|
+
/** Run one action within its declared authority, with the person's approval when required. */
|
|
12
8
|
export const act = (asked, context) => called(asked, context, planAct, async ({ word, say, lines, source, operation, prepared, typed, attach, send, lands, widen, uncovered }) => {
|
|
13
9
|
const [family, done] = [word('act/family'), word('act/done')];
|
|
14
10
|
const allowed = allowedOf(lines, word('allow/family'), source, operation);
|
package/dist/src/flows/call.d.ts
CHANGED
|
@@ -3,10 +3,7 @@ import type { Said } from '../core/lock.ts';
|
|
|
3
3
|
import type { Call, Plan } from '../core/read.ts';
|
|
4
4
|
import type { Attachment } from '../access/contract.ts';
|
|
5
5
|
import type { Fetched, Prepared } from '../ports/adapters/request.ts';
|
|
6
|
-
/**
|
|
7
|
-
* The origin of a source a person picks once for a place, when its document declares several: offered as a choice among
|
|
8
|
-
* them, none a default, and kept as the person's line; the lines a call stands on, with it.
|
|
9
|
-
*/
|
|
6
|
+
/** Ask for one declared origin when the source offers several. */
|
|
10
7
|
export declare const pickedOrigin: (context: Context, place: string, lines: readonly Said[], source: string, plan: Call | Plan) => Promise<{
|
|
11
8
|
readonly lines: readonly Said[];
|
|
12
9
|
} | {
|
|
@@ -40,9 +37,5 @@ export interface Frame {
|
|
|
40
37
|
readonly send: (attachment: Attachment | undefined) => Promise<Fetched>;
|
|
41
38
|
readonly uncovered: (role: string, scopes: readonly string[]) => readonly string[] | undefined;
|
|
42
39
|
}
|
|
43
|
-
/**
|
|
44
|
-
* The steps a read and an act share: the place, the lines it stands on once bound settled them, the offer's
|
|
45
|
-
* plan, the transport its origin names and the request it prepares; then what is particular, given a frame that says
|
|
46
|
-
* by the lock, attaches access when asked, types what came back and lands a line under head's key.
|
|
47
|
-
*/
|
|
40
|
+
/** Prepare one call, authorize its access and pass its frame to the read or action flow. */
|
|
48
41
|
export declare const called: (asked: Asked, context: Context, planOf: (lines: readonly Said[], lock: readonly Said[], source: string, operation: string, place: string) => Call | Plan, then: (frame: Frame) => Promise<readonly string[]>) => Promise<readonly string[]>;
|
package/dist/src/flows/call.js
CHANGED
|
@@ -5,13 +5,12 @@ import { sayIn } from "../core/surface.js";
|
|
|
5
5
|
import { valueOf } from "../core/lock.js";
|
|
6
6
|
import { uncoveredIn } from "../core/device.js";
|
|
7
7
|
import { widening } from "./device.js";
|
|
8
|
+
import { namedIn } from "../use/named.js";
|
|
9
|
+
import { answering } from "../answers/answered.js";
|
|
8
10
|
import { attachmentFor } from "../access/obtain.js";
|
|
9
11
|
import { typedAnswer } from "./answer.js";
|
|
10
12
|
import { standingFor } from "./world.js";
|
|
11
|
-
/**
|
|
12
|
-
* The origin of a source a person picks once for a place, when its document declares several: offered as a choice among
|
|
13
|
-
* them, none a default, and kept as the person's line; the lines a call stands on, with it.
|
|
14
|
-
*/
|
|
13
|
+
/** Ask for one declared origin when the source offers several. */
|
|
15
14
|
export const pickedOrigin = async (context, place, lines, source, plan) => {
|
|
16
15
|
if (plan.kind !== 'said' || plan.key !== 'pick-origin')
|
|
17
16
|
return { lines };
|
|
@@ -27,18 +26,18 @@ export const pickedOrigin = async (context, place, lines, source, plan) => {
|
|
|
27
26
|
const landed = await by.land([canonical(line(chosen))]);
|
|
28
27
|
return 'refused' in landed ? { said: [sayIn(context.lock)('not-landed', { why: landed.refused })] } : { lines: [...lines, line(chosen)] };
|
|
29
28
|
};
|
|
30
|
-
/**
|
|
31
|
-
* The steps a read and an act share: the place, the lines it stands on once bound settled them, the offer's
|
|
32
|
-
* plan, the transport its origin names and the request it prepares; then what is particular, given a frame that says
|
|
33
|
-
* by the lock, attaches access when asked, types what came back and lands a line under head's key.
|
|
34
|
-
*/
|
|
29
|
+
/** Prepare one call, authorize its access and pass its frame to the read or action flow. */
|
|
35
30
|
export const called = async (asked, context, planOf, then) => {
|
|
36
31
|
const say = (key, fields = {}) => [sayIn(context.lock)(key, fields)];
|
|
37
|
-
const
|
|
32
|
+
const source = asked['source'] ?? '';
|
|
38
33
|
return inPlace(asked, context, async (place) => {
|
|
39
34
|
const held = await standingFor(context, place);
|
|
40
35
|
if ('unsettled' in held)
|
|
41
36
|
return say('unsettled', { source, why: held.unsettled });
|
|
37
|
+
const named = await namedIn(context, held.lines, source, asked['operation'] ?? '', asked['ahead'] === 'true');
|
|
38
|
+
if ('said' in named)
|
|
39
|
+
return named.said;
|
|
40
|
+
const operation = named.operation;
|
|
42
41
|
const widen = async (message, lot) => {
|
|
43
42
|
const by = widening(context, place, held.lines, lot);
|
|
44
43
|
if ('said' in by)
|
|
@@ -65,9 +64,10 @@ export const called = async (asked, context, planOf, then) => {
|
|
|
65
64
|
return say(prepared.refused, { source, origin: plan.origin });
|
|
66
65
|
const most = (scope) => Number((lines.find((l) => l.scope === scope)?.value ?? '0..0').split('..')[1]);
|
|
67
66
|
const typed = async (status, bytes) => ((got, at) => ['lines' in got ? say('answered', { source, operation, status, at })[0] : say('untyped', { source, operation, status, at, why: got.untyped })[0], ...('lines' in got ? got.lines : [])])(await typedAnswer(context, place, lines, source, operation, prepared.key, bytes), new Date(context.now()).toISOString());
|
|
68
|
-
const lands = (...lot) => context.landAs(place, lot, 'head', lines);
|
|
69
67
|
const attach = async () => ((got) => ('said' in got ? { said: say(got.said, { source, operation, kinds: plan.access.kind, input: got.input }) } : got))(await attachmentFor(context, place, lines, plan));
|
|
70
68
|
const uncovered = (role, scopes) => uncoveredIn(context.lock, lines, context.device(role), place, [], scopes);
|
|
71
|
-
|
|
69
|
+
let use = named.use !== undefined && uncovered('head', [named.scope]) === undefined ? [named.use] : [];
|
|
70
|
+
const lands = (...lot) => { const once = use; use = []; return context.landAs(place, [...once, ...lot], 'head', lines).then((out) => ('lines' in out ? { lines: out.lines.filter((l) => !l.includes(` scope=${valueOf(context.lock, 'use/family')}/`)) } : out)); };
|
|
71
|
+
return then(answering(context, place, asked, { widen, word: (scope) => valueOf(context.lock, scope), say, place, lines, source, operation, plan, prepared, most, typed, attach, send: (attachment) => prepared.send(attachment, most('read/response-bytes')), lands, uncovered }));
|
|
72
72
|
});
|
|
73
73
|
};
|
package/dist/src/flows/read.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
1
|
import type { Asked, Context } from '../core/answer.ts';
|
|
2
|
-
/**
|
|
3
|
-
* A read of one offer the place declares of the class head reads: the response comes back once, typed by the world,
|
|
4
|
-
* and both are kept — the body's digest and the typed answer's, receipts the device key signs. Asked again as the same
|
|
5
|
-
* principal, while the place stands on what typed it, the person's grant holds now and the newest read is younger than
|
|
6
|
-
* the reuse the place allows, it answers from what was kept: nothing called. Asked for history, it shows what was kept
|
|
7
|
-
* of that request as of when it was read — evidence of that read, never a current answer — where the place allows it.
|
|
8
|
-
*/
|
|
2
|
+
/** Read one permitted offer, reuse only under its policy and land the signed receipt. */
|
|
9
3
|
export declare const readSource: (asked: Asked, context: Context) => Promise<readonly string[]>;
|
package/dist/src/flows/read.js
CHANGED
|
@@ -20,13 +20,7 @@ const closeWith = async (intent, source, digest, { word, lines, lands }, context
|
|
|
20
20
|
const landed = await lands(draftOf('', word('receipt/at'), `${intent}/${word('act/closed')}`, digest, new Date(context.now()).toISOString()));
|
|
21
21
|
return 'refused' in landed ? [say('not-landed', { why: landed.refused })] : [...landed.lines, say('closed', { intent, digest })];
|
|
22
22
|
};
|
|
23
|
-
/**
|
|
24
|
-
* A read of one offer the place declares of the class head reads: the response comes back once, typed by the world,
|
|
25
|
-
* and both are kept — the body's digest and the typed answer's, receipts the device key signs. Asked again as the same
|
|
26
|
-
* principal, while the place stands on what typed it, the person's grant holds now and the newest read is younger than
|
|
27
|
-
* the reuse the place allows, it answers from what was kept: nothing called. Asked for history, it shows what was kept
|
|
28
|
-
* of that request as of when it was read — evidence of that read, never a current answer — where the place allows it.
|
|
29
|
-
*/
|
|
23
|
+
/** Read one permitted offer, reuse only under its policy and land the signed receipt. */
|
|
30
24
|
export const readSource = (asked, context) => called(asked, context, planRead, async ({ word, say, place, lines, source, operation, plan, prepared, most, typed, attach, send, lands, widen, uncovered }) => {
|
|
31
25
|
const [family, at, when] = [word('receipt/family'), word('receipt/at'), new Date(context.now()).toISOString()];
|
|
32
26
|
const closing = (digest) => closeWith(asked['closes'], source, digest, { word, lines, lands }, context);
|
|
@@ -86,7 +80,7 @@ export const readSource = (asked, context) => called(asked, context, planRead, a
|
|
|
86
80
|
if (asked['ahead'] === 'true' && (got.kind !== 'got' || got.status >= 300))
|
|
87
81
|
return [];
|
|
88
82
|
if (got.kind !== 'got')
|
|
89
|
-
return say(got.kind === 'over' ? 'too-large' : 'unreachable-source', { source });
|
|
83
|
+
return say(got.kind === 'over' ? 'too-large' : 'unreachable-source', { source, why: got.kind === 'failed' ? got.why : '' });
|
|
90
84
|
if (got.status === 401 && attached.principal !== 'anonymous')
|
|
91
85
|
return refusedAccess(context, place, lines, plan, operation);
|
|
92
86
|
const digest = context.cache.keep(Buffer.concat([Buffer.from(`${got.status}\n`), got.bytes]));
|
package/dist/src/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import { parse } from '@lapxo/topos/wire';
|
|
|
17
17
|
import { aboutOf, valueOf } from "./core/lock.js";
|
|
18
18
|
import { fill, sayIn } from "./core/surface.js";
|
|
19
19
|
import { matchTemplate, refusedOn, shapeOf, shapeSchema, surfaceOf } from "./ports/surface.js";
|
|
20
|
-
import { closing, cooled, founded, land, landAct, limits, pinnedAt, policyAt, run, settledView, sign, standingAt, told, warm, words } from "./ports/bound.js";
|
|
20
|
+
import { birth, closing, cooled, founded, land, landAct, limits, pinnedAt, policyAt, run, settledView, sign, standingAt, told, warm, words } from "./ports/bound.js";
|
|
21
21
|
import { warmth } from "./ports/warm.js";
|
|
22
22
|
import { held, keep } from "./ports/cache.js";
|
|
23
23
|
import { registryFor } from "./ports/signers.js";
|
|
@@ -30,10 +30,15 @@ import { personOf } from "./ports/access/person.js";
|
|
|
30
30
|
import { readAhead } from "./ahead/read.js";
|
|
31
31
|
const [ext, root] = ((self) => [self.slice(self.lastIndexOf('.')), fileURLToPath(new URL(self.endsWith('.ts') ? '..' : '../..', self))])(import.meta.url);
|
|
32
32
|
const lock = lockAt(root);
|
|
33
|
+
if (process.argv[2] !== valueOf(lock, 'cli/run')) {
|
|
34
|
+
process.stdout.write(`${aboutOf(lock, 'cli/help')}\n`);
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
33
37
|
const surface = surfaceOf(lock);
|
|
34
38
|
const runs = valueOf(lock, 'runs');
|
|
35
39
|
Object.assign(words, Object.fromEntries(['read', 'view', 'sign', 'land', 'key', 'signer', 'pin', 'topos', 'standing', 'evidence', 'act', 'profile', 'receipts', 'refusal', 'identity'].map((w) => [w, valueOf(lock, `bound/${w}`)])));
|
|
36
40
|
Object.assign(limits, { wait: Number(valueOf(lock, 'bound/wait').split('..')[1]), most: Number(valueOf(lock, 'bound/printed').split('..')[1]) });
|
|
41
|
+
Object.assign(birth, { at: valueOf(lock, 'founding/person-at'), file: valueOf(lock, 'founding/birth-file') });
|
|
37
42
|
Object.assign(warmth, { spares: Number(valueOf(lock, 'bound/spares').split('..')[1]), boot: valueOf(lock, 'bound/spare-env').split('|') });
|
|
38
43
|
/** One call at a time, each after the one before it and never on a timer, so one asked while another lands sees its receipt; bound started ahead for it, and nothing of it kept once it is answered. */
|
|
39
44
|
let [last, ahead] = [Promise.resolve(), new AbortController()];
|
|
@@ -4,5 +4,6 @@ import { type Asked, type Context } from '../core/answer.ts';
|
|
|
4
4
|
* link's, and nothing shows who shared it — and what it names is shown; nothing is connected or granted, since a package
|
|
5
5
|
* is received by the digest its sharer sent apart. A place that admits no content transport is handed the lines that admit
|
|
6
6
|
* one, to sign; bytes that do not arrive are owed by the link until they do; bytes that are not what it names are refused.
|
|
7
|
+
* Asked why, it shows the transport it reads by: the profile's lines, laid at the place's birth by head, never the person's.
|
|
7
8
|
*/
|
|
8
9
|
export declare const linked: (asked: Asked, context: Context) => Promise<readonly string[]>;
|
|
@@ -10,11 +10,14 @@ import { rootIn } from "../flows/device.js";
|
|
|
10
10
|
* link's, and nothing shows who shared it — and what it names is shown; nothing is connected or granted, since a package
|
|
11
11
|
* is received by the digest its sharer sent apart. A place that admits no content transport is handed the lines that admit
|
|
12
12
|
* one, to sign; bytes that do not arrive are owed by the link until they do; bytes that are not what it names are refused.
|
|
13
|
+
* Asked why, it shows the transport it reads by: the profile's lines, laid at the place's birth by head, never the person's.
|
|
13
14
|
*/
|
|
14
15
|
export const linked = (asked, context) => inPlace(asked, context, async (place) => {
|
|
15
16
|
const [say, word, link] = [sayIn(context.lock), (k) => valueOf(context.lock, k), asked['document'] ?? ''], [held, admits] = [await standingOr(context, place, context.policy), members(valueOf(context.lock, 'link/transport'))];
|
|
16
17
|
if ('said' in held)
|
|
17
18
|
return held.said;
|
|
19
|
+
if (asked['why'] === 'true')
|
|
20
|
+
return [...held.lines.filter((l) => admits.includes(l.scope ?? '') || l.scope?.startsWith('transport/')).map((l) => canonical(l)), say('why-transport', { place })];
|
|
18
21
|
const lines = context.lock.filter((l) => admits.some((s) => l.scope === `birth/${s}`)).map(({ about: _about, scope, ...l }) => canonical({ ...l, scope: scope.slice('birth/'.length), at: 'policy:person/transport', by: 'target' }));
|
|
19
22
|
let reference;
|
|
20
23
|
try {
|
|
@@ -28,7 +28,7 @@ export const firstRun = async (asked, context, then) => {
|
|
|
28
28
|
const connected = await then(dir, true), unread = connected.some((l) => l.startsWith('said/head/document-unread ')), held = unread ? undefined : await context.policy(dir);
|
|
29
29
|
if (held !== undefined && ('refused' in held || !held.lines.some((l) => l.scope === `uses/${source}`)))
|
|
30
30
|
return [...born, ...connected];
|
|
31
|
-
const line = (scope, measure, value, role = 'writes') => canonical({ at: 'policy:
|
|
31
|
+
const line = (scope, measure, value, role = 'writes') => canonical({ at: 'policy:person/grant', by: 'target', form: 'alphabet', measure, role, scope, value });
|
|
32
32
|
const owed = unread ? [line(`${word('place/needs')}/bytes/${source}`, 'id', document, 'demands')] : [];
|
|
33
33
|
const landed = await context.landRoot(dir, [line(word('grant/family'), 'sources', source), line(word('grant/family'), 'until', until), ...owed], root.account);
|
|
34
34
|
if ('refused' in landed)
|
package/dist/src/places/admit.js
CHANGED
|
@@ -26,7 +26,7 @@ export const admitted = (asked, context) => inPlace(asked, context, async (place
|
|
|
26
26
|
const refused = await unless(context, say('elicit-admit', { name, place, coverage }));
|
|
27
27
|
if (refused)
|
|
28
28
|
return refused;
|
|
29
|
-
const lot = admissionOf(context.lock, key, 'head', { id: name, coverage, at: `policy:
|
|
29
|
+
const lot = admissionOf(context.lock, key, 'head', { id: name, coverage, at: `policy:person/admit/${new Date(context.now()).toISOString()}` });
|
|
30
30
|
const landed = await context.landRoot(place, lot, root.account);
|
|
31
31
|
return 'refused' in landed ? [say('not-landed', { why: landed.refused })] : [say('admitted', { name, place, coverage }), ...landed.lines];
|
|
32
32
|
});
|
package/dist/src/places/found.js
CHANGED
|
@@ -2,19 +2,15 @@ import { isAbsolute, basename } from 'node:path';
|
|
|
2
2
|
import { canonical, LOCK } from '@lapxo/topos/wire';
|
|
3
3
|
import { unless } from "../core/answer.js";
|
|
4
4
|
import { admissionOf } from "../core/device.js";
|
|
5
|
-
import { valueOf } from "../core/lock.js";
|
|
5
|
+
import { members, valueOf } from "../core/lock.js";
|
|
6
6
|
import { deviceKey } from "../flows/device.js";
|
|
7
7
|
import { walkable } from "../walk/context.js";
|
|
8
8
|
import { fill, sayIn } from "../core/surface.js";
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
*
|
|
12
|
-
* birth/ — the wire, the regions and views a world is read through, and the lifetime every reader is bounded by.
|
|
13
|
-
*/
|
|
14
|
-
const birthOf = (lock, root, head, person, place) => [
|
|
15
|
-
...[['class', 'authorize'], ['coverage', '*'], ['signer', valueOf(lock, 'device/head/signer')], ['public-key', root]].map(([measure, value]) => canonical({ at: 'policy:head/birth', by: 'target', form: 'alphabet', measure: measure, role: 'writes', scope: valueOf(lock, 'founding/root'), value: value })),
|
|
9
|
+
/** Found the place from the declared prelude; personal lines retain the person's provenance. */
|
|
10
|
+
const birthOf = (lock, root, head, person, place, mine = `${valueOf(lock, 'founding/person-at')}birth`) => [
|
|
11
|
+
...[['class', 'authorize'], ['coverage', '*'], ['signer', valueOf(lock, 'device/head/signer')], ['public-key', root]].map(([measure, value]) => canonical({ at: mine, by: 'target', form: 'alphabet', measure: measure, role: 'writes', scope: valueOf(lock, 'founding/root'), value: value })),
|
|
16
12
|
...admissionOf(lock, head, 'head'), ...admissionOf(lock, person, 'person'),
|
|
17
|
-
...lock.filter((l) => l.scope?.startsWith('birth/')).map(({ about: _about, scope, ...line }) => canonical({ ...line, value: fill(line.value ?? '', { place }), scope:
|
|
13
|
+
...lock.filter((l) => l.scope?.startsWith('birth/')).map(({ about: _about, scope, ...line }) => ((s) => canonical({ ...line, value: fill(line.value ?? '', { place }), scope: s, at: members(valueOf(lock, 'founding/person')).includes(s) ? mine : 'policy:head/birth', by: 'target' }))(scope.slice('birth/'.length))),
|
|
18
14
|
];
|
|
19
15
|
/** A place founded in a folder: a root key made in the keychain under the place's name — never a file — the birth lines written, and the first land signed by that root. */
|
|
20
16
|
const founded = async (context, dir, name) => {
|
|
@@ -19,8 +19,8 @@ const requestCapped = async (method, url, headers, body, most, wait, redirect =
|
|
|
19
19
|
}
|
|
20
20
|
return size > most ? { kind: 'over' } : { kind: 'got', status: response.status, bytes: Buffer.concat(chunks), type: response.headers.get('content-type') ?? '', ...((at) => (at ? { location: at } : {}))(response.headers.get('location')), ...((asks) => (asks ? { authenticate: asks } : {}))(response.headers.get('www-authenticate')) };
|
|
21
21
|
}
|
|
22
|
-
catch {
|
|
23
|
-
return { kind: 'failed' };
|
|
22
|
+
catch (error) {
|
|
23
|
+
return { kind: 'failed', why: String(error?.cause?.code ?? error?.name ?? '') };
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
26
|
const loopback = new Set(['127.0.0.1', 'localhost', '[::1]']);
|
|
@@ -17,20 +17,18 @@ export type Folded = {
|
|
|
17
17
|
} | {
|
|
18
18
|
readonly refused: string;
|
|
19
19
|
};
|
|
20
|
-
/**
|
|
21
|
-
* The lines a place stands on, from bound's public views alone: its folded normative standing (the lines view) — its
|
|
22
|
-
* policy, all that a reader of its pins, keys and views needs, read alone where nothing else is — and from
|
|
23
|
-
* its authenticated evidence what this device's own keys signed. Receipts are history, each kept as it was made; any other
|
|
24
|
-
* line of a device key, the person's own attestation, stands only while it is the one line of its scope and measure,
|
|
25
|
-
* unwithdrawn and beside no line of the place's own: anything else needs a fold bound does not publish, and is refused by
|
|
26
|
-
* name. A place whose land prints no act result is refused before anything lands in it. Any refusal of bound is no standing.
|
|
27
|
-
*/
|
|
20
|
+
/** Read normative standing plus authenticated device evidence; ambiguous device history refuses. */
|
|
28
21
|
export declare const policyAt: (name: string, place: string, signal?: AbortSignal) => Promise<Folded>;
|
|
29
22
|
export declare const standingAt: (name: string, place: string, devicesOf: (lines: readonly Said[]) => readonly string[], isHistory: (line: Said) => boolean, signal?: AbortSignal) => Promise<Folded>;
|
|
30
23
|
export declare const sign: (name: string, place: string, lot: readonly string[], key: string, signer: string, env: Readonly<Record<string, string>>, signal?: AbortSignal) => Promise<Outcome>;
|
|
31
24
|
/** A lot signed and landed in one process, its result read from what bound printed of the committed act: the records it admitted, never the receipt beside them; a place that declares no act result is refused by name. */
|
|
32
25
|
/** A place's receipts rendered beside its lock, so bound alone checks the place: nothing when they closed, else why they did not. */
|
|
33
26
|
export declare const closing: (name: string, place: string, signal?: AbortSignal) => Promise<string | undefined>;
|
|
27
|
+
/** A place's own lines in the person's words, beside its lock, the text they read and share: each line head lands at the person's at, drafted with no signature, gone with its withdraw. Bound reads the file as it reads the place, so it is written with the land, never while a fold runs. Set from head's lock. */
|
|
28
|
+
export declare const birth: {
|
|
29
|
+
at: string;
|
|
30
|
+
file: string;
|
|
31
|
+
}, birthed: (place: string, lot: readonly string[]) => void;
|
|
34
32
|
export declare const landAct: (name: string, place: string, lot: readonly string[], key: string, signer: string, env: Readonly<Record<string, string>>, signal?: AbortSignal) => Promise<Outcome>;
|
|
35
33
|
export declare const land: (name: string, place: string, signed: readonly string[], signal?: AbortSignal) => Promise<Outcome>;
|
|
36
34
|
export declare const told: (name: string, place: string, signed: readonly string[], view: string, signal?: AbortSignal) => Promise<readonly string[]>;
|
package/dist/src/ports/bound.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
|
-
import { parse } from '@lapxo/topos/wire';
|
|
5
|
+
import { canonical, LOCK, parse } from '@lapxo/topos/wire';
|
|
6
6
|
import { spawned } from "./process.js";
|
|
7
7
|
import { cooled, kept, warmed } from "./warm.js";
|
|
8
8
|
import { folded } from "../ahead/fold.js";
|
|
@@ -46,21 +46,13 @@ const nativeView = (name, place, view, signal) => {
|
|
|
46
46
|
};
|
|
47
47
|
return !words.identity ? read() : folded(`${name}/${view}`, place, async () => { const result = readingOf(await run(name, place, [words.read, words.view, `${words.identity}@${view}`], {}, signal), view); return 'refused' in result ? undefined : result.identity; }, read);
|
|
48
48
|
};
|
|
49
|
-
/**
|
|
50
|
-
* The lines a place stands on, from bound's public views alone: its folded normative standing (the lines view) — its
|
|
51
|
-
* policy, all that a reader of its pins, keys and views needs, read alone where nothing else is — and from
|
|
52
|
-
* its authenticated evidence what this device's own keys signed. Receipts are history, each kept as it was made; any other
|
|
53
|
-
* line of a device key, the person's own attestation, stands only while it is the one line of its scope and measure,
|
|
54
|
-
* unwithdrawn and beside no line of the place's own: anything else needs a fold bound does not publish, and is refused by
|
|
55
|
-
* name. A place whose land prints no act result is refused before anything lands in it. Any refusal of bound is no standing.
|
|
56
|
-
*/
|
|
49
|
+
/** Read normative standing plus authenticated device evidence; ambiguous device history refuses. */
|
|
57
50
|
export const policyAt = async (name, place, signal) => ((folded) => ('refused' in folded || folded.lines.some((line) => line.scope === words.profile) ? folded
|
|
58
51
|
: { refused: `REFUSE·act the place declares no ${words.profile}: land would print nothing of what it admits, so head lands nothing there; its root can land the local-act@1 profile and view/act` }))(await nativeView(name, place, words.standing, signal));
|
|
59
52
|
export const standingAt = async (name, place, devicesOf, isHistory, signal) => {
|
|
60
|
-
const folded = await policyAt(name, place, signal);
|
|
53
|
+
const [folded, evidence] = await Promise.all([policyAt(name, place, signal), nativeView(name, place, words.evidence, signal)]);
|
|
61
54
|
if ('refused' in folded)
|
|
62
55
|
return folded;
|
|
63
|
-
const evidence = await nativeView(name, place, words.evidence, signal);
|
|
64
56
|
if ('refused' in evidence)
|
|
65
57
|
return evidence;
|
|
66
58
|
const ids = new Set(devicesOf(folded.lines)), own = evidence.lines.filter((line) => ids.has(line.by ?? ''));
|
|
@@ -74,14 +66,20 @@ export const sign = async (name, place, lot, key, signer, env, signal) => ((got)
|
|
|
74
66
|
/** A lot signed and landed in one process, its result read from what bound printed of the committed act: the records it admitted, never the receipt beside them; a place that declares no act result is refused by name. */
|
|
75
67
|
/** A place's receipts rendered beside its lock, so bound alone checks the place: nothing when they closed, else why they did not. */
|
|
76
68
|
export const closing = async (name, place, signal) => ((got) => (got !== undefined && got.code === 0 ? undefined : why(got) ?? 'nothing printed'))(await run(name, place, [words.read, words.view, words.receipts], {}, signal));
|
|
69
|
+
/** A place's own lines in the person's words, beside its lock, the text they read and share: each line head lands at the person's at, drafted with no signature, gone with its withdraw. Bound reads the file as it reads the place, so it is written with the land, never while a fold runs. Set from head's lock. */
|
|
70
|
+
export const birth = { at: '', file: '' }, birthed = (place, lot) => {
|
|
71
|
+
const [file, mine] = [join(place, birth.file), facts(lot).filter((l) => birth.file && l['at']?.startsWith(birth.at))], bare = (l) => JSON.stringify(Object.entries(l).filter(([k]) => !['about', 'value', 'sig', 'epoch', 'by'].includes(k)).sort());
|
|
72
|
+
if (mine.length)
|
|
73
|
+
writeFileSync(file, [...(existsSync(file) ? facts(readFileSync(file, 'utf8').split('\n')) : []).filter((k) => !mine.some((m) => bare(m) === bare(k))), ...mine.filter((m) => m['value'] !== 'withdraw')].map(({ sig: _sig, epoch: _epoch, ...l }) => `${canonical({ ...l, by: 'target' })}\n`).join(''));
|
|
74
|
+
};
|
|
77
75
|
export const landAct = async (name, place, lot, key, signer, env, signal) => ((got) => {
|
|
78
76
|
if (got === undefined || got.code !== 0)
|
|
79
77
|
return { refused: why(got) ?? '' };
|
|
80
78
|
const at = got.out.findIndex((line) => line.startsWith(words.act));
|
|
81
79
|
return at < 0 ? { refused: `REFUSE·act bound printed no act result, and the place declares ${words.profile}: what was admitted is bound's to say` }
|
|
82
|
-
: { lines: got.out.slice(at + 1).filter((line) => parse(line).kind === 'fact').slice(0, -1) };
|
|
80
|
+
: (birthed(place, lot), { lines: got.out.slice(at + 1).filter((line) => parse(line).kind === 'fact').slice(0, -1) });
|
|
83
81
|
})(await run(name, place, [words.land, lotFile(lot), words.key, key, words.signer, signer], env, signal));
|
|
84
|
-
export const land = async (name, place, signed, signal) => ((got) => (got !== undefined && got.code === 0 ? { lines: signed } : { refused: why(got) ?? '' }))(await run(name, place, [words.land, lotFile(signed)], {}, signal));
|
|
82
|
+
export const land = async (name, place, signed, signal) => ((got) => (got !== undefined && got.code === 0 ? (birthed(place, signed), { lines: signed }) : { refused: why(got) ?? '' }))(await run(name, place, [words.land, lotFile(signed)], {}, signal));
|
|
85
83
|
export const told = async (name, place, signed, view, signal) => ((got) => [...(got?.out ?? []), ...(got?.err ?? [])])(await run(name, place, [words.read, words.view, view, lotFile(signed)], {}, signal));
|
|
86
84
|
export const pinnedAt = async (name, place, file, signal) => {
|
|
87
85
|
const printed = await run(name, place, [words.read, file], {}, signal);
|
|
@@ -92,6 +90,6 @@ export const pinnedAt = async (name, place, file, signal) => {
|
|
|
92
90
|
const bytes = printed.out.filter((line) => line.startsWith(words.topos)).map((line) => `${line.slice(words.topos.length)}\n`).join('');
|
|
93
91
|
return pin === undefined || !bytes ? undefined : { pin, bytes, lines: read.lines };
|
|
94
92
|
};
|
|
95
|
-
export const founded = async (name, place, key, signer, env, signal) => ((got) => (got !== undefined && got.code === 0 ? { lines: got.out } : { refused: why(got) ?? '' }))(await run(name, place, [words.land, words.key, key, words.signer, signer], env, signal));
|
|
93
|
+
export const founded = async (name, place, key, signer, env, signal) => ((got) => (got !== undefined && got.code === 0 ? (birthed(place, readFileSync(join(place, LOCK), 'utf8').split('\n')), { lines: got.out }) : { refused: why(got) ?? '' }))(await run(name, place, [words.land, words.key, key, words.signer, signer], env, signal));
|
|
96
94
|
export const words = { read: '', view: '', sign: '', land: '', key: '', signer: '', pin: '', topos: '', standing: '', evidence: '', act: '', profile: '', receipts: '', refusal: '', identity: '' };
|
|
97
95
|
export const settledView = async (name, place, view, signal) => readOf(await run(name, place, [words.read, words.view, view], {}, signal));
|
package/dist/src/tools/look.js
CHANGED
|
@@ -24,18 +24,18 @@ export const answer = (asked, context) => inPlace(asked, context, async (place)
|
|
|
24
24
|
if ('unsettled' in held)
|
|
25
25
|
return [sayIn(context.lock)('unsettled', { source, why: held.unsettled })];
|
|
26
26
|
const [v, first, shallow] = [vocabOf(context.lock), asked['at'] === '0', word('look/first').split('|')];
|
|
27
|
-
const region = `${v.offers}/${source}/${operation ?? ''
|
|
27
|
+
const [region, uses] = [`${v.offers}/${source}/`, held.lines.filter((l) => l.scope?.startsWith(`${word('use/family')}/${source}/`))], named = operation ? [operation] : uses.map((l) => l.value ?? '');
|
|
28
28
|
const shown = held.lines.filter((l) => {
|
|
29
29
|
const scope = l.scope ?? '';
|
|
30
|
-
if (scope === `${v.region}/${source}`)
|
|
30
|
+
if (scope === `${v.region}/${source}` || (!operation && uses.includes(l)))
|
|
31
31
|
return true;
|
|
32
32
|
if (scope.startsWith(`${v.keys}/${source}/`))
|
|
33
33
|
return !first;
|
|
34
|
-
if (!(
|
|
34
|
+
if (!named.some((op) => scope === `${region}${op}` || scope.startsWith(`${region}${op}/`)) || scope.endsWith(`/${v.carry}`))
|
|
35
35
|
return false;
|
|
36
36
|
return !first || (!scope.endsWith(`/${v.form}`) && shallow.includes(l.measure ?? ''));
|
|
37
|
-
});
|
|
38
|
-
return shown.length ? [...shown.map((l) => canonical(l)).sort(), ...(first ? [] : trustOf(context.lock, held.lines, source)), ...(operation ? await readSource(asked, context) : [])] : [sayIn(context.lock)('unread', { source })];
|
|
37
|
+
}), more = held.lines.filter((l) => l.measure === v.class && l.scope?.startsWith(region) && !named.includes(l.scope.slice(region.length))).length;
|
|
38
|
+
return shown.length ? [...shown.map((l) => canonical(l)).sort(), ...(first ? [] : trustOf(context.lock, held.lines, source)), ...(more ? [sayIn(context.lock)('more-offers', { source, more: String(more) })] : []), ...(operation ? await readSource(asked, context) : [])] : [sayIn(context.lock)('unread', { source })];
|
|
39
39
|
}
|
|
40
40
|
const view = `${asked['view'] ?? ''}${asked['at'] ? `${word('bound/at')}${asked['at']}` : ''}`;
|
|
41
41
|
return printedOr(context, await context.bound(place, [word('bound/read'), word('bound/view'), view]));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Context } from '../core/answer.ts';
|
|
2
|
+
import { type Said } from '../core/lock.ts';
|
|
3
|
+
/** The offers of a source a place holds: head's own registry of it, local, never sent to the model whole. */
|
|
4
|
+
export declare const offersOf: (lock: readonly Said[], lines: readonly Said[], source: string) => readonly string[];
|
|
5
|
+
/** Resolve the offer against the local registry. Ambiguity asks the person; read-ahead creates no use line. */
|
|
6
|
+
export declare const namedIn: (context: Context, lines: readonly Said[], source: string, asked: string, ahead: boolean) => Promise<{
|
|
7
|
+
readonly operation: string;
|
|
8
|
+
readonly use?: string;
|
|
9
|
+
readonly scope?: string;
|
|
10
|
+
} | {
|
|
11
|
+
readonly said: readonly string[];
|
|
12
|
+
}>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { canonical } from '@lapxo/topos/wire';
|
|
2
|
+
import { valueOf } from "../core/lock.js";
|
|
3
|
+
import { vocabOf } from "../core/read.js";
|
|
4
|
+
import { sayIn } from "../core/surface.js";
|
|
5
|
+
/** The words of a name as a person or a model says it: its runs of letters and digits, lowercase. */
|
|
6
|
+
const words = (text) => text.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
7
|
+
/** The offers of a source a place holds: head's own registry of it, local, never sent to the model whole. */
|
|
8
|
+
export const offersOf = (lock, lines, source) => ((at) => lines.filter((l) => l.measure === vocabOf(lock).class && l.scope?.startsWith(at)).map((l) => l.scope.slice(at.length)))(`${vocabOf(lock).offers}/${source}/`);
|
|
9
|
+
/** Resolve the offer against the local registry. Ambiguity asks the person; read-ahead creates no use line. */
|
|
10
|
+
export const namedIn = async (context, lines, source, asked, ahead) => {
|
|
11
|
+
const [v, word, say] = [vocabOf(context.lock), (k) => valueOf(context.lock, k), sayIn(context.lock)];
|
|
12
|
+
const [family, name] = [word('use/family'), words(asked).join('-')], uses = lines.filter((l) => l.scope?.startsWith(`${family}/${source}/`)), offers = offersOf(context.lock, lines, source);
|
|
13
|
+
const used = uses.find((l) => l.scope === `${family}/${source}/${name}`)?.value;
|
|
14
|
+
if (used !== undefined || ahead || !asked)
|
|
15
|
+
return { operation: used ?? asked };
|
|
16
|
+
const named = (op) => words([op, ...lines.filter((l) => l.scope === `${v.offers}/${source}/${op}/${v.carry}`).map((l) => l.value ?? '')].join(' '));
|
|
17
|
+
const found = offers.includes(asked) ? [asked] : offers.filter((op) => words(asked).length > 0 && words(asked).every((w) => named(op).includes(w)));
|
|
18
|
+
const operation = found.length > 1 ? (await context.person.choose(say('pick-offer', { source, asked }), [{ name: family, options: found.map((o) => ({ value: o, label: o })) }]))?.[family] : found[0];
|
|
19
|
+
if (!found.length)
|
|
20
|
+
return { operation: asked };
|
|
21
|
+
if (operation === undefined || !found.includes(operation))
|
|
22
|
+
return { said: [say('pick-offer', { source, asked })] };
|
|
23
|
+
const scope = `${family}/${source}/${name}`;
|
|
24
|
+
return uses.some((l) => l.value === operation) ? { operation } : { operation, scope, use: canonical({ at: `${word('founding/person-at')}use/${new Date(context.now()).toISOString()}`, by: 'target', form: 'alphabet', measure: 'operation', role: 'writes', scope, value: operation }) };
|
|
25
|
+
};
|
package/dist/src/verbs/ask.d.ts
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
1
|
import type { Answer } from '../core/answer.ts';
|
|
2
|
-
/**
|
|
3
|
-
* What a person asks, answered by the question it is. A view, or the region of a source or of one operation in it (what
|
|
4
|
-
* it offers, one read of it, why that answer stands, what was kept of it), is answered by look; drafted lines by plan;
|
|
5
|
-
* what is open by card; one question of several sources at once, by comparing what each said; anything else by places. Each form is read with no resolution unless the lock declares one for a
|
|
6
|
-
* person; asked why, at the one it declares to explain, and where it declares none, the same answer, said so. Nothing
|
|
7
|
-
* here writes one. An answer about one place ends with its card, taken off the fold the answer reads, valid from it.
|
|
8
|
-
* With no place at all, nothing is founded: it says so, and that connecting an API founds the person's place, the question
|
|
9
|
-
* carried along. A place still owing a source's bytes is answered only once they arrive.
|
|
10
|
-
*/
|
|
2
|
+
/** Route a question at the declared resolution, then attach the card of the place it read. */
|
|
11
3
|
export declare const asked: Answer;
|
package/dist/src/verbs/ask.js
CHANGED
|
@@ -7,17 +7,9 @@ import { paidIn } from "../onboard/owed.js";
|
|
|
7
7
|
import { pendingIn } from "../sources/refused.js";
|
|
8
8
|
import { compared } from "../marks/compare.js";
|
|
9
9
|
import { cardAt } from "./card.js";
|
|
10
|
-
/**
|
|
11
|
-
* What a person asks, answered by the question it is. A view, or the region of a source or of one operation in it (what
|
|
12
|
-
* it offers, one read of it, why that answer stands, what was kept of it), is answered by look; drafted lines by plan;
|
|
13
|
-
* what is open by card; one question of several sources at once, by comparing what each said; anything else by places. Each form is read with no resolution unless the lock declares one for a
|
|
14
|
-
* person; asked why, at the one it declares to explain, and where it declares none, the same answer, said so. Nothing
|
|
15
|
-
* here writes one. An answer about one place ends with its card, taken off the fold the answer reads, valid from it.
|
|
16
|
-
* With no place at all, nothing is founded: it says so, and that connecting an API founds the person's place, the question
|
|
17
|
-
* carried along. A place still owing a source's bytes is answered only once they arrive.
|
|
18
|
-
*/
|
|
10
|
+
/** Route a question at the declared resolution, then attach the card of the place it read. */
|
|
19
11
|
export const asked = async (asked, context) => {
|
|
20
|
-
const why = asked['why'] === 'true', at = (form) => valueOf(context.lock, `resolution/${form}/${why ? 'why' : 'person'}`)
|
|
12
|
+
const why = asked['why'] === 'true', at = (form) => ((v) => (v && v !== valueOf(context.lock, 'resolution/own') ? v : undefined))(valueOf(context.lock, `resolution/${form}/${why ? 'why' : 'person'}`));
|
|
21
13
|
const read = async (form, tool, more = {}) => [...(why && at(form) === undefined ? [sayIn(context.lock)('why-undeclared', { form })] : []),
|
|
22
14
|
...await (await answerOf('tools', tool))({ ...asked, ...more, at: at(form) }, context)];
|
|
23
15
|
const place = await placeOf(asked, context), founded = place !== undefined && context.files.has(place, LOCK), carded = asked['card'] === 'true';
|
package/dist/src/verbs/card.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Context } from '../core/answer.ts';
|
|
2
2
|
/**
|
|
3
|
-
* The card an act is made on: the place's standing as bound folds it, with the reads it kept
|
|
4
|
-
* device acts with. Anything landed, a grant withdrawn or a key admitted or changed moves it; a read never does. A folder
|
|
3
|
+
* The card an act is made on: the place's standing as bound folds it, with the reads it kept and the uses they named set
|
|
4
|
+
* aside, and the keys this device acts with. Anything landed, a grant withdrawn or a key admitted or changed moves it; a read never does. A folder
|
|
5
5
|
* that holds no place yet has the card of nothing.
|
|
6
6
|
*/
|
|
7
7
|
export declare const cardAt: (context: Context, place: string | undefined) => Promise<{
|
package/dist/src/verbs/card.js
CHANGED
|
@@ -3,8 +3,8 @@ import { LOCK } from '@lapxo/topos/wire';
|
|
|
3
3
|
import { baseOf, standingOr } from "../core/answer.js";
|
|
4
4
|
import { valueOf } from "../core/lock.js";
|
|
5
5
|
/**
|
|
6
|
-
* The card an act is made on: the place's standing as bound folds it, with the reads it kept
|
|
7
|
-
* device acts with. Anything landed, a grant withdrawn or a key admitted or changed moves it; a read never does. A folder
|
|
6
|
+
* The card an act is made on: the place's standing as bound folds it, with the reads it kept and the uses they named set
|
|
7
|
+
* aside, and the keys this device acts with. Anything landed, a grant withdrawn or a key admitted or changed moves it; a read never does. A folder
|
|
8
8
|
* that holds no place yet has the card of nothing.
|
|
9
9
|
*/
|
|
10
10
|
export const cardAt = async (context, place) => {
|
|
@@ -12,5 +12,5 @@ export const cardAt = async (context, place) => {
|
|
|
12
12
|
if ('said' in held)
|
|
13
13
|
return held;
|
|
14
14
|
const read = `${valueOf(context.lock, 'receipt/at')}:`, keys = ['head', 'person'].map((role) => context.device(role) ?? '');
|
|
15
|
-
return { card: createHash('sha256').update([baseOf(held.lines.filter((l) => !l.at?.startsWith(read))), ...keys].join('\n')).digest('hex').slice(0, 16) };
|
|
15
|
+
return { card: createHash('sha256').update([baseOf(held.lines.filter((l) => !l.at?.startsWith(read) && !l.scope?.startsWith(`${valueOf(context.lock, 'use/family')}/`))), ...keys].join('\n')).digest('hex').slice(0, 16) };
|
|
16
16
|
};
|
package/package.json
CHANGED