@lapxo/head 0.1.0 → 0.1.4

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.
Files changed (87) hide show
  1. package/README.md +24 -0
  2. package/TARGET.bound +517 -54
  3. package/dist/src/access/contract.d.ts +2 -0
  4. package/dist/src/access/obtain.d.ts +7 -1
  5. package/dist/src/access/obtain.js +6 -6
  6. package/dist/src/adopt/adopt.js +5 -2
  7. package/dist/src/compile/pin.js +5 -5
  8. package/dist/src/core/act.d.ts +1 -0
  9. package/dist/src/core/act.js +1 -0
  10. package/dist/src/core/answer.d.ts +18 -4
  11. package/dist/src/core/answer.js +7 -4
  12. package/dist/src/core/lock.d.ts +2 -0
  13. package/dist/src/core/lock.js +2 -0
  14. package/dist/src/core/package.d.ts +54 -0
  15. package/dist/src/core/package.js +65 -0
  16. package/dist/src/core/read.d.ts +3 -0
  17. package/dist/src/core/read.js +8 -4
  18. package/dist/src/core/surface.d.ts +1 -40
  19. package/dist/src/core/surface.js +4 -29
  20. package/dist/src/core/trust.d.ts +7 -0
  21. package/dist/src/core/trust.js +19 -0
  22. package/dist/src/flows/call.d.ts +1 -3
  23. package/dist/src/flows/call.js +3 -6
  24. package/dist/src/flows/device.js +1 -6
  25. package/dist/src/flows/read.js +24 -11
  26. package/dist/src/flows/world.js +3 -3
  27. package/dist/src/index.js +45 -21
  28. package/dist/src/onboard/first.d.ts +9 -0
  29. package/dist/src/onboard/first.js +38 -0
  30. package/dist/src/onboard/owed.d.ts +10 -0
  31. package/dist/src/onboard/owed.js +28 -0
  32. package/dist/src/onboard/site.d.ts +12 -0
  33. package/dist/src/onboard/site.js +22 -0
  34. package/dist/src/places/admit.js +1 -1
  35. package/dist/src/places/found.d.ts +1 -0
  36. package/dist/src/places/found.js +8 -7
  37. package/dist/src/ports/access/authorizer.d.ts +10 -0
  38. package/dist/src/ports/access/authorizer.js +34 -0
  39. package/dist/src/ports/access/flow.js +13 -3
  40. package/dist/src/ports/access/person.d.ts +3 -2
  41. package/dist/src/ports/access/person.js +12 -5
  42. package/dist/src/ports/adapters/request.d.ts +3 -0
  43. package/dist/src/ports/adapters/request.js +5 -3
  44. package/dist/src/ports/bound.d.ts +12 -2
  45. package/dist/src/ports/bound.js +30 -13
  46. package/dist/src/ports/documents.d.ts +34 -0
  47. package/dist/src/ports/documents.js +64 -0
  48. package/dist/src/ports/keychain.d.ts +2 -1
  49. package/dist/src/ports/keychain.js +3 -2
  50. package/dist/src/ports/place.d.ts +1 -0
  51. package/dist/src/ports/place.js +7 -1
  52. package/dist/src/ports/process.d.ts +9 -0
  53. package/dist/src/ports/process.js +5 -3
  54. package/dist/src/ports/spare.d.ts +1 -0
  55. package/dist/src/ports/spare.js +33 -0
  56. package/dist/src/ports/surface.d.ts +130 -0
  57. package/dist/src/ports/surface.js +50 -0
  58. package/dist/src/ports/warm.d.ts +19 -0
  59. package/dist/src/ports/warm.js +46 -0
  60. package/dist/src/signer.js +1 -1
  61. package/dist/src/sources/access.d.ts +10 -1
  62. package/dist/src/sources/access.js +15 -10
  63. package/dist/src/sources/connect.d.ts +13 -2
  64. package/dist/src/sources/connect.js +69 -38
  65. package/dist/src/sources/refused.d.ts +7 -0
  66. package/dist/src/sources/refused.js +16 -0
  67. package/dist/src/tools/ask.d.ts +1 -0
  68. package/dist/src/tools/ask.js +1 -0
  69. package/dist/src/tools/card.d.ts +1 -1
  70. package/dist/src/tools/card.js +5 -4
  71. package/dist/src/tools/do.d.ts +1 -0
  72. package/dist/src/tools/do.js +1 -0
  73. package/dist/src/tools/look.d.ts +5 -4
  74. package/dist/src/tools/look.js +15 -13
  75. package/dist/src/tools/places.d.ts +7 -0
  76. package/dist/src/tools/places.js +32 -0
  77. package/dist/src/tools/share.d.ts +1 -0
  78. package/dist/src/tools/share.js +1 -0
  79. package/dist/src/verbs/ask.d.ts +11 -0
  80. package/dist/src/verbs/ask.js +35 -0
  81. package/dist/src/verbs/card.d.ts +11 -0
  82. package/dist/src/verbs/card.js +16 -0
  83. package/dist/src/verbs/do.d.ts +8 -0
  84. package/dist/src/verbs/do.js +28 -0
  85. package/package.json +6 -5
  86. /package/dist/src/{core → ports}/keys.d.ts +0 -0
  87. /package/dist/src/{core → ports}/keys.js +0 -0
