@astrosheep/square 0.3.106 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activity-feed.js +1 -1
- package/dist/artifact-store.d.ts +41 -0
- package/dist/artifact-store.js +272 -0
- package/dist/artifact.d.ts +8 -12
- package/dist/artifact.js +66 -343
- package/dist/attention-presentation.d.ts +33 -7
- package/dist/attention-presentation.js +33 -69
- package/dist/boundary-presentation.d.ts +0 -2
- package/dist/boundary-presentation.js +17 -22
- package/dist/catch-decisions.d.ts +2 -2
- package/dist/catch-decisions.js +14 -4
- package/dist/catch-selection.d.ts +8 -0
- package/dist/catch-selection.js +5 -0
- package/dist/channel-artifact.d.ts +10 -0
- package/dist/channel-artifact.js +55 -0
- package/dist/channel-delivery.d.ts +11 -0
- package/dist/channel-delivery.js +51 -0
- package/dist/channel-domain.d.ts +3 -0
- package/dist/channel-domain.js +83 -0
- package/dist/channel-facade.d.ts +76 -0
- package/dist/channel-facade.js +1 -0
- package/dist/channel-internal.d.ts +126 -0
- package/dist/channel-internal.js +440 -0
- package/dist/channel-metadata.d.ts +11 -0
- package/dist/channel-metadata.js +1 -0
- package/dist/channel-ports.d.ts +241 -0
- package/dist/channel-ports.js +1 -0
- package/dist/channel-presentation.d.ts +17 -0
- package/dist/channel-presentation.js +22 -0
- package/dist/channel-sqlite-schema.d.ts +13 -0
- package/dist/channel-sqlite-schema.js +81 -0
- package/dist/channel-transport.d.ts +62 -0
- package/dist/channel-transport.js +1 -0
- package/dist/channel-values.d.ts +27 -0
- package/dist/channel-values.js +90 -0
- package/dist/channel.d.ts +31 -21
- package/dist/channel.js +42 -74
- package/dist/claude-delivery-status.js +1 -1
- package/dist/claude-delivery.d.ts +2 -5
- package/dist/claude-delivery.js +56 -103
- package/dist/claude-mod.js +4 -4
- package/dist/claude-transport.d.ts +5 -0
- package/dist/claude-transport.js +58 -0
- package/dist/cli/context.d.ts +2 -2
- package/dist/cli/context.js +0 -1
- package/dist/cli/maintenance-commands.d.ts +0 -4
- package/dist/cli/maintenance-commands.js +2 -29
- package/dist/cli/program.js +1 -1
- package/dist/cli/registry.js +1 -2
- package/dist/codex-address.d.ts +1 -1
- package/dist/codex-queue.d.ts +2 -2
- package/dist/codex-queue.js +1 -3
- package/dist/decisions.js +3 -3
- package/dist/delivery-deferral.d.ts +9 -18
- package/dist/delivery-deferral.js +4 -4
- package/dist/delivery-health.d.ts +4 -4
- package/dist/delivery-health.js +11 -11
- package/dist/delivery-operations.d.ts +15 -2
- package/dist/delivery-operations.js +60 -30
- package/dist/delivery.d.ts +44 -33
- package/dist/delivery.js +49 -75
- package/dist/help.js +1 -6
- package/dist/http-delivery.d.ts +2 -2
- package/dist/http-delivery.js +2 -4
- package/dist/inbox.d.ts +10 -18
- package/dist/inbox.js +44 -53
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.js +40 -70
- package/dist/message.d.ts +7 -0
- package/dist/message.js +23 -0
- package/dist/model.d.ts +41 -77
- package/dist/model.js +7 -139
- package/dist/neutral.d.ts +72 -0
- package/dist/neutral.js +27 -0
- package/dist/notifications.d.ts +2 -8
- package/dist/notifications.js +15 -50
- package/dist/operation-presentation.d.ts +1 -2
- package/dist/operation-presentation.js +1 -7
- package/dist/owner.d.ts +2 -2
- package/dist/owner.js +1 -1
- package/dist/paseo-delivery.d.ts +1 -1
- package/dist/paseo-delivery.js +2 -4
- package/dist/plugins/opencode/delivery.js +27 -42
- package/dist/ports.d.ts +46 -168
- package/dist/presentation-operations.d.ts +1 -14
- package/dist/presentation-operations.js +16 -16
- package/dist/presentation.d.ts +5 -5
- package/dist/presentation.js +1 -0
- package/dist/runtime.js +2 -2
- package/dist/search.js +1 -1
- package/dist/sqlite-channel.d.ts +15 -0
- package/dist/sqlite-channel.js +600 -0
- package/dist/sqlite-framing.d.ts +8 -0
- package/dist/sqlite-framing.js +31 -0
- package/dist/sqlite-square.d.ts +12 -13
- package/dist/sqlite-square.js +192 -248
- package/dist/sqlite-values.d.ts +21 -0
- package/dist/sqlite-values.js +78 -0
- package/dist/square-api.d.ts +1 -1
- package/dist/square-api.js +1 -1
- package/dist/square-core.d.ts +1 -1
- package/dist/square-domain.d.ts +21 -7
- package/dist/square-domain.js +93 -94
- package/dist/square-facade.d.ts +12 -66
- package/dist/square-index.d.ts +1 -1
- package/dist/square-index.js +1 -1
- package/dist/square-location.d.ts +6 -1
- package/dist/square-location.js +1 -1
- package/dist/square-receive.d.ts +2 -3
- package/dist/square-sqlite.d.ts +45 -44
- package/dist/square-sqlite.js +308 -764
- package/dist/square-tools.d.ts +8 -0
- package/dist/square-tools.js +39 -0
- package/dist/views.d.ts +6 -6
- package/dist/views.js +15 -9
- package/dist/wake-preview.d.ts +7 -0
- package/dist/wake-preview.js +15 -0
- package/dist/wake-sink.d.ts +2 -2
- package/dist/wake-sink.js +1 -1
- package/dist/wake-transports.d.ts +7 -0
- package/dist/wake-transports.js +42 -0
- package/guides/claude-native.md +13 -10
- package/guides/codex-delivery.md +3 -1
- package/guides/participant-ownership.md +1 -1
- package/guides/row-runtime-delivery-evidence.md +2 -0
- package/guides/row-storage-design.md +19 -44
- package/guides/square-api.md +137 -60
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/types.d.ts +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +28 -41
- package/plugins/pi/square-pi.js +28 -51
- package/dist/square-legacy-framing.d.ts +0 -11
- package/dist/square-legacy-framing.js +0 -64
- package/dist/square-migration-host-ledger.d.ts +0 -26
- package/dist/square-migration-host-ledger.js +0 -169
- package/dist/square-migration.d.ts +0 -30
- package/dist/square-migration.js +0 -599
- package/guides/migration.md +0 -156
package/dist/sqlite-square.js
CHANGED
|
@@ -1,54 +1,36 @@
|
|
|
1
|
-
import { formatActivityId
|
|
1
|
+
import { formatActivityId } from './square-core.js';
|
|
2
2
|
import { assertCurrentSeat } from './square-domain.js';
|
|
3
3
|
import { canonicalPath } from './canonical-path.js';
|
|
4
|
-
import { activityQueryFilter } from './activity-query.js';
|
|
5
|
-
import { validateMetadataMatch, validateDeferralDuration, validateDeferralId } from './delivery-deferral.js';
|
|
6
4
|
import { squareLocation } from './square-location.js';
|
|
7
5
|
import crypto from 'node:crypto';
|
|
8
6
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
9
|
-
import {
|
|
7
|
+
import { InternalSquareError, SquareError, isSquareError, nameKey, validateName } from './model.js';
|
|
8
|
+
import { Channel, ChannelEndpoint, checkControl, failure, squareClock } from './channel-internal.js';
|
|
9
|
+
import { channelArtifact } from './artifact.js';
|
|
10
|
+
import { deliverPending } from './delivery-operations.js';
|
|
10
11
|
import { attentionChange, coreDone, coreListening, decideAct, decideJoin, resolveKnownName, validateDoneBody } from './decisions.js';
|
|
11
12
|
import { WATCH_HEARTBEAT_MS, inSquareCount, presenceAnchor } from './runtime.js';
|
|
12
13
|
import { catchLimit, decideCatch, observeCatchSelection, selectCatchPage } from './catch-decisions.js';
|
|
13
14
|
import { expressionContext } from './activity-feed.js';
|
|
14
15
|
import { expressHint } from './hints.js';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { recoveryObservation, entryPresentation, queryStatus, queryParticipants, catchPresence, parseRequiredActivityId, toPublicActivity, toPerceivedActivity, publicActivities, selectHistory, NON_READ, HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT } from './views.js';
|
|
16
|
+
import { asSeatRef, participantAuthority, requireJoinName, requireOwner } from './seats.js';
|
|
17
|
+
import { recoveryObservation, entryPresentation, queryStatus, queryParticipants, catchPresence, parseRequiredActivityId, toPublicActivity, toPerceivedActivity, publicActivities, selectHistory, NON_READ, HISTORY_MAX_LIMIT, HISTORY_DEFAULT_LIMIT } from './views.js';
|
|
18
18
|
import { sessionInboxCounts } from './inbox.js';
|
|
19
|
-
import {
|
|
20
|
-
import { deliverPending } from './delivery-operations.js';
|
|
19
|
+
import { squareIndexPath } from './square-index.js';
|
|
21
20
|
import { participantIdentity } from './participant-identity.js';
|
|
22
21
|
import { joinRecoveryCommand } from './presentation.js';
|
|
23
22
|
import { acquireWatchLease, ownsWatchLease, pulseWatchLease, releaseWatchLease } from './wakes.js';
|
|
23
|
+
import { squareWakeProjection } from './wake-preview.js';
|
|
24
24
|
import { createArtifactPort, createSquare, diagnoseSquareFile, prepareSquareBuild } from './artifact.js';
|
|
25
|
-
import {
|
|
25
|
+
import { activityQueryFilter } from './activity-query.js';
|
|
26
26
|
import { discoverSquares } from './list.js';
|
|
27
27
|
import { streamSquare } from './stream.js';
|
|
28
28
|
export { createSquareResourceOwner } from './artifact.js';
|
|
29
|
+
export { squareClock } from './channel-internal.js';
|
|
29
30
|
export { formatActivityId, parseActivityId, MAX_IDENTITY_SET_SIZE } from './square-core.js';
|
|
30
31
|
export { CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT } from './catch-decisions.js';
|
|
31
32
|
export { HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT } from './views.js';
|
|
32
33
|
export { DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT } from './list.js';
|
|
33
|
-
function checkControl(control) {
|
|
34
|
-
if (control?.signal?.aborted)
|
|
35
|
-
throw control.signal.reason ?? new Error('Square operation was cancelled');
|
|
36
|
-
}
|
|
37
|
-
export function squareClock(env, clock) {
|
|
38
|
-
if (clock !== undefined)
|
|
39
|
-
return clock;
|
|
40
|
-
if (env.SQUARE_NOW_MS === undefined)
|
|
41
|
-
return Date.now;
|
|
42
|
-
const value = Number.parseInt(env.SQUARE_NOW_MS, 10);
|
|
43
|
-
if (!Number.isFinite(value))
|
|
44
|
-
throw new SquareError('invalid_args', 'Invalid SQUARE_NOW_MS: expected integer milliseconds.');
|
|
45
|
-
return () => value;
|
|
46
|
-
}
|
|
47
|
-
function failure(error, detail) {
|
|
48
|
-
const normalized = error instanceof Error ? error : new Error(String(error), { cause: error });
|
|
49
|
-
Object.assign(normalized, { operation: detail });
|
|
50
|
-
return normalized;
|
|
51
|
-
}
|
|
52
34
|
function rejectUnsentActivity(operation) {
|
|
53
35
|
try {
|
|
54
36
|
return operation();
|
|
@@ -59,27 +41,25 @@ function rejectUnsentActivity(operation) {
|
|
|
59
41
|
throw error;
|
|
60
42
|
}
|
|
61
43
|
}
|
|
62
|
-
/** A generic managed address must not displace the receiver's native registration on reconnect. */
|
|
63
|
-
function canRefreshWakeAddress(current, proposed) {
|
|
64
|
-
return proposed.kind !== 'paseo' || current === undefined || current.kind === 'paseo';
|
|
65
|
-
}
|
|
66
44
|
/** One concrete local resource, including its entry-resolved dependencies and lifetime. */
|
|
67
45
|
export class SqliteSquare {
|
|
68
46
|
location;
|
|
69
47
|
artifact;
|
|
70
48
|
clock;
|
|
71
49
|
wakeTransport;
|
|
72
|
-
indexPath;
|
|
73
|
-
closesArtifact;
|
|
74
50
|
deliveryLocation;
|
|
51
|
+
channel;
|
|
75
52
|
constructor(location, artifact, clock, wakeTransport, indexPath, closesArtifact = true, deliveryLocation = location) {
|
|
76
53
|
this.location = location;
|
|
77
54
|
this.artifact = artifact;
|
|
78
55
|
this.clock = clock;
|
|
79
56
|
this.wakeTransport = wakeTransport;
|
|
80
|
-
this.indexPath = indexPath;
|
|
81
|
-
this.closesArtifact = closesArtifact;
|
|
82
57
|
this.deliveryLocation = deliveryLocation;
|
|
58
|
+
this.channel = Channel.fromPort({ location, artifact: channelArtifact(artifact), clock,
|
|
59
|
+
...(wakeTransport === undefined ? {} : { deliverEffect: message => deliverPending({ artifact: channelArtifact(artifact),
|
|
60
|
+
preparePresentation: squareWakeProjection(artifact), transport: wakeTransport, location: deliveryLocation,
|
|
61
|
+
...(message === undefined ? {} : { message }), now: clock() }) }),
|
|
62
|
+
...(indexPath === undefined ? {} : { indexPath }), closesArtifact });
|
|
83
63
|
}
|
|
84
64
|
static async at(input, control) {
|
|
85
65
|
const env = { ...(input.env ?? process.env) }, location = squareLocation({ ...input, env });
|
|
@@ -111,10 +91,115 @@ export class SqliteSquare {
|
|
|
111
91
|
const env = { ...(input.env ?? process.env) };
|
|
112
92
|
return new SqliteSquare(`memory://${crypto.randomUUID()}`, createArtifactPort(':memory:', prepareSquareBuild(input, input.markdown)), squareClock(env, input.clock), input.wakeTransport || undefined);
|
|
113
93
|
}
|
|
114
|
-
/**
|
|
115
|
-
async
|
|
116
|
-
|
|
117
|
-
|
|
94
|
+
/** Agora join composes membership and endpoint custody in one transaction. */
|
|
95
|
+
async joinSeat(name, options, control) {
|
|
96
|
+
checkControl(control);
|
|
97
|
+
const last = options?.last ?? HISTORY_DEFAULT_LIMIT;
|
|
98
|
+
if (!Number.isSafeInteger(last) || last < 1 || last > HISTORY_MAX_LIMIT)
|
|
99
|
+
throw new SquareError('invalid_args', 'Join history limit must be between 1 and 100');
|
|
100
|
+
const owner = requireOwner(options?.owner), wakeAddress = options.wakeAddress, now = this.clock();
|
|
101
|
+
const initial = !options.takeover ? undefined : (await this.artifact.read(facts => facts.participant(name)?.joined ? facts.seat(name) : undefined, control?.signal)).value;
|
|
102
|
+
const authority = initial === undefined ? { kind: 'join', participant: name, owner, wakeAddress }
|
|
103
|
+
: { kind: 'takeover', participant: initial.participant, owner, expectedGeneration: initial.generation, wakeAddress };
|
|
104
|
+
const committed = (await this.artifact.transactAs(authority, facts => {
|
|
105
|
+
const participant = facts.participant(name)?.name ?? name;
|
|
106
|
+
const current = facts.participant(participant), currentSeat = facts.seat(participant);
|
|
107
|
+
let kind = 'joined', activity = null;
|
|
108
|
+
if (initial !== undefined) {
|
|
109
|
+
if (!current?.joined)
|
|
110
|
+
throw new SquareError('already_done', `${participantIdentity(participant)} is no longer standing here`);
|
|
111
|
+
facts.land(coreDone(facts, participant, '', now));
|
|
112
|
+
kind = 'taken-over';
|
|
113
|
+
}
|
|
114
|
+
else if (current?.joined) {
|
|
115
|
+
if (currentSeat === undefined || currentSeat.owner !== owner)
|
|
116
|
+
throw new SquareError('already_joined', `${participantIdentity(participant)} is held by another owner; take it over explicitly`);
|
|
117
|
+
if (currentSeat.attachment === 'detached')
|
|
118
|
+
facts.reattachSeat(asSeatRef(currentSeat), wakeAddress);
|
|
119
|
+
else if (wakeAddress !== undefined && (wakeAddress.kind !== 'paseo' || currentSeat.wakeAddress === undefined || currentSeat.wakeAddress.kind === 'paseo'))
|
|
120
|
+
facts.updateWakeAddress(asSeatRef(currentSeat), wakeAddress);
|
|
121
|
+
kind = 'reconnected';
|
|
122
|
+
}
|
|
123
|
+
if (kind !== 'reconnected') {
|
|
124
|
+
const joinAct = decideJoin(facts, participant, now);
|
|
125
|
+
if (joinAct === undefined)
|
|
126
|
+
throw new SquareError('already_joined', `${participantIdentity(participant)} could not be joined`);
|
|
127
|
+
activity = toPublicActivity(facts.land(joinAct));
|
|
128
|
+
}
|
|
129
|
+
const seat = facts.seat(participant);
|
|
130
|
+
if (seat === undefined)
|
|
131
|
+
throw new SquareError('invalid_args', 'The join activity did not establish its artifact endpoint.');
|
|
132
|
+
return { seat, activity, kind,
|
|
133
|
+
...(!options.observe ? {} : { observation: { ...this.observation(inSquareCount(facts), seat.participant, now),
|
|
134
|
+
deliveryAvailable: seat.wakeAddress !== undefined } }) };
|
|
135
|
+
}, control?.signal)).value;
|
|
136
|
+
await this.channel.remember(owner);
|
|
137
|
+
return committed;
|
|
138
|
+
}
|
|
139
|
+
async requiredSeat(name, { owner }) {
|
|
140
|
+
validateName(name);
|
|
141
|
+
requireOwner(owner);
|
|
142
|
+
return (await this.artifact.read(facts => {
|
|
143
|
+
const participant = facts.participant(name), seat = facts.seat(name);
|
|
144
|
+
if (!participant?.joined || seat === undefined || seat.owner !== owner || seat.attachment !== 'attached') {
|
|
145
|
+
const reason = participant === undefined ? 'has never stepped into this square'
|
|
146
|
+
: !participant.joined ? 'needs a current attached endpoint; join first'
|
|
147
|
+
: seat !== undefined && seat.owner !== owner ? 'is not held by this owner; join before writing'
|
|
148
|
+
: seat?.attachment === 'detached' ? 'is away — join again before writing'
|
|
149
|
+
: 'needs a current attached endpoint; join first';
|
|
150
|
+
throw new SquareError('already_joined', `${participantIdentity(participant?.name ?? name)} ${reason}`, {
|
|
151
|
+
reason: 'needs_seat', join: joinRecoveryCommand(this.location, participant?.name ?? name),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return seat;
|
|
155
|
+
})).value;
|
|
156
|
+
}
|
|
157
|
+
async reattachSeat(name, { owner, wakeAddress }, control) {
|
|
158
|
+
checkControl(control);
|
|
159
|
+
validateName(name);
|
|
160
|
+
requireOwner(owner);
|
|
161
|
+
const captured = (await this.artifact.read(facts => facts.seat(name), control?.signal)).value;
|
|
162
|
+
if (captured === undefined || captured.owner !== owner)
|
|
163
|
+
return null;
|
|
164
|
+
const seat = (await this.artifact.transactAs({ kind: 'join', participant: name, owner, wakeAddress }, facts => {
|
|
165
|
+
const current = facts.seat(name);
|
|
166
|
+
if (!facts.participant(name)?.joined || current === undefined || current.generation !== captured.generation || current.owner !== owner)
|
|
167
|
+
return null;
|
|
168
|
+
if (current.attachment === 'detached')
|
|
169
|
+
facts.reattachSeat(asSeatRef(current), wakeAddress);
|
|
170
|
+
else if (wakeAddress !== undefined && (wakeAddress.kind !== 'paseo' || current.wakeAddress === undefined || current.wakeAddress.kind === 'paseo'))
|
|
171
|
+
facts.updateWakeAddress(asSeatRef(current), wakeAddress);
|
|
172
|
+
return facts.seat(name);
|
|
173
|
+
}, control?.signal)).value;
|
|
174
|
+
if (seat !== null)
|
|
175
|
+
await this.channel.remember(owner);
|
|
176
|
+
return seat;
|
|
177
|
+
}
|
|
178
|
+
async endHeldSeat(name, identity) {
|
|
179
|
+
const captured = (await this.artifact.read(facts => {
|
|
180
|
+
const seat = facts.seat(name);
|
|
181
|
+
return seat?.generation === identity.generation && seat.owner === identity.owner ? seat : undefined;
|
|
182
|
+
})).value;
|
|
183
|
+
if (captured === undefined)
|
|
184
|
+
return null;
|
|
185
|
+
return (await this.artifact.transactAs({ kind: 'host-cleanup', seat: captured }, facts => {
|
|
186
|
+
const current = facts.seat(name);
|
|
187
|
+
if (current === undefined || current.generation !== captured.generation || current.owner !== captured.owner)
|
|
188
|
+
return null;
|
|
189
|
+
return { activity: toPublicActivity(facts.land(coreDone(facts, current.participant, '', this.clock()))) };
|
|
190
|
+
})).value;
|
|
191
|
+
}
|
|
192
|
+
async cancelHeld(name, identity) {
|
|
193
|
+
const captured = (await this.artifact.read(facts => {
|
|
194
|
+
const seat = facts.seat(name);
|
|
195
|
+
return seat?.generation === identity.generation && seat.owner === identity.owner ? seat : undefined;
|
|
196
|
+
})).value;
|
|
197
|
+
if (captured === undefined)
|
|
198
|
+
return false;
|
|
199
|
+
return (await this.artifact.transactAs({ kind: 'participant', seat: captured }, facts => {
|
|
200
|
+
const boundary = facts.channel.header().lastMessageIndex;
|
|
201
|
+
return boundary < 0 ? false : facts.channel.cancelThrough(captured, boundary);
|
|
202
|
+
})).value;
|
|
118
203
|
}
|
|
119
204
|
observation(standingCount, participant, now = this.clock()) {
|
|
120
205
|
return { location: this.location, standingCount, now,
|
|
@@ -130,12 +215,14 @@ export class SqliteSquare {
|
|
|
130
215
|
name;
|
|
131
216
|
owner;
|
|
132
217
|
seat;
|
|
218
|
+
endpoint;
|
|
133
219
|
constructor(name, owner, seat) {
|
|
134
220
|
this.name = name;
|
|
135
221
|
this.owner = owner;
|
|
136
222
|
this.seat = seat;
|
|
223
|
+
this.endpoint = new ChannelEndpoint(owner.channel, seat);
|
|
137
224
|
}
|
|
138
|
-
get generation() { return this.
|
|
225
|
+
get generation() { return this.endpoint.generation; }
|
|
139
226
|
async express(bodyOrInput, optionsOrControl = {}, operationControl) {
|
|
140
227
|
const square = this.owner;
|
|
141
228
|
const input = typeof bodyOrInput === 'object' && bodyOrInput !== null ? bodyOrInput : undefined;
|
|
@@ -179,18 +266,22 @@ export class SqliteSquare {
|
|
|
179
266
|
throw new SquareError('bell_quota', `${participantIdentity(name)} cannot ring the bell yet`, { retryAfterMs: Math.max(1, proposed.nextAt - now) });
|
|
180
267
|
return proposed;
|
|
181
268
|
});
|
|
182
|
-
|
|
269
|
+
const stored = facts.land(decision.act);
|
|
270
|
+
// One explicit projection: a landed say resolves its own Channel content in this transaction.
|
|
271
|
+
const activity = stored.kind === 'say' ? facts.activities([stored.index])[0] : stored;
|
|
272
|
+
if (activity === undefined)
|
|
273
|
+
throw new SquareError('io', 'The landed activity is missing.');
|
|
274
|
+
return { stored, activity, standingCount: inSquareCount(facts),
|
|
183
275
|
ownActivityCount: facts.participant(name).sayCount, hardCap: facts.header().hardCap,
|
|
184
276
|
...(decision.unmatchedMentions === undefined ? {} : { unmatchedMentions: decision.unmatchedMentions }),
|
|
185
277
|
...(decision.replyAuthorNotHere === undefined ? {} : { replyAuthorNotHere: decision.replyAuthorNotHere }) };
|
|
186
278
|
}, control?.signal)).value;
|
|
187
279
|
landedId = formatActivityId(committed.stored.index);
|
|
188
280
|
effect = 'landed';
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const { stored, standingCount, ownActivityCount, hardCap, ...metadata } = committed;
|
|
281
|
+
await square.channel.deliver(committed.stored.kind === 'say' ? committed.stored.messageIndex : undefined);
|
|
282
|
+
const { stored, activity, standingCount, ownActivityCount, hardCap, ...metadata } = committed;
|
|
192
283
|
const hint = expressHint(ownActivityCount);
|
|
193
|
-
return { activity: toPublicActivity(
|
|
284
|
+
return { activity: toPublicActivity(activity), ...metadata,
|
|
194
285
|
...(hint === undefined ? {} : { hint }), ...(context === undefined ? {} : { observation: {
|
|
195
286
|
...square.observation(standingCount, name, now), ownActivityCount, hardCap, context, participant: name,
|
|
196
287
|
} }) };
|
|
@@ -350,58 +441,46 @@ export class SqliteSquare {
|
|
|
350
441
|
const limit = options.limit === undefined ? 100 : options.limit;
|
|
351
442
|
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)
|
|
352
443
|
throw new SquareError('invalid_args', 'Pending limit must be a positive integer no greater than 100.');
|
|
353
|
-
|
|
354
|
-
|
|
444
|
+
// Pending follows the shared archive cursor: act/N with before/after, never a Channel msg/N cursor.
|
|
445
|
+
const shared = activityQueryFilter(options);
|
|
446
|
+
const filter = { kinds: ['say'], unseenOnly: true,
|
|
447
|
+
...(shared.indexes === undefined ? {} : { indexes: shared.indexes }),
|
|
448
|
+
...(shared.actors === undefined ? {} : { senders: shared.actors }),
|
|
449
|
+
...(shared.metadataMatch === undefined ? {} : { metadataMatch: shared.metadataMatch }),
|
|
450
|
+
...(shared.search === undefined ? {} : { search: shared.search }),
|
|
451
|
+
...(shared.afterIndex === undefined ? {} : { afterIndex: shared.afterIndex }),
|
|
452
|
+
...(shared.beforeIndex === undefined ? {} : { beforeIndex: shared.beforeIndex }) };
|
|
453
|
+
return (await this.owner.artifact.read(facts => {
|
|
355
454
|
assertCurrentSeat(facts, asSeatRef(this.seat));
|
|
356
|
-
const filter = { ...query, senders: actors, unseenOnly: true };
|
|
357
455
|
const heads = facts.directed(this.name, filter, { edge: 'oldest', limit });
|
|
358
|
-
|
|
359
|
-
.
|
|
360
|
-
return { activities, remaining: facts.directedCount(this.name, filter) - activities.length };
|
|
456
|
+
return { activities: facts.activities(heads.items.map(head => head.index)).map(activity => toPerceivedActivity(facts, activity, this.name)),
|
|
457
|
+
remaining: facts.directedCount(this.name, filter) - heads.items.length };
|
|
361
458
|
}, control?.signal)).value;
|
|
362
459
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
});
|
|
460
|
+
ack(ids, control) {
|
|
461
|
+
const square = this.owner;
|
|
462
|
+
return square.artifact.transactAs(participantAuthority(this.seat), facts => {
|
|
463
|
+
const indexes = [];
|
|
464
|
+
for (const id of ids) {
|
|
465
|
+
const activity = facts.activity(parseRequiredActivityId(id));
|
|
466
|
+
if (activity?.kind !== 'say' || activity.messageIndex === undefined)
|
|
467
|
+
throw new SquareError('invalid_args', `${id} is not addressed to you.`, { reason: 'not_addressed', activityId: id });
|
|
468
|
+
const head = facts.directed(this.name, { indexes: [activity.index], kinds: ['say'] }, { edge: 'oldest', limit: 1 }).items[0];
|
|
469
|
+
if (head === undefined)
|
|
470
|
+
throw new SquareError('invalid_args', `${id} is not addressed to you.`, { reason: 'not_addressed', activityId: id });
|
|
471
|
+
indexes.push(activity.messageIndex);
|
|
374
472
|
}
|
|
375
|
-
return { consumed: facts.markSeen(this.name, indexes, square.clock()) };
|
|
376
|
-
}, control?.signal)
|
|
473
|
+
return { consumed: facts.channel.markSeen(this.name, indexes, square.clock()) };
|
|
474
|
+
}, control?.signal).then(result => result.value);
|
|
377
475
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const match = validateMetadataMatch(input?.match), duration = validateDeferralDuration(input?.durationMs);
|
|
381
|
-
const square = this.owner, seat = this.seat;
|
|
382
|
-
return (await square.artifact.transactAs(participantAuthority(seat), facts => {
|
|
383
|
-
const now = square.clock(), expiresAt = now + duration, id = crypto.randomUUID();
|
|
384
|
-
const result = { id, expiresAt: new Date(expiresAt).toISOString() };
|
|
385
|
-
facts.putDeliveryDeferral(asSeatRef(seat), id, match, expiresAt, now);
|
|
386
|
-
return result;
|
|
387
|
-
}, control?.signal)).value;
|
|
476
|
+
deferDelivery(input, control) {
|
|
477
|
+
return this.endpoint.deferDelivery(input, control);
|
|
388
478
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
validateDeferralId(id);
|
|
392
|
-
const duration = validateDeferralDuration(durationMs), square = this.owner;
|
|
393
|
-
const seat = this.seat;
|
|
394
|
-
return (await square.artifact.transactAs(participantAuthority(seat), facts => {
|
|
395
|
-
const now = square.clock(), expiresAt = now + duration;
|
|
396
|
-
const result = { id, expiresAt: new Date(expiresAt).toISOString() };
|
|
397
|
-
return facts.renewDeliveryDeferral(asSeatRef(seat), id, expiresAt, now) ? result : null;
|
|
398
|
-
}, control?.signal)).value;
|
|
479
|
+
renewDeliveryDeferral(id, durationMs, control) {
|
|
480
|
+
return this.endpoint.renewDeliveryDeferral(id, durationMs, control);
|
|
399
481
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
validateDeferralId(id);
|
|
403
|
-
const square = this.owner, seat = this.seat;
|
|
404
|
-
return (await square.artifact.transactAs(participantAuthority(seat), facts => facts.releaseDeliveryDeferral(asSeatRef(seat), id, square.clock()), control?.signal)).value;
|
|
482
|
+
releaseDeliveryDeferral(id, control) {
|
|
483
|
+
return this.endpoint.releaseDeliveryDeferral(id, control);
|
|
405
484
|
}
|
|
406
485
|
async done(body, options = {}, control) {
|
|
407
486
|
validateDoneBody(body);
|
|
@@ -415,152 +494,37 @@ export class SqliteSquare {
|
|
|
415
494
|
}, control?.signal)).value;
|
|
416
495
|
}
|
|
417
496
|
};
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
checkControl(control);
|
|
421
|
-
const { from, to, body, truncate = ATTENTION_BODY_MAX } = input;
|
|
422
|
-
if (typeof from !== 'string')
|
|
423
|
-
throw new SquareError('invalid_args', 'Channel sender must be a name.');
|
|
424
|
-
validateName(from);
|
|
425
|
-
const targets = typeof to === 'string' ? [to] : to;
|
|
426
|
-
if (!Array.isArray(targets) || targets.length < 1 || targets.length > MAX_IDENTITY_SET_SIZE)
|
|
427
|
-
throw new SquareError('invalid_args', `Channel needs between 1 and ${MAX_IDENTITY_SET_SIZE} recipients.`);
|
|
428
|
-
const unique = [];
|
|
429
|
-
for (const target of targets) {
|
|
430
|
-
if (typeof target !== 'string')
|
|
431
|
-
throw new SquareError('invalid_args', 'Channel recipients must be names.');
|
|
432
|
-
validateName(target);
|
|
433
|
-
if (!unique.some(name => sameName(name, target)))
|
|
434
|
-
unique.push(target);
|
|
435
|
-
}
|
|
436
|
-
if (typeof body !== 'string' || body.trim() === '')
|
|
437
|
-
throw new SquareError('invalid_args', 'Channel body must be non-empty.');
|
|
438
|
-
if (truncate !== false && (!Number.isSafeInteger(truncate) || truncate < 1))
|
|
439
|
-
throw new SquareError('invalid_args', 'Channel truncate must be false or a positive integer.');
|
|
440
|
-
const fullBody = body.replace(/\r\n/g, '\n');
|
|
441
|
-
const preview = truncate !== false && fullBody.length > truncate
|
|
442
|
-
? `${fullBody.slice(0, truncate).trimEnd()}… (truncated ${fullBody.length - truncate} chars)`
|
|
443
|
-
: undefined;
|
|
444
|
-
const delivery = preview === undefined ? { mode: 'direct' } : { mode: 'direct', preview };
|
|
445
|
-
const metadata = validateActivityMetadata(input.metadata);
|
|
446
|
-
const { stored, resolvedTo } = (await this.artifact.transactAs({ kind: 'send', actor: from }, facts => {
|
|
447
|
-
const standing = facts.roster().filter(p => p.joined), resolvedTo = [], missing = [];
|
|
448
|
-
for (const target of unique) {
|
|
449
|
-
const recipient = standing.find(p => sameName(p.name, target));
|
|
450
|
-
if (recipient === undefined)
|
|
451
|
-
missing.push(target);
|
|
452
|
-
else
|
|
453
|
-
resolvedTo.push(recipient.name);
|
|
454
|
-
}
|
|
455
|
-
if (missing.length > 0)
|
|
456
|
-
throw new SquareError('invalid_args', `Channel recipients are not standing: ${missing.join(', ')}`, {
|
|
457
|
-
reason: 'not_standing', targets: missing, activityUnsent: true,
|
|
458
|
-
});
|
|
459
|
-
return { stored: facts.land({ kind: 'say', actor: from, at: this.clock(), body: fullBody,
|
|
460
|
-
...(activityMetadataHasContent(metadata) ? { metadata } : {}), delivery, mentions: resolvedTo }), resolvedTo };
|
|
461
|
-
}, control?.signal)).value;
|
|
462
|
-
if (this.wakeTransport !== undefined)
|
|
463
|
-
await deliverPending({ artifact: this.artifact, transport: this.wakeTransport, location: this.deliveryLocation,
|
|
464
|
-
activity: stored.index, now: stored.at }).catch(() => undefined);
|
|
465
|
-
return { id: formatActivityId(stored.index), at: stored.at, from: stored.actor, to: resolvedTo, body: preview ?? fullBody,
|
|
466
|
-
...(stored.kind !== 'say' || stored.metadata === undefined ? {} : { metadata: stored.metadata }) };
|
|
497
|
+
send(input, control) {
|
|
498
|
+
return this.channel.send(input, control);
|
|
467
499
|
}
|
|
468
|
-
async participant(name,
|
|
469
|
-
|
|
470
|
-
requireOwner(owner);
|
|
471
|
-
return (await this.artifact.read(facts => {
|
|
472
|
-
const participant = facts.participant(name), seat = facts.seat(name);
|
|
473
|
-
if (!participant?.joined || seat === undefined || seat.owner !== owner || seat.attachment !== 'attached') {
|
|
474
|
-
const reason = participant === undefined ? 'has never stepped into this square'
|
|
475
|
-
: !participant.joined ? 'needs a current attached seat; join first'
|
|
476
|
-
: seat !== undefined && seat.owner !== owner ? 'is not held by this owner; join before writing'
|
|
477
|
-
: seat?.attachment === 'detached' ? 'is away — join again before writing'
|
|
478
|
-
: 'needs a current attached seat; join first';
|
|
479
|
-
throw new SquareError('already_joined', `${participantIdentity(participant?.name ?? name)} ${reason}`, {
|
|
480
|
-
reason: 'needs_seat', join: joinRecoveryCommand(this.location, participant?.name ?? name),
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
return this.handle(seat);
|
|
484
|
-
})).value;
|
|
500
|
+
async participant(name, options) {
|
|
501
|
+
return this.handle(await this.requiredSeat(name, options));
|
|
485
502
|
}
|
|
486
503
|
async join(requested, options, control) {
|
|
487
504
|
let name = requested;
|
|
488
505
|
try {
|
|
489
506
|
checkControl(control);
|
|
490
507
|
name = requireJoinName(requested);
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const initial = !options.takeover ? undefined : (await this.artifact.read(facts => facts.participant(name)?.joined ? facts.seat(name) : undefined, control?.signal)).value;
|
|
496
|
-
const authority = initial === undefined ? { kind: 'join', participant: name, owner, wakeAddress }
|
|
497
|
-
: { kind: 'takeover', participant: initial.participant, owner, expectedGeneration: initial.generation, wakeAddress };
|
|
498
|
-
const committed = (await this.artifact.transactAs(authority, facts => {
|
|
499
|
-
const participant = facts.participant(name)?.name ?? name;
|
|
500
|
-
const current = facts.participant(participant), currentSeat = facts.seat(participant);
|
|
501
|
-
let kind = 'joined', activity = null;
|
|
502
|
-
if (initial !== undefined) {
|
|
503
|
-
if (!current?.joined)
|
|
504
|
-
throw new SquareError('already_done', `${participantIdentity(participant)} is no longer standing here`);
|
|
505
|
-
facts.land(coreDone(facts, participant, '', now));
|
|
506
|
-
kind = 'taken-over';
|
|
507
|
-
}
|
|
508
|
-
else if (current?.joined) {
|
|
509
|
-
if (currentSeat === undefined || currentSeat.owner !== owner)
|
|
510
|
-
throw new SquareError('already_joined', `${participantIdentity(participant)} is held by another owner; take it over explicitly`);
|
|
511
|
-
if (currentSeat.attachment === 'detached')
|
|
512
|
-
facts.reattachSeat(asSeatRef(currentSeat), wakeAddress);
|
|
513
|
-
else if (wakeAddress !== undefined && canRefreshWakeAddress(currentSeat.wakeAddress, wakeAddress))
|
|
514
|
-
facts.updateWakeAddress(asSeatRef(currentSeat), wakeAddress);
|
|
515
|
-
kind = 'reconnected';
|
|
516
|
-
}
|
|
517
|
-
if (kind !== 'reconnected') {
|
|
518
|
-
const joinAct = decideJoin(facts, participant, now);
|
|
519
|
-
if (joinAct === undefined)
|
|
520
|
-
throw new SquareError('already_joined', `${participantIdentity(participant)} could not be joined`);
|
|
521
|
-
activity = toPublicActivity(facts.land(joinAct));
|
|
522
|
-
}
|
|
523
|
-
const seat = facts.seat(participant);
|
|
524
|
-
if (seat === undefined)
|
|
525
|
-
throw new SquareError('invalid_args', 'The join activity did not establish its artifact seat.');
|
|
526
|
-
return { participant: this.handle(seat), activity, kind,
|
|
527
|
-
...(!options.observe ? {} : { observation: { ...this.observation(inSquareCount(facts), seat.participant, now),
|
|
528
|
-
deliveryAvailable: seat.wakeAddress !== undefined } }) };
|
|
529
|
-
}, control?.signal)).value;
|
|
530
|
-
await this.publishSeat(owner);
|
|
508
|
+
const committed = await this.joinSeat(name, options, control);
|
|
509
|
+
const participant = this.handle(committed.seat);
|
|
510
|
+
const result = { participant, activity: committed.activity, kind: committed.kind,
|
|
511
|
+
...(committed.observation === undefined ? {} : { observation: committed.observation }) };
|
|
531
512
|
if (!options.observe)
|
|
532
|
-
return
|
|
513
|
+
return result;
|
|
533
514
|
let entry;
|
|
534
515
|
try {
|
|
535
|
-
entry = (await this.artifact.read(facts => entryPresentation(facts,
|
|
516
|
+
entry = (await this.artifact.read(facts => entryPresentation(facts, participant.name, options.last ?? HISTORY_DEFAULT_LIMIT), control?.signal)).value;
|
|
536
517
|
}
|
|
537
518
|
catch { /* Keep the committed join even when optional history is unavailable. */ }
|
|
538
|
-
return { ...
|
|
519
|
+
return { ...result, observation: { ...committed.observation,
|
|
520
|
+
...(entry === undefined ? { enrichmentUnavailable: true } : { entry }) } };
|
|
539
521
|
}
|
|
540
522
|
catch (error) {
|
|
541
523
|
throw failure(error, { operation: 'join', subject: 'caller', participant: name });
|
|
542
524
|
}
|
|
543
525
|
}
|
|
544
|
-
/** Restore only a standing seat; a concurrent departure or takeover cannot be revived. */
|
|
545
526
|
async reattach(name, { owner, wakeAddress }, control) {
|
|
546
|
-
|
|
547
|
-
validateName(name);
|
|
548
|
-
requireOwner(owner);
|
|
549
|
-
const captured = (await this.artifact.read(facts => facts.seat(name), control?.signal)).value;
|
|
550
|
-
if (captured === undefined || captured.owner !== owner)
|
|
551
|
-
return null;
|
|
552
|
-
const seat = (await this.artifact.transactAs({ kind: 'join', participant: name, owner, wakeAddress }, facts => {
|
|
553
|
-
const current = facts.seat(name);
|
|
554
|
-
if (!facts.participant(name)?.joined || current === undefined || current.generation !== captured.generation || current.owner !== owner)
|
|
555
|
-
return null;
|
|
556
|
-
if (current.attachment === 'detached')
|
|
557
|
-
facts.reattachSeat(asSeatRef(current), wakeAddress);
|
|
558
|
-
else if (wakeAddress !== undefined && canRefreshWakeAddress(current.wakeAddress, wakeAddress))
|
|
559
|
-
facts.updateWakeAddress(asSeatRef(current), wakeAddress);
|
|
560
|
-
return facts.seat(name);
|
|
561
|
-
}, control?.signal)).value;
|
|
562
|
-
if (seat !== null)
|
|
563
|
-
await this.publishSeat(owner);
|
|
527
|
+
const seat = await this.reattachSeat(name, { owner, wakeAddress }, control);
|
|
564
528
|
return seat === null ? null : this.handle(seat);
|
|
565
529
|
}
|
|
566
530
|
async history(query = {}, control) {
|
|
@@ -575,7 +539,7 @@ export class SqliteSquare {
|
|
|
575
539
|
throw new SquareError('invalid_args', 'History context must be a non-negative safe integer');
|
|
576
540
|
const limit = query.limit === undefined ? (query.at?.length ? null : HISTORY_DEFAULT_LIMIT) : query.limit;
|
|
577
541
|
const contextual = !!query.at?.length || query.contextBefore !== undefined || query.contextAfter !== undefined;
|
|
578
|
-
const kinds = contextual || query.since !== undefined ?
|
|
542
|
+
const kinds = contextual || query.since !== undefined ? [...NON_READ, 'read'] : NON_READ;
|
|
579
543
|
return (await this.artifact.read(facts => {
|
|
580
544
|
const selected = selectHistory(facts, query, limit, kinds, query.observe === true);
|
|
581
545
|
if (contextual && limit === null && selected.totalMatches > HISTORY_MAX_LIMIT)
|
|
@@ -611,40 +575,20 @@ export class SqliteSquare {
|
|
|
611
575
|
return streamSquare(this.location, options, control);
|
|
612
576
|
}
|
|
613
577
|
async seatsOf(owner) {
|
|
614
|
-
|
|
615
|
-
return (await this.artifact.read(facts => facts.seatsForOwner(owner).map(({ participant: name, generation, attachment }) => ({ name, generation, attachment })))).value;
|
|
578
|
+
return this.channel.endpointsOf(owner);
|
|
616
579
|
}
|
|
617
|
-
close() { return this.
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
const seat = (await this.artifact.read(facts => facts.seat(name))).value;
|
|
621
|
-
return seat?.generation === generation && seat.owner === owner ? seat : undefined;
|
|
622
|
-
}
|
|
623
|
-
async cancelDeliveries(name, identity) {
|
|
624
|
-
const seat = await this.heldSeat(name, identity);
|
|
625
|
-
return seat === undefined ? false : this.artifact.cancelDeliveries(asSeatRef(seat));
|
|
580
|
+
close() { return this.channel.close(); }
|
|
581
|
+
cancelDeliveries(name, identity) {
|
|
582
|
+
return this.cancelHeld(name, identity);
|
|
626
583
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
if (captured === undefined)
|
|
630
|
-
return null;
|
|
631
|
-
const seat = asSeatRef(captured);
|
|
632
|
-
return (await this.artifact.transactAs({ kind: 'host-cleanup', seat }, facts => facts.detachSeat(seat) ? true : null)).value;
|
|
584
|
+
detach(name, identity) {
|
|
585
|
+
return this.channel.detach(name, identity);
|
|
633
586
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
if (captured === undefined)
|
|
637
|
-
return null;
|
|
638
|
-
return (await this.artifact.transactAs({ kind: 'host-cleanup', seat: asSeatRef(captured) }, facts => {
|
|
639
|
-
const current = facts.seat(name);
|
|
640
|
-
if (current === undefined || current.generation !== captured.generation || current.owner !== captured.owner)
|
|
641
|
-
return null;
|
|
642
|
-
return { activity: toPublicActivity(facts.land(coreDone(facts, current.participant, '', this.clock()))) };
|
|
643
|
-
})).value;
|
|
587
|
+
endHeld(name, identity) {
|
|
588
|
+
return this.endHeldSeat(name, identity);
|
|
644
589
|
}
|
|
645
590
|
}
|
|
646
591
|
export function diagnoseSquare(input, control) { checkControl(control); return diagnoseSquareFile(squareLocation(input)); }
|
|
647
|
-
export function migrateSquare(input, control) { checkControl(control); const env = input.env ?? process.env; return migrateSquareFile(squareLocation(input), control?.signal, env); }
|
|
648
592
|
export function discover(input, options = {}, control) {
|
|
649
593
|
if (input.path !== undefined || (input.env ?? process.env).SQUARE_LOCATION)
|
|
650
594
|
squareLocation(input);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SquareError, type FactPage, type IndexPage } from './neutral.js';
|
|
2
|
+
export type Row = Record<string, string | number | null>;
|
|
3
|
+
export declare const encode: {
|
|
4
|
+
(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string;
|
|
5
|
+
(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
|
|
6
|
+
};
|
|
7
|
+
export declare const key: (name: string) => string;
|
|
8
|
+
export declare function invalidArtifact(detail: string): SquareError;
|
|
9
|
+
export declare function requireFact(ok: unknown, detail?: string): asserts ok;
|
|
10
|
+
export declare function integer(value: unknown, minimum?: number): number;
|
|
11
|
+
export declare function finite(value: unknown): number;
|
|
12
|
+
export declare function string(value: unknown, nonempty?: boolean): string;
|
|
13
|
+
export declare function json(value: unknown): unknown;
|
|
14
|
+
export declare function object(value: unknown): Record<string, unknown>;
|
|
15
|
+
export declare function shape(value: unknown, required: readonly string[], optional?: readonly string[]): Record<string, unknown>;
|
|
16
|
+
export declare function strings(value: unknown): string[];
|
|
17
|
+
export declare function decoded(value: unknown, nonempty?: boolean): string;
|
|
18
|
+
export declare function immutable<T>(value: T): T;
|
|
19
|
+
export declare function stableJson(value: unknown): string;
|
|
20
|
+
export declare function pageRows<T>(rows: Row[], page: IndexPage, decode: (row: Row) => T): FactPage<T>;
|
|
21
|
+
export declare function pageLimit(page: IndexPage): number;
|