@astrosheep/square 0.3.93 → 0.3.95
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/dist/activity-feed.d.ts +3 -5
- package/dist/activity-feed.js +16 -14
- package/dist/artifact.js +3 -3
- package/dist/attention.d.ts +19 -0
- package/dist/attention.js +30 -0
- package/dist/boundary-presentation.d.ts +2 -2
- package/dist/boundary-presentation.js +33 -40
- package/dist/catch-decisions.d.ts +5 -8
- package/dist/catch-decisions.js +45 -25
- package/dist/claude-delivery-status.js +10 -4
- package/dist/claude-delivery.js +19 -11
- package/dist/claude-mod.js +24 -53
- package/dist/cli/context.d.ts +9 -4
- package/dist/cli/context.js +30 -27
- package/dist/cli/express-presentation.d.ts +3 -1
- package/dist/cli/express-presentation.js +28 -16
- package/dist/cli/json.d.ts +3 -0
- package/dist/cli/json.js +14 -0
- package/dist/cli/mcp-server-command.js +2 -2
- package/dist/cli/observation-commands.d.ts +1 -3
- package/dist/cli/observation-commands.js +43 -56
- package/dist/cli/program.js +3 -3
- package/dist/cli/registry.js +7 -6
- package/dist/cli/square-commands.d.ts +0 -2
- package/dist/cli/square-commands.js +30 -44
- package/dist/codex-hook.d.ts +3 -2
- package/dist/codex-hook.js +13 -4
- package/dist/codex-queue.js +4 -4
- package/dist/decisions.d.ts +5 -16
- package/dist/decisions.js +9 -33
- package/dist/delivery-attempts.d.ts +52 -0
- package/dist/delivery-attempts.js +73 -0
- package/dist/delivery-deferral.js +2 -2
- package/dist/delivery-effects.d.ts +139 -0
- package/dist/delivery-effects.js +163 -0
- package/dist/delivery-health.d.ts +0 -1
- package/dist/delivery-health.js +1 -3
- package/dist/delivery-operations.d.ts +2 -2
- package/dist/delivery-operations.js +76 -101
- package/dist/delivery-primitives.d.ts +5 -0
- package/dist/delivery-primitives.js +3 -0
- package/dist/delivery.d.ts +1 -1
- package/dist/delivery.js +7 -6
- package/dist/help.js +14 -14
- package/dist/http-delivery.d.ts +16 -0
- package/dist/http-delivery.js +101 -0
- package/dist/inbox.d.ts +9 -22
- package/dist/inbox.js +23 -27
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.js +23 -24
- package/dist/model.d.ts +12 -21
- package/dist/model.js +5 -8
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.js +8 -5
- package/dist/operation-presentation.d.ts +6 -6
- package/dist/operation-presentation.js +55 -58
- package/dist/owner.d.ts +11 -0
- package/dist/owner.js +32 -0
- package/dist/paseo-delivery.d.ts +4 -1
- package/dist/paseo-delivery.js +7 -12
- package/dist/paseo-state.d.ts +2 -2
- package/dist/paseo-state.js +6 -4
- package/dist/plugins/opencode/delivery.js +27 -15
- package/dist/ports.d.ts +18 -47
- package/dist/presentation-operations.d.ts +14 -19
- package/dist/presentation-operations.js +51 -61
- package/dist/presentation.d.ts +7 -17
- package/dist/presentation.js +46 -91
- package/dist/receive.d.ts +66 -0
- package/dist/receive.js +54 -0
- package/dist/runtime.d.ts +1 -8
- package/dist/runtime.js +2 -17
- package/dist/seats.d.ts +2 -3
- package/dist/seats.js +7 -15
- package/dist/session-lifecycle.d.ts +6 -2
- package/dist/session-lifecycle.js +39 -11
- package/dist/sqlite-square.d.ts +36 -23
- package/dist/sqlite-square.js +162 -211
- package/dist/square-api.d.ts +16 -11
- package/dist/square-api.js +27 -23
- package/dist/square-core.d.ts +0 -14
- package/dist/square-core.js +0 -9
- package/dist/square-domain.d.ts +4 -2
- package/dist/square-domain.js +65 -52
- package/dist/square-facade.d.ts +47 -105
- package/dist/square-index.d.ts +5 -13
- package/dist/square-index.js +13 -38
- package/dist/square-legacy-framing.d.ts +2 -0
- package/dist/square-legacy-framing.js +8 -0
- package/dist/square-location.d.ts +1 -14
- package/dist/square-location.js +4 -70
- package/dist/square-migration-host-ledger.d.ts +2 -2
- package/dist/square-migration-host-ledger.js +6 -5
- package/dist/square-migration.d.ts +4 -1
- package/dist/square-migration.js +93 -38
- package/dist/square-receive.d.ts +6 -0
- package/dist/square-receive.js +18 -0
- package/dist/square-sqlite.d.ts +9 -3
- package/dist/square-sqlite.js +139 -114
- package/dist/square-tools.d.ts +7 -10
- package/dist/square-tools.js +21 -61
- package/dist/stream.js +4 -5
- package/dist/views.d.ts +23 -13
- package/dist/views.js +47 -53
- package/dist/wakes.d.ts +1 -5
- package/dist/wakes.js +4 -6
- package/examples/symmetric-domains.mjs +250 -0
- package/guides/application-facade-delivery-evidence.md +6 -6
- package/guides/attention-primitive.md +32 -0
- package/guides/change-waits.md +2 -2
- package/guides/claude-native.md +18 -9
- package/guides/codex-delivery.md +17 -6
- package/guides/delivery-primitives.md +180 -0
- package/guides/mcp-connection-lifetime.md +1 -1
- package/guides/mcp.md +3 -3
- package/guides/migration.md +148 -151
- package/guides/operation-cancellation.md +1 -1
- package/guides/participant-ownership.md +78 -47
- package/guides/pi-tools.md +34 -5
- package/guides/primitive-catch-consolidation.md +5 -5
- package/guides/primitive-history-consolidation.md +2 -2
- package/guides/primitive-status-consolidation.md +3 -3
- package/guides/primitive-storage-consolidation.md +1 -1
- package/guides/receive-primitive.md +90 -0
- package/guides/row-runtime-delivery-evidence.md +1 -1
- package/guides/row-storage-design.md +22 -28
- package/guides/square-api.md +314 -91
- package/guides/symmetric-domains.md +92 -0
- package/package.json +14 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/skills/square/SKILL.md +2 -6
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +27 -17
- package/plugins/paseo/client/palette.ts +18 -18
- package/plugins/paseo/client/square-message.tsx +17 -23
- package/plugins/pi/square-pi.js +17 -17
- package/skills/square/SKILL.md +2 -6
- package/dist/square-backend.d.ts +0 -13
- package/dist/square-backend.js +0 -1
- package/dist/square-stdio.d.ts +0 -22
- package/dist/square-stdio.js +0 -235
- package/dist/stdio-server.d.ts +0 -3
- package/dist/stdio-server.js +0 -212
- package/guides/backends.md +0 -193
package/dist/square-api.d.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { SqliteSquare } from './sqlite-square.js';
|
|
2
|
+
import { type Owner } from './model.js';
|
|
3
|
+
import type { OperationControl, Participant, SquareAtInput, SquareBuildInput } from './square-facade.js';
|
|
4
|
+
export type Square = SqliteSquare;
|
|
5
|
+
export declare const Square: {
|
|
6
|
+
at: (input: SquareAtInput, control?: OperationControl) => Promise<SqliteSquare>;
|
|
7
|
+
build: (input: SquareBuildInput, control?: OperationControl) => Promise<SqliteSquare>;
|
|
8
|
+
inMemory: typeof SqliteSquare.inMemory;
|
|
9
|
+
};
|
|
10
|
+
export { requireOwner, requireJoinName } from './seats.js';
|
|
3
11
|
export type * from './square-facade.js';
|
|
4
12
|
export { squareLocation } from './square-location.js';
|
|
13
|
+
export declare function selectOwnSeat(square: Pick<SqliteSquare, 'participant' | 'seatsOf'>, owner: Owner | undefined, name?: string): Promise<Participant>;
|
|
14
|
+
export declare function selectOwnSeat(square: Pick<SqliteSquare, 'participant' | 'seatsOf'>, owner: Owner | undefined, name: string | undefined, options: {
|
|
15
|
+
readonly optional: true;
|
|
16
|
+
}): Promise<Participant | undefined>;
|
|
5
17
|
export { ACTIVITY_METADATA_MAX_BYTES } from './model.js';
|
|
6
|
-
export {
|
|
7
|
-
|
|
8
|
-
export interface Square extends SquareBackend {
|
|
9
|
-
}
|
|
10
|
-
export declare const Square: {
|
|
11
|
-
at(input: SquareAtInput, control?: OperationControl): Promise<Square>;
|
|
12
|
-
build(input: SquareBuildInput, control?: OperationControl): Promise<Square>;
|
|
13
|
-
inMemory(input: Omit<SquareBuildInput, "path" | "resources">): Square;
|
|
14
|
-
};
|
|
18
|
+
export { localUserOwner, harnessOwner, decodeHarnessOwner } from './owner.js';
|
|
19
|
+
export { squareClock, createSquareResourceOwner, formatActivityId, parseActivityId, MAX_IDENTITY_SET_SIZE, CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT, HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT, DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT, diagnoseSquare, migrateSquare, discover, inbox } from './sqlite-square.js';
|
package/dist/square-api.js
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
import { SqliteSquare } from './sqlite-square.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { SquareError } from './model.js';
|
|
3
|
+
import { requireOwner } from './seats.js';
|
|
4
|
+
import { createDefaultWakeTransport } from './notifications.js';
|
|
5
|
+
export const Square = { at: async (input, control) => SqliteSquare.at({ ...input, wakeTransport: await resolveWakeTransport(input) }, control),
|
|
6
|
+
build: async (input, control) => SqliteSquare.build({ ...input, wakeTransport: await resolveWakeTransport(input) }, control), inMemory: SqliteSquare.inMemory };
|
|
7
|
+
const resolveWakeTransport = (input) => input.wakeTransport !== undefined
|
|
8
|
+
? Promise.resolve(input.wakeTransport)
|
|
9
|
+
: createDefaultWakeTransport(input.env ?? process.env).catch(() => undefined);
|
|
10
|
+
export { requireOwner, requireJoinName } from './seats.js';
|
|
4
11
|
export { squareLocation } from './square-location.js';
|
|
12
|
+
export async function selectOwnSeat(square, owner, name, options) {
|
|
13
|
+
if (owner === undefined && options?.optional && name === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
const caller = requireOwner(owner);
|
|
16
|
+
if (name !== undefined)
|
|
17
|
+
return square.participant(name, { owner: caller });
|
|
18
|
+
const seats = (await square.seatsOf(caller)).filter(seat => seat.attachment === 'attached');
|
|
19
|
+
if (seats.length !== 1) {
|
|
20
|
+
if (options?.optional)
|
|
21
|
+
return undefined;
|
|
22
|
+
throw new SquareError('invalid_args', seats.length > 1
|
|
23
|
+
? 'Square needs an explicit participant name: this owner holds multiple names'
|
|
24
|
+
: 'Square needs one unambiguous participant name');
|
|
25
|
+
}
|
|
26
|
+
return square.participant(seats[0].name, { owner: caller });
|
|
27
|
+
}
|
|
5
28
|
export { ACTIVITY_METADATA_MAX_BYTES } from './model.js';
|
|
6
|
-
export {
|
|
7
|
-
export
|
|
8
|
-
async at(input, control) {
|
|
9
|
-
const captured = { ...input, cwd: input.cwd ?? process.cwd(), env: { ...(input.env ?? process.env) } };
|
|
10
|
-
const location = squareLocation(captured);
|
|
11
|
-
const selected = parseSquareLocation(location);
|
|
12
|
-
return selected.kind === 'local' ? SqliteSquare.at({ ...captured, path: location }, control)
|
|
13
|
-
: openStdioSquare(location, selected, captured, await backendConfiguration(captured), undefined, control);
|
|
14
|
-
},
|
|
15
|
-
async build(input, control) {
|
|
16
|
-
const captured = { ...input, cwd: input.cwd ?? process.cwd(), env: { ...(input.env ?? process.env) } };
|
|
17
|
-
const location = squareLocation(captured);
|
|
18
|
-
const selected = parseSquareLocation(location);
|
|
19
|
-
return selected.kind === 'local' ? SqliteSquare.build({ ...captured, path: location }, control)
|
|
20
|
-
: openStdioSquare(location, selected, captured, await backendConfiguration(captured), {
|
|
21
|
-
markdown: input.markdown, hardCap: input.hardCap, throttlePerMinute: input.throttlePerMinute,
|
|
22
|
-
template: input.template, force: input.force,
|
|
23
|
-
}, control);
|
|
24
|
-
},
|
|
25
|
-
inMemory(input) { return SqliteSquare.inMemory(input); },
|
|
26
|
-
};
|
|
29
|
+
export { localUserOwner, harnessOwner, decodeHarnessOwner } from './owner.js';
|
|
30
|
+
export { squareClock, createSquareResourceOwner, formatActivityId, parseActivityId, MAX_IDENTITY_SET_SIZE, CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT, HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT, DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT, diagnoseSquare, migrateSquare, discover, inbox } from './sqlite-square.js';
|
package/dist/square-core.d.ts
CHANGED
|
@@ -29,15 +29,6 @@ export type Act = {
|
|
|
29
29
|
mentions?: readonly Participant[];
|
|
30
30
|
reach?: Reach;
|
|
31
31
|
reply?: number;
|
|
32
|
-
} | {
|
|
33
|
-
kind: 'hold';
|
|
34
|
-
actor?: Participant;
|
|
35
|
-
at?: number;
|
|
36
|
-
body?: string;
|
|
37
|
-
} | {
|
|
38
|
-
kind: 'resume';
|
|
39
|
-
actor?: Participant;
|
|
40
|
-
at?: number;
|
|
41
32
|
} | {
|
|
42
33
|
kind: 'read';
|
|
43
34
|
actor: Participant;
|
|
@@ -59,9 +50,4 @@ export type Act = {
|
|
|
59
50
|
target: Participant;
|
|
60
51
|
at?: number;
|
|
61
52
|
};
|
|
62
|
-
export declare function audienceOf(say: {
|
|
63
|
-
body?: string;
|
|
64
|
-
mentions?: readonly Participant[];
|
|
65
|
-
reach?: Reach;
|
|
66
|
-
}): Audience;
|
|
67
53
|
export declare function audienceIncludes(audience: Audience, name: string): boolean;
|
package/dist/square-core.js
CHANGED
|
@@ -13,15 +13,6 @@ export function parseActivityId(value) {
|
|
|
13
13
|
const index = Number(value.slice(4));
|
|
14
14
|
return Number.isSafeInteger(index) ? index : undefined;
|
|
15
15
|
}
|
|
16
|
-
export function audienceOf(say) {
|
|
17
|
-
if (say.reach === 'bell')
|
|
18
|
-
return { kind: 'bell' };
|
|
19
|
-
const names = [];
|
|
20
|
-
for (const name of say.mentions ?? [])
|
|
21
|
-
if (!names.some((existing) => sameName(existing, name)))
|
|
22
|
-
names.push(name);
|
|
23
|
-
return { kind: 'mentions', names };
|
|
24
|
-
}
|
|
25
16
|
export function audienceIncludes(audience, name) {
|
|
26
17
|
return audience.kind === 'bell' || audience.names.some((mentioned) => sameName(mentioned, name));
|
|
27
18
|
}
|
package/dist/square-domain.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Seat, type StoredAct } from './model.js';
|
|
2
|
-
import type { ArtifactWriteAuthority, AttentionFact, LandedRecipient, ParticipantFact, SquareRead, SquareWrite } from './ports.js';
|
|
2
|
+
import type { ArtifactWriteAuthority, AttentionFact, LandedRecipient, ParticipantFact, SeatRef, SquareRead, SquareWrite } from './ports.js';
|
|
3
3
|
export interface LandingEffect {
|
|
4
4
|
readonly activity: StoredAct;
|
|
5
5
|
readonly participant?: ParticipantFact;
|
|
@@ -21,7 +21,7 @@ export interface LandingFacts {
|
|
|
21
21
|
/** The single landing transition, shared by both adapters and explicit rebuild. */
|
|
22
22
|
export declare function deriveLanding(activity: StoredAct, facts: LandingFacts): LandingEffect;
|
|
23
23
|
export declare function landingFacts(read: SquareRead, activity: StoredAct): LandingFacts;
|
|
24
|
-
export interface LandingWriter extends Pick<SquareWrite, 'markSeen' | 'putLease' | 'removeLease' | 'updateWakeAddress' | 'cancelThrough' | 'putDeliveryDeferral' | 'renewDeliveryDeferral' | 'releaseDeliveryDeferral' | 'claimDelivery' | 'prepareDelivery' | 'completeDelivery' | 'observeNativeDelivery'> {
|
|
24
|
+
export interface LandingWriter extends Pick<SquareWrite, 'markSeen' | 'putLease' | 'removeLease' | 'updateWakeAddress' | 'reattachSeat' | 'detachSeat' | 'cancelThrough' | 'putDeliveryDeferral' | 'renewDeliveryDeferral' | 'releaseDeliveryDeferral' | 'claimDelivery' | 'prepareDelivery' | 'completeDelivery' | 'observeNativeDelivery'> {
|
|
25
25
|
/** Artifact-private seat persistence used only by this gate's lifecycle transition. */
|
|
26
26
|
storeSeat(seat: Seat): boolean;
|
|
27
27
|
removeSeat(seat: import('./ports.js').SeatRef): boolean;
|
|
@@ -31,5 +31,7 @@ export interface SquareWriteGate {
|
|
|
31
31
|
readonly facts: SquareWrite;
|
|
32
32
|
complete(): void;
|
|
33
33
|
}
|
|
34
|
+
/** Read probes and writes share the same captured-coordinate and attachment fence. */
|
|
35
|
+
export declare function assertCurrentSeat(read: SquareRead, expected: SeatRef, allowDetached?: boolean): void;
|
|
34
36
|
/** The artifact transaction supplies one captured authority; every mutation checks it here. */
|
|
35
37
|
export declare function wrapSquareWrite(read: SquareRead, writes: LandingWriter, authority: ArtifactWriteAuthority): SquareWriteGate;
|
package/dist/square-domain.js
CHANGED
|
@@ -1,35 +1,13 @@
|
|
|
1
|
-
import { SquareError, formatActivityId, parseActivityId,
|
|
1
|
+
import { SquareError, formatActivityId, parseActivityId, sameName, validateOwner, validateName } from './model.js';
|
|
2
|
+
import { resolveAudience } from './attention.js';
|
|
2
3
|
/** The single landing transition, shared by both adapters and explicit rebuild. */
|
|
3
4
|
export function deriveLanding(activity, facts) {
|
|
4
|
-
const audience =
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|| audience.some((item) => sameName(item.recipient, recipient)))
|
|
10
|
-
return;
|
|
11
|
-
if (activity.reach !== 'bell' && facts.incoming.some((item) => item.kind === 'ignore' && sameName(item.listener, recipient)))
|
|
12
|
-
return;
|
|
13
|
-
audience.push({ recipient, route });
|
|
14
|
-
};
|
|
15
|
-
if (activity.reach === 'bell') {
|
|
16
|
-
for (const peer of facts.standing)
|
|
17
|
-
add(peer.name, 'bell');
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
for (const mention of activity.mentions ?? [])
|
|
21
|
-
add(mention, 'mention');
|
|
22
|
-
if (facts.replyAuthor !== undefined)
|
|
23
|
-
add(facts.replyAuthor, 'reply');
|
|
24
|
-
for (const peer of facts.standing) {
|
|
25
|
-
if (facts.incoming.some((item) => item.kind === 'listen' && sameName(item.listener, peer.name)))
|
|
26
|
-
add(peer.name, 'attention');
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
5
|
+
const audience = activity.kind === 'say' ? resolveAudience({
|
|
6
|
+
actor: activity.actor, recipients: facts.standing.map((item) => item.name),
|
|
7
|
+
mentions: activity.mentions, replyAuthor: facts.replyAuthor, reach: activity.reach,
|
|
8
|
+
incoming: facts.incoming, sameIdentity: sameName,
|
|
9
|
+
}) : [];
|
|
30
10
|
const actor = activity.actor;
|
|
31
|
-
if (actor === undefined)
|
|
32
|
-
return { activity, audience };
|
|
33
11
|
const previous = facts.actor ?? { name: actor, firstIndex: activity.index, joined: false, done: false, sayCount: 0 };
|
|
34
12
|
const participant = {
|
|
35
13
|
...previous,
|
|
@@ -57,7 +35,7 @@ export function deriveLanding(activity, facts) {
|
|
|
57
35
|
}
|
|
58
36
|
export function landingFacts(read, activity) {
|
|
59
37
|
return {
|
|
60
|
-
actor:
|
|
38
|
+
actor: read.participant(activity.actor),
|
|
61
39
|
standing: activity.kind === 'say' ? read.roster().filter((item) => item.joined) : [],
|
|
62
40
|
incoming: activity.kind === 'say' ? read.incomingAttention(activity.actor) : [],
|
|
63
41
|
outgoing: activity.kind === 'listen' || activity.kind === 'ignore' || activity.kind === 'unlisten' ? read.attention(activity.actor) : [],
|
|
@@ -68,31 +46,38 @@ function validateGeneration(value) {
|
|
|
68
46
|
if (typeof value !== 'string' || parseActivityId(value) === undefined)
|
|
69
47
|
throw new SquareError('invalid_args', 'Artifact authority requires a valid join generation.');
|
|
70
48
|
}
|
|
49
|
+
/** Read probes and writes share the same captured-coordinate and attachment fence. */
|
|
50
|
+
export function assertCurrentSeat(read, expected, allowDetached = false) {
|
|
51
|
+
const current = read.seat(expected.participant);
|
|
52
|
+
if (current === undefined || current.owner !== expected.owner || current.generation !== expected.generation)
|
|
53
|
+
throw new SquareError('already_joined', 'This participant seat has been replaced.');
|
|
54
|
+
if (!allowDetached && current.attachment !== 'attached')
|
|
55
|
+
throw new SquareError('already_joined', 'This participant is away; join again before writing.', { reason: 'needs_seat', activityUnsent: true });
|
|
56
|
+
}
|
|
71
57
|
/** The artifact transaction supplies one captured authority; every mutation checks it here. */
|
|
72
58
|
export function wrapSquareWrite(read, writes, authority) {
|
|
73
59
|
if (authority === undefined || typeof authority !== 'object' || authority === null)
|
|
74
60
|
throw new SquareError('invalid_args', 'Artifact writes require an explicit authority.');
|
|
75
61
|
const { applyLanding, ...mutations } = writes;
|
|
76
|
-
const
|
|
77
|
-
|
|
62
|
+
const seated = authority.kind === 'participant' || authority.kind === 'host-cleanup';
|
|
63
|
+
const expectedName = seated ? authority.seat?.participant : authority.participant;
|
|
64
|
+
if (!['participant', 'host-cleanup', 'join', 'takeover'].includes(authority.kind) || typeof expectedName !== 'string')
|
|
78
65
|
throw new SquareError('invalid_args', 'Artifact write authority is malformed.');
|
|
79
66
|
validateName(expectedName);
|
|
80
|
-
if (
|
|
81
|
-
|
|
67
|
+
if (seated) {
|
|
68
|
+
validateOwner(authority.seat.owner);
|
|
82
69
|
validateGeneration(authority.seat.generation);
|
|
83
70
|
}
|
|
84
71
|
else {
|
|
85
|
-
|
|
72
|
+
validateOwner(authority.owner);
|
|
86
73
|
if (authority.kind === 'takeover')
|
|
87
74
|
validateGeneration(authority.expectedGeneration);
|
|
88
75
|
}
|
|
89
|
-
const assertSeat = (seat, expected = authority.kind === 'participant' ? authority.seat : undefined) => {
|
|
76
|
+
const assertSeat = (seat, expected = authority.kind === 'participant' ? authority.seat : undefined, allowDetached = false) => {
|
|
90
77
|
if (expected === undefined || !sameName(expected.participant, seat.participant)
|
|
91
|
-
|| seat.generation !== expected.generation ||
|
|
78
|
+
|| seat.generation !== expected.generation || seat.owner !== expected.owner)
|
|
92
79
|
throw new SquareError('already_joined', 'This write needs the current participant seat.');
|
|
93
|
-
|
|
94
|
-
if (current === undefined || !sameBody(current.body, expected.body) || current.generation !== expected.generation)
|
|
95
|
-
throw new SquareError('already_joined', 'This participant seat has been replaced.');
|
|
80
|
+
assertCurrentSeat(read, expected, allowDetached);
|
|
96
81
|
};
|
|
97
82
|
const assertParticipant = (name, generation) => {
|
|
98
83
|
if (authority.kind !== 'participant')
|
|
@@ -104,11 +89,14 @@ export function wrapSquareWrite(read, writes, authority) {
|
|
|
104
89
|
if (authority.kind === 'participant') {
|
|
105
90
|
assertSeat(authority.seat);
|
|
106
91
|
}
|
|
92
|
+
else if (authority.kind === 'host-cleanup') {
|
|
93
|
+
// A late host cleanup is allowed to inspect a stale coordinate and return a no-op.
|
|
94
|
+
}
|
|
107
95
|
else if (authority.kind === 'join') {
|
|
108
96
|
const participant = read.participant(expectedName), seat = read.seat(expectedName);
|
|
109
97
|
if (participant?.joined) {
|
|
110
|
-
if (seat === undefined ||
|
|
111
|
-
throw new SquareError('already_joined', `${expectedName} is held by another
|
|
98
|
+
if (seat === undefined || seat.owner !== authority.owner)
|
|
99
|
+
throw new SquareError('already_joined', `${expectedName} is held by another owner; take it over explicitly`);
|
|
112
100
|
reconnectSeat = seat;
|
|
113
101
|
}
|
|
114
102
|
}
|
|
@@ -131,6 +119,11 @@ export function wrapSquareWrite(read, writes, authority) {
|
|
|
131
119
|
if (activity.kind === 'join')
|
|
132
120
|
throw new SquareError('invalid_args', 'A participant seat can only be re-established by join.');
|
|
133
121
|
}
|
|
122
|
+
else if (authority.kind === 'host-cleanup') {
|
|
123
|
+
assertSeat(authority.seat, authority.seat, true);
|
|
124
|
+
if (activity.kind !== 'done')
|
|
125
|
+
throw new SquareError('invalid_args', 'Host cleanup may only end membership.');
|
|
126
|
+
}
|
|
134
127
|
else if (authority.kind === 'join') {
|
|
135
128
|
if (activity.kind !== 'join')
|
|
136
129
|
throw new SquareError('invalid_args', 'A new seat must be established by its join activity.');
|
|
@@ -157,27 +150,28 @@ export function wrapSquareWrite(read, writes, authority) {
|
|
|
157
150
|
const stored = { ...activity, index };
|
|
158
151
|
applyLanding(deriveLanding(stored, landingFacts(read, stored)));
|
|
159
152
|
if (stored.kind === 'join') {
|
|
160
|
-
if (
|
|
161
|
-
throw new SquareError('invalid_args', '
|
|
153
|
+
if (seated)
|
|
154
|
+
throw new SquareError('invalid_args', 'Seat authority cannot establish another seat.');
|
|
162
155
|
const seat = {
|
|
163
156
|
participant: stored.actor,
|
|
164
|
-
|
|
157
|
+
owner: authority.owner,
|
|
165
158
|
generation: formatActivityId(stored.index),
|
|
159
|
+
attachment: 'attached',
|
|
166
160
|
...(authority.wakeAddress === undefined ? {} : { wakeAddress: authority.wakeAddress }),
|
|
167
161
|
};
|
|
168
162
|
mutations.storeSeat(seat);
|
|
169
163
|
const established = read.seat(seat.participant);
|
|
170
|
-
if (established === undefined ||
|
|
164
|
+
if (established === undefined || established.owner !== seat.owner || established.generation !== seat.generation)
|
|
171
165
|
throw new SquareError('invalid_args', 'The join activity did not establish its artifact seat.');
|
|
172
166
|
if (authority.kind === 'takeover')
|
|
173
167
|
takeoverPhase = 'joined';
|
|
174
168
|
}
|
|
175
|
-
else if (stored.kind === 'done' &&
|
|
169
|
+
else if (stored.kind === 'done' && seated) {
|
|
176
170
|
if (!mutations.removeSeat(authority.seat))
|
|
177
171
|
throw new SquareError('already_joined', 'The participant seat changed while it was being completed.');
|
|
178
172
|
}
|
|
179
173
|
else if (stored.kind === 'done' && authority.kind === 'takeover') {
|
|
180
|
-
if (captured === undefined || !mutations.removeSeat({ participant: expectedName,
|
|
174
|
+
if (captured === undefined || !mutations.removeSeat({ participant: expectedName, owner: captured.owner, generation: captured.generation }))
|
|
181
175
|
throw new SquareError('already_joined', 'The participant seat changed while takeover was completing it.');
|
|
182
176
|
takeoverPhase = 'done';
|
|
183
177
|
}
|
|
@@ -185,13 +179,32 @@ export function wrapSquareWrite(read, writes, authority) {
|
|
|
185
179
|
return lastActivity;
|
|
186
180
|
},
|
|
187
181
|
updateWakeAddress(seat, wakeAddress) {
|
|
188
|
-
|
|
182
|
+
if (authority.kind !== 'join' && authority.kind !== 'participant')
|
|
183
|
+
throw new SquareError('already_joined', 'This write needs the current participant seat.');
|
|
184
|
+
assertSeat(seat, authority.kind === 'join' ? reconnectSeat : authority.seat);
|
|
189
185
|
return mutations.updateWakeAddress(seat, wakeAddress);
|
|
190
186
|
},
|
|
187
|
+
reattachSeat(seat, wakeAddress) {
|
|
188
|
+
if (authority.kind !== 'join')
|
|
189
|
+
throw new SquareError('already_joined', 'Reattachment requires a same-owner join.');
|
|
190
|
+
assertSeat(seat, reconnectSeat, true);
|
|
191
|
+
return mutations.reattachSeat(seat, wakeAddress);
|
|
192
|
+
},
|
|
193
|
+
detachSeat(seat) {
|
|
194
|
+
if (authority.kind !== 'host-cleanup')
|
|
195
|
+
throw new SquareError('already_joined', 'Detach requires captured host cleanup authority.');
|
|
196
|
+
const current = read.seat(seat.participant), expected = authority.seat;
|
|
197
|
+
if (!sameName(seat.participant, expected.participant) || seat.owner !== expected.owner || seat.generation !== expected.generation)
|
|
198
|
+
throw new SquareError('already_joined', 'Host cleanup does not match its captured seat.');
|
|
199
|
+
if (current === undefined || current.attachment !== 'attached' || current.owner !== expected.owner
|
|
200
|
+
|| current.generation !== expected.generation)
|
|
201
|
+
return false;
|
|
202
|
+
return mutations.detachSeat(seat);
|
|
203
|
+
},
|
|
191
204
|
cancelThrough(seat, through) {
|
|
192
205
|
assertSeat(seat);
|
|
193
|
-
if (!Number.isSafeInteger(through) || through < 0 || through
|
|
194
|
-
throw new SquareError('invalid_args', 'Cancellation boundary must
|
|
206
|
+
if (!Number.isSafeInteger(through) || through < 0 || through >= read.header().nextActIndex)
|
|
207
|
+
throw new SquareError('invalid_args', 'Cancellation boundary must be an assigned activity index below the allocator.');
|
|
195
208
|
return mutations.cancelThrough(seat, through);
|
|
196
209
|
},
|
|
197
210
|
markSeen(name, indexes, at) {
|
|
@@ -242,7 +255,7 @@ export function wrapSquareWrite(read, writes, authority) {
|
|
|
242
255
|
return;
|
|
243
256
|
const seat = read.seat(expectedName);
|
|
244
257
|
if (takeoverPhase !== 'joined' || lastActivity?.kind !== 'join' || seat === undefined
|
|
245
|
-
||
|
|
258
|
+
|| seat.owner !== authority.owner || seat.generation !== formatActivityId(lastActivity.index))
|
|
246
259
|
throw new SquareError('already_joined', 'Takeover did not complete its replacement join.');
|
|
247
260
|
},
|
|
248
261
|
};
|