@@ -0,0 +1,50 @@
1
+ import { aboutOf, below, members, valueOf } from "../core/lock.js";
2
+ import { sayIn } from "../core/surface.js";
3
+ const nameOf = (line) => line.scope.split('/')[1];
4
+ const fieldsOf = (lock, scope) => ((required) => members(valueOf(lock, scope)).map((name) => ({
5
+ name, about: aboutOf(lock, `${scope}/${name}`), type: valueOf(lock, `${scope}/${name}`), required: required.includes(name)
6
+ })))(members(valueOf(lock, `${scope}/required`)));
7
+ /**
8
+ * Everything a client is offered, read off the lock alone, each with the words a model sees: the tools surface/tools
9
+ * names, out of the catalogue the tool lines are (every one of them where it names none), its resources and prompts.
10
+ */
11
+ export const surfaceOf = (lock) => ((catalogue, offered) => ({
12
+ name: valueOf(lock, 'name'), version: valueOf(lock, 'version'), catalogue, tools: offered.length ? catalogue.filter((t) => offered.includes(t.name)) : catalogue,
13
+ resources: below(lock, 'resource').map((line) => ({ name: nameOf(line), about: line.about ?? '', template: line.value ?? '', mime: valueOf(lock, `${line.scope}/mime`) })),
14
+ prompts: below(lock, 'prompt').map((line) => ({ name: nameOf(line), about: line.about ?? '', text: aboutOf(lock, `${line.scope}/text`), line: aboutOf(lock, `${line.scope}/line`), fields: fieldsOf(lock, line.scope) })),
15
+ }))(below(lock, 'tool').map((line) => ({ name: nameOf(line), about: line.about ?? '', fields: fieldsOf(lock, line.scope), hints: members(valueOf(lock, `${line.scope}/hints`)) })), members(valueOf(lock, 'surface/tools')));
16
+ export const refusedOn = (lock, platform) => ((supported) => (supported.includes(platform) ? undefined : sayIn(lock)('platform', { platform, supported: supported.join('|') })))(members(valueOf(lock, 'platform/supported')));
17
+ /**
18
+ * The fields a URI gives a resource template, or none when it is not one of its URIs: {name} matches one path step,
19
+ * {?a,b} names query fields any of which may be absent, in any order, each decoded.
20
+ */
21
+ export const matchTemplate = (template, uri) => {
22
+ const [path = '', query = ''] = template.split('{?');
23
+ const names = [...path.matchAll(/\{(\w+)\}/g)].map((m) => m[1]);
24
+ const pattern = new RegExp(`^${path.split(/\{\w+\}/).map((part) => part.replace(/[.*+?^$()|[\]\\]/g, '\\$&')).join('([^/?#]+)')}(?:\\?(.*))?$`);
25
+ const got = pattern.exec(uri);
26
+ if (got === null)
27
+ return undefined;
28
+ const params = new URLSearchParams(got[names.length + 1] ?? '');
29
+ return { ...Object.fromEntries(names.map((name, i) => [name, decodeURIComponent(got[i + 1])])),
30
+ ...Object.fromEntries(query.replace('}', '').split(',').filter(Boolean).flatMap((name) => ((v) => (v === null ? [] : [[name, v]]))(params.get(name)))) };
31
+ };
32
+ /** A said code as the cell head answers with: its state and next from the lock, the next's arguments read from the cell's own fields; a bound refusal a field carries keeps bound's code, head's own beside it. */
33
+ export const cellOf = (lock, key, given) => {
34
+ const fields = Object.fromEntries(Object.entries(given).filter((e) => e[1] !== undefined)), bound = Object.values(fields).map((v) => /^REFUSE·[\w-]+/.exec(v)?.[0]).find(Boolean);
35
+ const [tool, args] = [valueOf(lock, `said/${key}/next`), members(valueOf(lock, `said/${key}/next/args`)).map((a) => ((k, v) => [k, v.startsWith('$') ? fields[v.slice(1)] ?? '' : v])(a.split(':')[0], a.slice(a.indexOf(':') + 1)))];
36
+ return { code: bound ?? `head/${key}`, state: valueOf(lock, `said/${key}/state`), next: tool === 'none' ? { none: aboutOf(lock, `said/${key}/next`) } : { tool, args: Object.fromEntries(args) }, fields, ...(bound ? { head: `head/${key}` } : {}) };
37
+ };
38
+ export const cellLine = (cell) => [`said/${cell.code} state=${cell.state}`, ...('tool' in cell.next ? [`next=${cell.next.tool}`, ...Object.entries(cell.next.args).map(([k, v]) => `next.${k}=${JSON.stringify(v)}`)] : ['next=none', `next.why=${JSON.stringify(cell.next.none)}`]),
39
+ ...(cell.head ? [`head=${JSON.stringify(cell.head)}`] : []), ...Object.entries(cell.fields).map(([k, v]) => `${k}=${JSON.stringify(v)}`)].join(' ');
40
+ /** An answer as cells and the lines bound printed beside them, read back from what head said: the shape every tool result has. */
41
+ export const shapeOf = (answer) => ({ lines: answer.filter((l) => !l.startsWith('said/')),
42
+ cells: answer.filter((l) => l.startsWith('said/')).map((l) => ((code, rest) => ((t) => ({ code, state: t['state'] ?? '', next: t['next'] === 'none' || t['next'] === undefined ? { none: t['next.why'] ?? '' } : { tool: t['next'], args: Object.fromEntries(Object.entries(t).filter(([k]) => k.startsWith('next.')).map(([k, v]) => [k.slice(5), v])) },
43
+ fields: Object.fromEntries(Object.entries(t).filter(([k]) => !['state', 'next', 'head'].includes(k) && !k.startsWith('next.'))), ...(t['head'] ? { head: t['head'] } : {}) }))(Object.fromEntries([...rest.matchAll(/(\S+?)=("(?:[^"\\]|\\.)*"|\S+)/g)].map((m) => [m[1], m[2].startsWith('"') ? JSON.parse(m[2]) : m[2]]))))(l.slice('said/'.length, l.indexOf(' ')), l.slice(l.indexOf(' ') + 1))) });
44
+ /** The one output schema every tool declares: cells in the states the lock names, and the lines bound printed. */
45
+ export const shapeSchema = (lock) => ({ type: 'object', required: ['cells', 'lines'], properties: {
46
+ cells: { type: 'array', items: { type: 'object', required: ['code', 'state', 'next', 'fields'], properties: { code: { type: 'string' }, state: { enum: [...members(valueOf(lock, 'shape/states'))] }, head: { type: 'string' },
47
+ next: { oneOf: [{ type: 'object', required: ['tool', 'args'], properties: { tool: { type: 'string' }, args: { type: 'object', additionalProperties: { type: 'string' } } } }, { type: 'object', required: ['none'], properties: { none: { type: 'string' } } }] },
48
+ fields: { type: 'object', additionalProperties: { type: 'string' } } } } },
49
+ lines: { type: 'array', items: { type: 'string' } }
50
+ } });
@@ -0,0 +1,19 @@
1
+ import type { Bounds, Printed } from './process.ts';
2
+ /** How many processes head keeps started ahead of each program while it answers a call, and the variables node reads as it starts: a process started under other values of them is never asked. */
3
+ export declare const warmth: {
4
+ spares: number;
5
+ boot: readonly string[];
6
+ };
7
+ /** Keeps as many processes started ahead of a program as head's lock says; resolved when each has loaded what it runs. */
8
+ export declare const kept: (program: string) => Promise<unknown>;
9
+ /** Every process still waiting to be asked is stopped: head keeps none past the call it started them for. */
10
+ export declare const cooled: () => void;
11
+ /**
12
+ * A program run in a process started ahead under the same starting variables, handed the folder, arguments and
13
+ * variables a process started now would get, and watched within the same bounds; undefined when none is held ready,
14
+ * so the caller starts one as before and never waits on one still loading. Each process answers one ask and ends.
15
+ */
16
+ export declare const warmed: (program: string, args: readonly string[], at: {
17
+ readonly cwd: string;
18
+ readonly env: NodeJS.ProcessEnv;
19
+ }, bounds: Bounds) => Promise<Printed> | undefined;
@@ -0,0 +1,46 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { grouped, watched } from "./process.js";
3
+ /** How many processes head keeps started ahead of each program while it answers a call, and the variables node reads as it starts: a process started under other values of them is never asked. */
4
+ export const warmth = { spares: 0, boot: [] };
5
+ const spares = new Map();
6
+ const alive = (one) => one.child.exitCode === null && one.child.signalCode === null;
7
+ const entry = fileURLToPath(new URL(`./spare${import.meta.url.slice(import.meta.url.lastIndexOf('.'))}`, import.meta.url));
8
+ const bootOf = (env) => JSON.stringify(Object.entries(env).filter(([name]) => warmth.boot.some((p) => name.startsWith(p))).sort(([a], [b]) => (a < b ? -1 : 1)));
9
+ const stop = (one) => { try {
10
+ process.kill(-one.child.pid, 'SIGKILL');
11
+ }
12
+ catch {
13
+ one.child.kill('SIGKILL');
14
+ } };
15
+ const started = (program) => {
16
+ const env = { ...process.env }, child = grouped(process.execPath, [entry, program], { env }, 4);
17
+ child.stdin.on('error', () => undefined).end('');
18
+ const spare = { child, boot: bootOf(env), ready: new Promise((resolve) => { child.stdio[3].on('error', () => undefined).once('data', () => { spare.loaded = true; resolve(); }); child.once('close', () => resolve()); }), loaded: false };
19
+ return spare;
20
+ };
21
+ /** Keeps as many processes started ahead of a program as head's lock says; resolved when each has loaded what it runs. */
22
+ export const kept = (program) => {
23
+ const live = (spares.get(program) ?? []).filter(alive);
24
+ while (live.length < warmth.spares)
25
+ live.push(started(program));
26
+ spares.set(program, live);
27
+ return Promise.all(live.map((one) => one.ready));
28
+ };
29
+ /** Every process still waiting to be asked is stopped: head keeps none past the call it started them for. */
30
+ export const cooled = () => { for (const one of [...spares.values()].flat())
31
+ stop(one); spares.clear(); };
32
+ process.once('exit', cooled);
33
+ /**
34
+ * A program run in a process started ahead under the same starting variables, handed the folder, arguments and
35
+ * variables a process started now would get, and watched within the same bounds; undefined when none is held ready,
36
+ * so the caller starts one as before and never waits on one still loading. Each process answers one ask and ends.
37
+ */
38
+ export const warmed = (program, args, at, bounds) => {
39
+ const live = spares.get(program) ?? [], index = live.findIndex((one) => one.loaded && alive(one) && one.boot === bootOf(at.env));
40
+ const spare = index < 0 ? undefined : live.splice(index, 1)[0];
41
+ if (spare === undefined)
42
+ return undefined;
43
+ void kept(program);
44
+ spare.child.stdio[3].end(`${JSON.stringify({ cwd: at.cwd, args, env: at.env })}\n`);
45
+ return watched(spare.child, bounds);
46
+ };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from 'node:fs';
3
- import { signLot } from "./core/keys.js";
3
+ import { signLot } from "./ports/keys.js";
4
4
  import { keychain } from "./ports/keychain.js";
5
5
  const [service = '', account = '', key = '', algorithm = '', store = 'keychain'] = process.argv.slice(2);
6
6
  const secret = keychain(service, store).read(account);
@@ -1,8 +1,17 @@
1
1
  import type { Asked, Context } from '../core/answer.ts';
2
+ import type { Call } from '../core/read.ts';
3
+ import type { Attachment } from '../access/contract.ts';
2
4
  /**
3
5
  * Access to a source obtained ahead of any call, at the origin the person picked once where several are declared: the
4
6
  * way its document says access is proven, run by the adapter of
5
7
  * that kind — the person's own page, or the provider they are sent to with a client registered for the visit — its
6
- * secret kept in this keychain and its obtaining landed as a receipt the device key signs. Nothing is called.
8
+ * secret kept in this keychain and its obtaining landed as a receipt the device key signs. Nothing is called. What it
9
+ * attaches is what a fetch made as that source carries, to that source's own origin alone.
7
10
  */
8
11
  export declare const accessed: (asked: Asked, context: Context) => Promise<readonly string[]>;
12
+ export declare const attachedFor: (context: Context, place: string, source: string, asked?: string) => Promise<{
13
+ readonly plan: Call;
14
+ readonly attachment: Attachment | undefined;
15
+ } | {
16
+ readonly said: readonly string[];
17
+ }>;
@@ -10,25 +10,30 @@ import { standingFor } from "../flows/world.js";
10
10
  * Access to a source obtained ahead of any call, at the origin the person picked once where several are declared: the
11
11
  * way its document says access is proven, run by the adapter of
12
12
  * that kind — the person's own page, or the provider they are sent to with a client registered for the visit — its
13
- * secret kept in this keychain and its obtaining landed as a receipt the device key signs. Nothing is called.
13
+ * secret kept in this keychain and its obtaining landed as a receipt the device key signs. Nothing is called. What it
14
+ * attaches is what a fetch made as that source carries, to that source's own origin alone.
14
15
  */
15
16
  export const accessed = (asked, context) => inPlace(asked, context, async (place) => {
16
- const [say, v, source] = [sayIn(context.lock), vocabOf(context.lock), asked['source'] ?? ''];
17
+ const got = await attachedFor(context, place, asked['source'] ?? '', asked['operation']);
18
+ return 'said' in got ? got.said : [sayIn(context.lock)(got.plan.access.account === undefined ? 'access-anonymous' : 'access-kept', { source: got.plan.source })];
19
+ });
20
+ export const attachedFor = async (context, place, source, asked) => {
21
+ const [say, v] = [sayIn(context.lock), vocabOf(context.lock)];
17
22
  const held = await standingFor(context, place);
18
23
  if ('unsettled' in held)
19
- return [say('unsettled', { source, why: held.unsettled })];
20
- const operation = asked['operation'] || held.lines.filter((l) => l.measure === v.class && l.value === v.callable && l.scope?.startsWith(`${v.offers}/${source}/`)).map((l) => l.scope.split('/').slice(2).join('/')).sort()[0];
24
+ return { said: [say('unsettled', { source, why: held.unsettled })] };
25
+ const operation = asked || held.lines.filter((l) => l.measure === v.class && l.value === v.callable && l.scope?.startsWith(`${v.offers}/${source}/`)).map((l) => l.scope.split('/').slice(2).join('/')).sort()[0];
21
26
  if (operation === undefined)
22
- return [say('unread', { source })];
27
+ return { said: [say('unread', { source })] };
23
28
  const picked = await pickedOrigin(context, place, held.lines, source, planRead(held.lines, context.lock, source, operation, placeNameOf(place)));
24
29
  if ('said' in picked)
25
- return picked.said;
30
+ return picked;
26
31
  const plan = planRead(picked.lines, context.lock, source, operation, placeNameOf(place));
27
32
  if (plan.kind === 'said')
28
- return [say(plan.key, plan.fields)];
33
+ return { said: [say(plan.key, plan.fields)] };
29
34
  const stopped = uncoveredIn(context.lock, held.lines, context.device('head'), place, [], [`${valueOf(context.lock, 'access/family')}/${source}/${plan.access.scheme || v.anonymous}/obtained`]);
30
35
  if (stopped)
31
- return stopped;
36
+ return { said: stopped };
32
37
  const got = await attachmentFor(context, place, picked.lines, plan);
33
- return 'said' in got ? [say(got.said, { source, operation, kinds: plan.access.kind, input: got.input })] : [say(plan.access.account === undefined ? 'access-anonymous' : 'access-kept', { source })];
34
- });
38
+ return 'said' in got ? { said: [say(got.said, { source, operation, kinds: plan.access.kind, input: got.input })] } : { plan, attachment: got.attachment };
39
+ };
@@ -1,10 +1,21 @@
1
1
  import type { Asked, Context } from '../core/answer.ts';
2
+ import type { Read } from '../ports/documents.ts';
2
3
  /**
3
4
  * A source connected to a place, once the person said yes; a lock is no source, and adopting one as a parent is refused
4
5
  * by name, since composing two places' policies is bound's. A document — and an overlay beside it — is laid whatever its
5
6
  * kind, with every world head carries adopted, and the fold decides which reads it: what no world compiles is open, the
6
7
  * bytes taken back and the line that pays it handed on. What one does is compiled once per digest of what it read — the
7
8
  * same bytes again find their compiled standing by name — and pinned from the place's store: a new pin lands with the
8
- * old one's withdraw, said at its own time, since a withdrawn saying stays withdrawn.
9
+ * old one's withdraw, said at its own time, since a withdrawn saying stays withdrawn. A lock named by its digest is a
10
+ * package another place shared: checked whole, then — on one yes to all it names — each source connected in the order
11
+ * its pins rest in, its pin held to the package's; one that differs is refused by name, and the place holds what it
12
+ * compiled. Nothing the sharing place was granted comes with it: access is asked here, issuer by issuer.
9
13
  */
10
- export declare const connected: (asked: Asked, context: Context) => Promise<readonly string[]>;
14
+ export declare const connected: (asked: Asked, context: Context, covered?: boolean) => Promise<readonly string[]>;
15
+ type Connection = {
16
+ readonly said: readonly string[];
17
+ readonly connected: boolean;
18
+ };
19
+ type Laid = (source: string, document: string, doc: Read, overlay: Read | undefined, started: number) => Promise<Connection>;
20
+ export declare const laidIn: (context: Context, place: string, account: string) => Laid;
21
+ export {};
@@ -1,55 +1,83 @@
1
- import { extname, isAbsolute, resolve, sep } from 'node:path';
2
1
  import { canonical, EXTENSION } from '@lapxo/topos/wire';
3
- import { inPlace, unless } from "../core/answer.js";
2
+ import { inPlace, placeOf, unless } from "../core/answer.js";
4
3
  import { valueOf } from "../core/lock.js";
5
- import { vocabOf } from "../core/read.js";
4
+ import { fieldsOf, vocabOf } from "../core/read.js";
6
5
  import { redIn, sayIn } from "../core/surface.js";
7
- import { digestIn } from "../core/worlds.js";
6
+ import { digestIn, linesOf } from "../core/worlds.js";
7
+ import { coveredIn, owedIn, unpacked } from "../core/package.js";
8
8
  import { paidBy, pinIn } from "../compile/pin.js";
9
9
  import { adopted } from "../adopt/adopt.js";
10
+ import { firstRun } from "../onboard/first.js";
11
+ import { resolved } from "../onboard/site.js";
10
12
  import { rootIn } from "../flows/device.js";
11
- /** The kind a document is named by, never read for: its file's extension, its address's, or the media type it was served as; bytes that say none are octet-stream. */
12
- const kindOf = (at, type = '') => (extname(URL.canParse(at) ? new URL(at).pathname : at).slice(1)
13
- || (type.split(';')[0].split('/')[1] ?? '').split('+').at(-1).replace(/^x-/, '') || 'octet-stream').toLowerCase();
14
- /** A document the person names: an encrypted address, or a file inside the place or a folder the client shares; anything else is not read. */
15
- const documentOf = async (context, place, at) => {
16
- if (URL.canParse(at))
17
- return ((got) => got && { bytes: got.bytes, kind: kindOf(at, got.type) })(await (await context.adapter(at))?.get(at, Number(valueOf(context.lock, 'adapter/request/wait').split('..')[1])));
18
- const inside = [place, ...(await context.roots())].some((root) => resolve(at).startsWith(`${resolve(root)}${sep}`));
19
- return ((bytes) => bytes && { bytes, kind: kindOf(at) })(isAbsolute(at) && inside ? context.files.take(at) : undefined);
20
- };
13
+ import { attachedFor } from "./access.js";
21
14
  /**
22
15
  * A source connected to a place, once the person said yes; a lock is no source, and adopting one as a parent is refused
23
16
  * by name, since composing two places' policies is bound's. A document — and an overlay beside it — is laid whatever its
24
17
  * kind, with every world head carries adopted, and the fold decides which reads it: what no world compiles is open, the
25
18
  * bytes taken back and the line that pays it handed on. What one does is compiled once per digest of what it read — the
26
19
  * same bytes again find their compiled standing by name — and pinned from the place's store: a new pin lands with the
27
- * old one's withdraw, said at its own time, since a withdrawn saying stays withdrawn.
20
+ * old one's withdraw, said at its own time, since a withdrawn saying stays withdrawn. A lock named by its digest is a
21
+ * package another place shared: checked whole, then — on one yes to all it names — each source connected in the order
22
+ * its pins rest in, its pin held to the package's; one that differs is refused by name, and the place holds what it
23
+ * compiled. Nothing the sharing place was granted comes with it: access is asked here, issuer by issuer.
28
24
  */
29
- export const connected = (asked, context) => ((started) => inPlace(asked, context, async (place) => {
30
- const say = sayIn(context.lock), v = vocabOf(context.lock), word = (k) => valueOf(context.lock, k), digest = digestIn(context.lock);
31
- const [source, document] = [(asked['source'] ?? '').toLowerCase().replace(/[^a-z0-9-]+/g, '-'), asked['document'] ?? ''];
32
- if (!source)
33
- return [say('missing-param', { operation: 'connect', param: 'source' })];
34
- if (`.${kindOf(document)}` === EXTENSION)
35
- return [say('parent-lock', { place, parent: source })];
36
- const root = rootIn(context, place);
37
- if ('said' in root)
38
- return root.said;
39
- const refused = await unless(context, say('elicit-connect', { source, document, place, overlay: asked['overlay'] ?? '' }));
25
+ export const connected = async (asked, context, covered = false) => ((got) => ('said' in got ? got.said : connectedAs(got.asked, context, covered)))(await resolved(asked, context));
26
+ const connectedAs = async (asked, context, covered = false) => (asked['source'] && !(await placeOf(asked, context)) ? firstRun(asked, context, (dir, covered) => connected({ ...asked, place: dir }, context, covered))
27
+ : ((started) => inPlace(asked, context, async (place) => {
28
+ const say = sayIn(context.lock), digest = digestIn(context.lock), { documentOf, fetchedAs, kindOf } = context.files.read;
29
+ const [source, document] = [(asked['source'] ?? '').toLowerCase().replace(/[^a-z0-9-]+/g, '-'), asked['document'] ?? ''];
30
+ const [via, named, accept, lock] = [asked['via'] ?? '', asked['digest'] ?? '', /^[\w.+-]+\/[\w.+-]+$/.test(asked['accept'] ?? '') ? asked['accept'] : undefined, `.${kindOf(document)}` === EXTENSION];
31
+ if (lock && !named)
32
+ return [say('parent-lock', { place, parent: source || document })];
33
+ if (!source && !lock)
34
+ return [say('missing-param', { operation: 'connect', param: 'source' })];
35
+ const root = rootIn(context, place);
36
+ if ('said' in root)
37
+ return root.said;
38
+ if (named && !/^sha256:[0-9a-f]{64}$/.test(named))
39
+ return [say('document-digest', { document, digest: named, got: '' })];
40
+ if (lock)
41
+ return ((read) => (read === undefined ? [say('document-unread', { document })] : digest(read.bytes) !== named ? [say('document-digest', { document, digest: named, got: digest(read.bytes) })]
42
+ : received(context, place, document, read.bytes, named, laidIn(context, place, root.account), root.account)))(await documentOf(context, place, document, named));
43
+ const refused = covered ? undefined : await unless(context, say('elicit-connect', { source, document, place, overlay: asked['overlay'] ?? '' }));
44
+ if (refused)
45
+ return refused;
46
+ const got = via ? await attachedFor(context, place, via) : undefined;
47
+ if (got && 'said' in got)
48
+ return got.said;
49
+ const [read, overlay] = [got ? await fetchedAs(context, document, via, got, named, accept) : await documentOf(context, place, document, named), asked['overlay'] ? await documentOf(context, place, asked['overlay']) : undefined];
50
+ if (read !== undefined && 'said' in read)
51
+ return read.said;
52
+ if (read === undefined || (asked['overlay'] && overlay === undefined))
53
+ return [say('document-unread', { document })];
54
+ if (named && digest(read.bytes) !== named)
55
+ return [say('document-digest', { document, digest: named, got: digest(read.bytes) })];
56
+ return `.${read.kind}` === EXTENSION ? [say('parent-lock', { place, parent: source })] : (await laidIn(context, place, root.account)(source, document, read, overlay, started)).said;
57
+ }))(context.machine()));
58
+ const received = async (context, place, document, bytes, named, laid, account) => {
59
+ const [say, got] = [sayIn(context.lock), unpacked(context, document, bytes, await context.files.read.besideOf(context, document, linesOf(bytes)))];
60
+ if (!got.sources)
61
+ return got.said ?? [];
62
+ const [names, said, before] = [got.sources.map((s) => s.source).join(', '), [], await context.policy(place)];
63
+ const refused = coveredIn(context, 'refused' in before ? [] : before.lines, got.sources, named) ? undefined : await unless(context, say('elicit-receive', { place, package: named, sources: names || '—', open: (got.open ?? []).map((o) => o.source).join(', ') || '—' }));
40
64
  if (refused)
41
65
  return refused;
42
- const [doc, overlay] = [await documentOf(context, place, document), asked['overlay'] ? await documentOf(context, place, asked['overlay']) : undefined];
43
- if (doc === undefined || (asked['overlay'] && overlay === undefined))
44
- return [say('document-unread', { document })];
45
- const lands = (lot) => context.landRoot(place, lot, root.account);
66
+ for (const s of got.sources)
67
+ said.push(...(await laid(s.source, s.document.at, s.document, s.overlay, context.machine())).said);
68
+ const held = await context.policy(place), [pinOf, owed] = [(source) => ('refused' in held ? '' : held.lines.find((l) => l.scope === `uses/${source}`)?.value ?? ''), owedIn(context, 'refused' in held ? [] : held.lines, got.sources, got.open ?? [], named)];
69
+ const [off, landed] = [got.sources.filter((s) => pinOf(s.source) !== s.pin), owed.length ? await context.landRoot(place, owed, account) : undefined];
70
+ return [...said, ...(off.length ? off.map((s) => say('package-pin', { source: s.source, pin: s.pin, got: pinOf(s.source) || 'none' })) : names ? [say('received', { place, package: named, sources: names })] : []),
71
+ ...(got.open ?? []).map((o) => say('received-open', { source: o.source, waits: o.waits })), ...(landed && 'refused' in landed ? [say('not-landed', { why: landed.refused })] : [])];
72
+ };
73
+ export const laidIn = (context, place, account) => (source, document, doc, overlay, started) => (async () => {
74
+ const say = sayIn(context.lock), v = vocabOf(context.lock), word = (k) => valueOf(context.lock, k), digest = digestIn(context.lock);
75
+ const lands = (lot) => context.landRoot(place, lot, account);
46
76
  const line = (scope, measure, value, more = {}) => canonical({ at: 'policy:head/connect', by: 'target', form: 'alphabet', measure, role: 'writes', scope, value, ...more });
47
77
  const pin = pinIn(context, place, lands, line);
48
- if (`.${doc.kind}` === EXTENSION)
49
- return [say('parent-lock', { place, parent: source })];
50
78
  const worlds = await adopted(context, place, lands, line);
51
79
  if ('refused' in worlds)
52
- return [say('not-landed', { why: worlds.refused })];
80
+ return { said: [say('not-landed', { why: worlds.refused })], connected: false };
53
81
  const laid = [[`${word('place/connected')}/${source}.${doc.kind}`, doc.bytes], ...(overlay ? [[`${word('place/connected')}/${source}.overlay.${overlay.kind}`, overlay.bytes]] : [])];
54
82
  for (const [at, bytes] of laid)
55
83
  context.files.lay(place, at, bytes);
@@ -58,11 +86,14 @@ export const connected = (asked, context) => ((started) => inPlace(asked, contex
58
86
  if (pinned.open || pinned.dropped)
59
87
  for (const [at] of laid)
60
88
  context.files.drop(place, at);
61
- return pinned.open ? [say('no-world', { kind: doc.kind, document }), line(`${word('place/needs')}/${doc.kind}`, 'id', valueOf(context.lock, 'birth/view/place'), { role: 'demands' })] : pinned.said;
89
+ return { said: pinned.open ? [say('no-world', { kind: doc.kind, document }), line(`${word('place/needs')}/${doc.kind}`, 'id', valueOf(context.lock, 'birth/view/place'), { role: 'demands' })] : pinned.said, connected: false };
62
90
  }
91
+ const declared = fieldsOf(pinned.lines, `${v.region}/${source}`)[v.origins] ?? [], fetched = URL.canParse(document) && !declared.some((o) => URL.canParse(o)) && ((held) => 'lines' in held && !held.lines.some((l) => l.scope === `${v.pick}/${source}`))(await context.policy(place));
92
+ const picked = fetched ? await lands([line(`${v.pick}/${source}`, 'id', new URL(declared[0] ?? '/', document).href.replace(/\/$/, ''))]) : undefined;
63
93
  const [owed, paid] = [pinned.lines.filter((l) => l.measure === v.unresolved).map((l) => l.value ?? ''), await paidBy(context, place, pin, laid[0][0])];
64
94
  const classes = pinned.lines.filter((l) => l.measure === v.class && l.scope?.startsWith(`${v.offers}/${source}/`) && l.scope.split('/').length === 3);
65
- return [say('connected', { source, place, offers: String(classes.length), reads: String(classes.filter((l) => l.value === v.callable).length), pin: pinned.pin }),
66
- ...(owed.length ? [say('unresolved', { source, refs: owed.join(', ') })] : []), ...paid.flatMap((p) => ('said' in p.pinned ? p.pinned.said : [say('resolved', { source: p.source, by: source, pin: p.pinned.pin })])),
67
- ...pinned.lines.filter((l) => l.scope === `${v.region}/${source}` || (l.scope?.startsWith(`${v.keys}/${source}/`) && l.measure === v.scheme)).map((l) => canonical(l))];
68
- }).then((lines) => [...lines, ...redIn(context.lock)('connect/seconds', `connect/${asked['source'] ?? ''}`, started, context.machine())]))(context.machine());
95
+ return { connected: picked === undefined || !('refused' in picked), said: [say('connected', { source, place, offers: String(classes.length), reads: String(classes.filter((l) => l.value === v.callable).length), pin: pinned.pin }),
96
+ ...(owed.length ? [say('unresolved', { source, refs: owed.join(', ') })] : []), ...paid.flatMap((p) => ('said' in p.pinned ? p.pinned.said : [say('resolved', { source: p.source, by: source, pin: p.pinned.pin })])),
97
+ ...pinned.lines.filter((l) => l.scope === `${v.region}/${source}` || (l.scope?.startsWith(`${v.keys}/${source}/`) && l.measure === v.scheme)).map((l) => canonical(l)),
98
+ ...(picked === undefined ? [] : 'refused' in picked ? [say('not-landed', { why: picked.refused })] : picked.lines)] };
99
+ })().then((result) => ({ ...result, said: [...result.said, ...redIn(context.lock)('connect/seconds', `connect/${source}`, started, context.machine())] }));
@@ -0,0 +1,7 @@
1
+ import { type Context } from '../core/answer.ts';
2
+ import { type Said } from '../core/lock.ts';
3
+ import type { Call } from '../core/read.ts';
4
+ /** A read refused with the access head attached: its secret kept marked spent, never in a line, so the next call obtains it once; the refusal lands as a receipt without it. */
5
+ export declare const refusedAccess: (context: Context, place: string, lines: readonly Said[], plan: Call, operation: string) => Promise<readonly string[]>;
6
+ /** The access a place was refused and has not obtained since, by its newest receipt: what its card shows as open. */
7
+ export declare const pendingIn: (context: Context, place: string) => Promise<readonly string[]>;
@@ -0,0 +1,16 @@
1
+ import { canonical } from '@lapxo/topos/wire';
2
+ import { standingOr } from "../core/answer.js";
3
+ import { valueOf } from "../core/lock.js";
4
+ import { sayIn } from "../core/surface.js";
5
+ import { opened, sealed } from "../access/obtain.js";
6
+ /** A read refused with the access head attached: its secret kept marked spent, never in a line, so the next call obtains it once; the refusal lands as a receipt without it. */
7
+ export const refusedAccess = async (context, place, lines, plan, operation) => {
8
+ const [account, word] = [plan.access.account ?? '', (k) => valueOf(context.lock, k)], kept = opened(context.keys.read(account));
9
+ if (kept !== undefined)
10
+ context.keys.write(account, sealed({ ...kept, expires: 0 }));
11
+ const landed = await context.landAs(place, [canonical({ at: `${word('receipt/at')}:${new Date(context.now()).toISOString()}`, by: 'target', form: 'alphabet', measure: 'status', role: 'writes', scope: `${word('access/family')}/${plan.access.flow}/refused`, value: 'present' })], 'head', lines);
12
+ return [sayIn(context.lock)('access-refused', { source: plan.source, operation, place }), ...('lines' in landed ? landed.lines : [])];
13
+ };
14
+ /** The access a place was refused and has not obtained since, by its newest receipt: what its card shows as open. */
15
+ export const pendingIn = async (context, place) => ((held, family) => [...new Map(('said' in held ? [] : held.lines).filter((l) => l.scope?.startsWith(family) && /\/(refused|obtained|renewed|entered)$/.test(l.scope)).sort((a, b) => ((a.at ?? '') < (b.at ?? '') ? -1 : 1))
16
+ .map((l) => [l.scope.slice(family.length).replace(/\/[^/]+$/, ''), l.scope.endsWith('/refused')]))].filter(([, refused]) => refused).map(([flow]) => sayIn(context.lock)('access-pending', { source: flow.split('/')[0], flow, place })))(await standingOr(context, place), `${valueOf(context.lock, 'access/family')}/`);
@@ -0,0 +1 @@
1
+ export { asked as answer } from '../verbs/ask.ts';
@@ -0,0 +1 @@
1
+ export { asked as answer } from "../verbs/ask.js";
@@ -1,3 +1,3 @@
1
1
  import type { Answer } from '../core/answer.ts';
2
- /** What is open for a key: the cells view the place's standing declares, as bound folded it, narrowed to the key; a refused fold is said, never read as no view. */
2
+ /** What is open for a key: the cells view the place's standing declares, as bound folded it at the resolution asked, narrowed to the key; a refused fold is said, never read as no view. The standing is the one the card was read off, folded once a call. */
3
3
  export declare const answer: Answer;
@@ -1,6 +1,7 @@
1
1
  import { inPlace, printedOr, standingOr } from "../core/answer.js";
2
- import { aboutOf, members, valueOf } from "../core/lock.js";
3
- /** What is open for a key: the cells view the place's standing declares, as bound folded it, narrowed to the key; a refused fold is said, never read as no view. */
2
+ import { members, valueOf } from "../core/lock.js";
3
+ import { sayIn } from "../core/surface.js";
4
+ /** What is open for a key: the cells view the place's standing declares, as bound folded it at the resolution asked, narrowed to the key; a refused fold is said, never read as no view. The standing is the one the card was read off, folded once a call. */
4
5
  export const answer = (asked, context) => inPlace(asked, context, async (place) => {
5
6
  const word = (scope) => valueOf(context.lock, `bound/${scope}`);
6
7
  const held = await standingOr(context, place);
@@ -8,6 +9,6 @@ export const answer = (asked, context) => inPlace(asked, context, async (place)
8
9
  return held.said;
9
10
  const view = held.lines.find((f) => f.scope?.startsWith('view/') && !f.shape && members(f.value ?? '').includes(word('cells')));
10
11
  if (view === undefined)
11
- return [aboutOf(context.lock, 'said/no-cells')];
12
- return printedOr(context, await context.bound(place, [word('read'), word('view'), view.scope.split('/')[1], ...(asked['key'] ? [word('key'), asked['key']] : [])]));
12
+ return [sayIn(context.lock)('no-cells', { place })];
13
+ return printedOr(context, await context.bound(place, [word('read'), word('view'), `${view.scope.split('/')[1]}${asked['at'] ? `${word('at')}${asked['at']}` : ''}`, ...(asked['key'] ? [word('key'), asked['key']] : [])]));
13
14
  });
@@ -0,0 +1 @@
1
+ export { done as answer } from '../verbs/do.ts';
@@ -0,0 +1 @@
1
+ export { done as answer } from "../verbs/do.js";
@@ -1,8 +1,9 @@
1
1
  import type { Answer } from '../core/answer.ts';
2
2
  /**
3
- * One view of a place, folded by bound at the resolution asked; or, of a source, what it offers as typed lines — at the
4
- * first resolution where it is reached and each offer's class, by default its forms and how access is proven too, never
5
- * what only a transport reads — and, at the call resolution, one read of an offer of the class head reads. Each answer
6
- * is measured against answer/seconds and reads red past it, with the question it answered as its coordinate.
3
+ * A view of a place, folded by bound at the resolution asked; or the region of a source, or of one operation in it, as
4
+ * typed lines at the resolution asked: at 0 where it is reached and each offer's class, past it their forms and access,
5
+ * flows in the order of trust, never what only a transport reads. An operation asked no why is read once, if of the class
6
+ * head reads; asked why, its region is followed by why its newest answer stands. Each answer reads red past answer/seconds
7
+ * from the call's start, all head did for it included, with the question it answered as its coordinate.
7
8
  */
8
9
  export declare const answer: Answer;
@@ -5,36 +5,38 @@ import { vocabOf } from "../core/read.js";
5
5
  import { redIn, sayIn } from "../core/surface.js";
6
6
  import { readSource } from "../flows/read.js";
7
7
  import { standingFor } from "../flows/world.js";
8
+ import { trustOf } from "../core/trust.js";
8
9
  /**
9
- * One view of a place, folded by bound at the resolution asked; or, of a source, what it offers as typed lines — at the
10
- * first resolution where it is reached and each offer's class, by default its forms and how access is proven too, never
11
- * what only a transport reads — and, at the call resolution, one read of an offer of the class head reads. Each answer
12
- * is measured against answer/seconds and reads red past it, with the question it answered as its coordinate.
10
+ * A view of a place, folded by bound at the resolution asked; or the region of a source, or of one operation in it, as
11
+ * typed lines at the resolution asked: at 0 where it is reached and each offer's class, past it their forms and access,
12
+ * flows in the order of trust, never what only a transport reads. An operation asked no why is read once, if of the class
13
+ * head reads; asked why, its region is followed by why its newest answer stands. Each answer reads red past answer/seconds
14
+ * from the call's start, all head did for it included, with the question it answered as its coordinate.
13
15
  */
14
- export const answer = (asked, context) => ((started) => inPlace(asked, context, async (place) => {
16
+ export const answer = (asked, context) => inPlace(asked, context, async (place) => {
15
17
  const word = (scope) => valueOf(context.lock, scope);
16
18
  const source = asked['source'];
17
19
  if (source) {
18
- if (asked['operation'] && (asked['at'] === word('look/call') || asked['history'] === 'true'))
20
+ const [operation, why] = [asked['operation'], asked['why'] === 'true'];
21
+ if (operation && !why)
19
22
  return readSource(asked, context);
20
23
  const held = await standingFor(context, place);
21
24
  if ('unsettled' in held)
22
25
  return [sayIn(context.lock)('unsettled', { source, why: held.unsettled })];
23
- const [v, first, shallow] = [vocabOf(context.lock), (asked['at'] ?? word('look/default')) === '0', word('look/first').split('|')];
24
- const offer = `${v.offers}/${source}/${asked['operation'] ?? ''}`;
26
+ const [v, first, shallow] = [vocabOf(context.lock), asked['at'] === '0', word('look/first').split('|')];
27
+ const region = `${v.offers}/${source}/${operation ?? ''}`;
25
28
  const shown = held.lines.filter((l) => {
26
29
  const scope = l.scope ?? '';
27
30
  if (scope === `${v.region}/${source}`)
28
31
  return true;
29
32
  if (scope.startsWith(`${v.keys}/${source}/`))
30
33
  return !first;
31
- const ofOffer = asked['operation'] ? scope === offer || scope.startsWith(`${offer}/`) : scope.startsWith(offer);
32
- if (!ofOffer || scope.endsWith(`/${v.carry}`))
34
+ if (!(operation ? scope === region || scope.startsWith(`${region}/`) : scope.startsWith(region)) || scope.endsWith(`/${v.carry}`))
33
35
  return false;
34
36
  return !first || (!scope.endsWith(`/${v.form}`) && shallow.includes(l.measure ?? ''));
35
37
  });
36
- return shown.length ? shown.map((l) => canonical(l)).sort() : [sayIn(context.lock)('unread', { source })];
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
39
  }
38
- const view = `${asked['view'] || word('bound/standing')}${asked['at'] ? `${word('bound/at')}${asked['at']}` : ''}`;
40
+ const view = `${asked['view'] ?? ''}${asked['at'] ? `${word('bound/at')}${asked['at']}` : ''}`;
39
41
  return printedOr(context, await context.bound(place, [word('bound/read'), word('bound/view'), view]));
40
- }).then((lines) => [...lines, ...redIn(context.lock)('answer/seconds', `answer/${asked['source'] ? `${asked['source']}/${asked['operation'] ?? ''}` : asked['view'] || valueOf(context.lock, 'bound/standing')}@${asked['at'] ?? valueOf(context.lock, 'look/default')}`, started, context.machine())]))(context.machine());
42
+ }).then((lines) => [...lines, ...redIn(context.lock)('answer/seconds', `answer/${asked['source'] ? `${asked['source']}/${asked['operation'] ?? ''}` : asked['view'] ?? ''}`, context.begun, context.machine())]);
@@ -0,0 +1,7 @@
1
+ import type { Answer } from '../core/answer.ts';
2
+ /**
3
+ * What this device has connected, read off each place's lines as bound folds them and the receipts its own keys signed:
4
+ * every place head's index names — or the one asked — the sources it pinned, the grants that stand today and the last
5
+ * read it kept a receipt of. Nothing is asked, called or signed.
6
+ */
7
+ export declare const answer: Answer;
@@ -0,0 +1,32 @@
1
+ import { LOCK } from '@lapxo/topos/wire';
2
+ import { standingOr } from "../core/answer.js";
3
+ import { under, valueOf } from "../core/lock.js";
4
+ import { sayIn } from "../core/surface.js";
5
+ /**
6
+ * What this device has connected, read off each place's lines as bound folds them and the receipts its own keys signed:
7
+ * every place head's index names — or the one asked — the sources it pinned, the grants that stand today and the last
8
+ * read it kept a receipt of. Nothing is asked, called or signed.
9
+ */
10
+ export const answer = async (asked, context) => {
11
+ const [say, word, home, today] = [sayIn(context.lock), (k) => valueOf(context.lock, k), context.home(), new Date(context.now()).toISOString().slice(0, 10)];
12
+ const index = context.files.has(home, LOCK) ? await standingOr(context, home, context.policy) : { lines: [] };
13
+ if ('said' in index)
14
+ return index.said;
15
+ const places = index.lines.filter((l) => l.scope?.startsWith(`${word('home/family')}/`) && (!asked['place'] || l.value === asked['place']));
16
+ if (!places.length)
17
+ return [say('places-none', { place: asked['place'] ?? '' })];
18
+ const said = [];
19
+ for (const p of places) {
20
+ const held = await standingOr(context, p.value);
21
+ if ('said' in held) {
22
+ said.push(...held.said);
23
+ continue;
24
+ }
25
+ const at = word('receipt/at'), last = held.lines.filter((l) => l.scope?.startsWith(`${word('receipt/family')}/`) && l.at?.startsWith(`${at}:`)).sort((a, b) => (a.at < b.at ? 1 : -1))[0];
26
+ said.push(say('place-held', { name: p.scope.split('/')[1], dir: p.value,
27
+ sources: held.lines.filter((l) => l.scope?.startsWith('uses/') && under(context.lock, 'place/compiled', l.needs)).map((l) => l.scope.slice('uses/'.length)).join(', ') || '—',
28
+ grants: held.lines.filter((l) => l.scope?.startsWith('allow/') && !(l.measure === 'until' && (l.value ?? '') < today)).map((l) => `${l.scope} ${l.measure} ${l.value}`).join('; ') || '—',
29
+ last: last ? `${last.scope.split('/').slice(1, 3).join('/')} at ${last.at.slice(at.length + 1)}` : '—' }));
30
+ }
31
+ return said;
32
+ };
@@ -0,0 +1 @@
1
+ export { shared as answer } from '../core/package.ts';
@@ -0,0 +1 @@
1
+ export { shared as answer } from "../core/package.js";
@@ -0,0 +1,11 @@
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; 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
+ */
11
+ export declare const asked: Answer;