@starcite/sdk 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -15
- package/dist/index.cjs +357 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +137 -83
- package/dist/index.d.ts +137 -83
- package/dist/index.js +355 -141
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,8 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
|
|
34
|
-
LocalStorageCursorStore: () => LocalStorageCursorStore,
|
|
33
|
+
LocalStorageSessionStore: () => LocalStorageSessionStore,
|
|
35
34
|
MemoryStore: () => MemoryStore,
|
|
36
35
|
SessionLogConflictError: () => SessionLogConflictError,
|
|
37
36
|
SessionLogGapError: () => SessionLogGapError,
|
|
@@ -45,7 +44,7 @@ __export(index_exports, {
|
|
|
45
44
|
StarciteSession: () => StarciteSession,
|
|
46
45
|
StarciteTailError: () => StarciteTailError,
|
|
47
46
|
StarciteTokenExpiredError: () => StarciteTokenExpiredError,
|
|
48
|
-
|
|
47
|
+
WebStorageSessionStore: () => WebStorageSessionStore
|
|
49
48
|
});
|
|
50
49
|
module.exports = __toCommonJS(index_exports);
|
|
51
50
|
|
|
@@ -129,13 +128,14 @@ function inferIdentityFromApiKey(apiKey) {
|
|
|
129
128
|
const claims = ApiKeyClaimsSchema.parse((0, import_jose.decodeJwt)(apiKey));
|
|
130
129
|
const id = claims.principal_id ?? claims.sub;
|
|
131
130
|
const tenantId = claims.tenant_id;
|
|
132
|
-
|
|
131
|
+
const type = claims.principal_type ?? "user";
|
|
132
|
+
if (!(tenantId && id)) {
|
|
133
133
|
return void 0;
|
|
134
134
|
}
|
|
135
135
|
return new StarciteIdentity({
|
|
136
136
|
tenantId,
|
|
137
137
|
id,
|
|
138
|
-
type
|
|
138
|
+
type
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
141
|
function decodeSessionToken(token) {
|
|
@@ -336,7 +336,7 @@ var SessionLog = class {
|
|
|
336
336
|
this.enforceRetention();
|
|
337
337
|
return true;
|
|
338
338
|
}
|
|
339
|
-
|
|
339
|
+
state(syncing) {
|
|
340
340
|
return {
|
|
341
341
|
events: this.history.slice(),
|
|
342
342
|
lastSeq: this.appliedSeq,
|
|
@@ -939,7 +939,7 @@ var TailStream = class {
|
|
|
939
939
|
this.follow = follow;
|
|
940
940
|
this.shouldReconnect = follow ? opts.reconnect ?? true : false;
|
|
941
941
|
this.catchUpIdleMs = opts.catchUpIdleMs ?? 1e3;
|
|
942
|
-
this.connectionTimeoutMs = opts.connectionTimeoutMs ??
|
|
942
|
+
this.connectionTimeoutMs = opts.connectionTimeoutMs ?? 12e3;
|
|
943
943
|
this.inactivityTimeoutMs = opts.inactivityTimeoutMs;
|
|
944
944
|
this.maxBufferedBatches = opts.maxBufferedBatches ?? 1024;
|
|
945
945
|
this.signal = opts.signal;
|
|
@@ -1310,8 +1310,10 @@ var StarciteSession = class {
|
|
|
1310
1310
|
store;
|
|
1311
1311
|
lifecycle = new import_eventemitter33.default();
|
|
1312
1312
|
eventSubscriptions = /* @__PURE__ */ new Map();
|
|
1313
|
+
appendTask = Promise.resolve();
|
|
1313
1314
|
liveSyncController;
|
|
1314
1315
|
liveSyncTask;
|
|
1316
|
+
liveSyncCatchUpActive = false;
|
|
1315
1317
|
constructor(options) {
|
|
1316
1318
|
this.id = options.id;
|
|
1317
1319
|
this.token = options.token;
|
|
@@ -1321,8 +1323,8 @@ var StarciteSession = class {
|
|
|
1321
1323
|
this.producerId = crypto.randomUUID();
|
|
1322
1324
|
this.store = options.store;
|
|
1323
1325
|
this.log = new SessionLog(options.logOptions);
|
|
1324
|
-
const storedState = this.store
|
|
1325
|
-
if (storedState) {
|
|
1326
|
+
const storedState = this.store?.load(this.id);
|
|
1327
|
+
if (storedState !== void 0) {
|
|
1326
1328
|
this.log.hydrate(storedState);
|
|
1327
1329
|
}
|
|
1328
1330
|
}
|
|
@@ -1331,28 +1333,35 @@ var StarciteSession = class {
|
|
|
1331
1333
|
*
|
|
1332
1334
|
* The SDK manages `actor`, `producer_id`, and `producer_seq` automatically.
|
|
1333
1335
|
*/
|
|
1334
|
-
|
|
1336
|
+
append(input, options) {
|
|
1335
1337
|
const parsed = SessionAppendInputSchema.parse(input);
|
|
1336
|
-
this.
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1338
|
+
const runAppend = this.appendTask.then(async () => {
|
|
1339
|
+
this.producerSeq += 1;
|
|
1340
|
+
const result = await this.appendRaw(
|
|
1341
|
+
{
|
|
1342
|
+
type: parsed.type ?? "content",
|
|
1343
|
+
payload: parsed.payload ?? { text: parsed.text },
|
|
1344
|
+
actor: parsed.actor ?? this.identity.toActor(),
|
|
1345
|
+
producer_id: this.producerId,
|
|
1346
|
+
producer_seq: this.producerSeq,
|
|
1347
|
+
source: parsed.source ?? "agent",
|
|
1348
|
+
metadata: parsed.metadata,
|
|
1349
|
+
refs: parsed.refs,
|
|
1350
|
+
idempotency_key: parsed.idempotencyKey,
|
|
1351
|
+
expected_seq: parsed.expectedSeq
|
|
1352
|
+
},
|
|
1353
|
+
options
|
|
1354
|
+
);
|
|
1355
|
+
return {
|
|
1356
|
+
seq: result.seq,
|
|
1357
|
+
deduped: result.deduped
|
|
1358
|
+
};
|
|
1359
|
+
});
|
|
1360
|
+
this.appendTask = runAppend.then(
|
|
1361
|
+
() => void 0,
|
|
1362
|
+
() => void 0
|
|
1351
1363
|
);
|
|
1352
|
-
return
|
|
1353
|
-
seq: result.seq,
|
|
1354
|
-
deduped: result.deduped
|
|
1355
|
-
};
|
|
1364
|
+
return runAppend;
|
|
1356
1365
|
}
|
|
1357
1366
|
/**
|
|
1358
1367
|
* Appends a raw event payload as-is. Caller manages all fields.
|
|
@@ -1369,11 +1378,33 @@ var StarciteSession = class {
|
|
|
1369
1378
|
AppendEventResponseSchema
|
|
1370
1379
|
);
|
|
1371
1380
|
}
|
|
1372
|
-
on(eventName, listener) {
|
|
1381
|
+
on(eventName, listener, options) {
|
|
1373
1382
|
if (eventName === "event") {
|
|
1374
1383
|
const eventListener = listener;
|
|
1375
1384
|
if (!this.eventSubscriptions.has(eventListener)) {
|
|
1376
|
-
const
|
|
1385
|
+
const eventOptions = options;
|
|
1386
|
+
const replay = eventOptions?.replay ?? true;
|
|
1387
|
+
const replayCutoffSeq = replay ? this.log.lastSeq : -1;
|
|
1388
|
+
const schema = eventOptions?.schema;
|
|
1389
|
+
const dispatch = (event) => {
|
|
1390
|
+
const parsedEvent = this.parseOnEvent(event, schema);
|
|
1391
|
+
if (!parsedEvent) {
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
const classifiedContext = this.resolveEventContext(
|
|
1395
|
+
event.seq,
|
|
1396
|
+
replayCutoffSeq,
|
|
1397
|
+
this.liveSyncCatchUpActive
|
|
1398
|
+
);
|
|
1399
|
+
try {
|
|
1400
|
+
this.observeEventListenerResult(
|
|
1401
|
+
eventListener(parsedEvent, classifiedContext)
|
|
1402
|
+
);
|
|
1403
|
+
} catch (error) {
|
|
1404
|
+
this.emitStreamError(error);
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
const unsubscribe = this.log.subscribe(dispatch, { replay });
|
|
1377
1408
|
this.eventSubscriptions.set(eventListener, unsubscribe);
|
|
1378
1409
|
}
|
|
1379
1410
|
this.ensureLiveSync();
|
|
@@ -1435,77 +1466,195 @@ var StarciteSession = class {
|
|
|
1435
1466
|
this.persistLogState();
|
|
1436
1467
|
}
|
|
1437
1468
|
/**
|
|
1438
|
-
* Returns a stable
|
|
1469
|
+
* Returns a stable view of the current canonical in-memory log state.
|
|
1439
1470
|
*/
|
|
1440
|
-
|
|
1441
|
-
return this.log.
|
|
1442
|
-
}
|
|
1443
|
-
/**
|
|
1444
|
-
* Streams tail events one at a time via callback.
|
|
1445
|
-
*/
|
|
1446
|
-
async tail(onEvent, options = {}) {
|
|
1447
|
-
await this.tailBatches(async (batch) => {
|
|
1448
|
-
for (const event of batch) {
|
|
1449
|
-
await onEvent(event);
|
|
1450
|
-
}
|
|
1451
|
-
}, options);
|
|
1471
|
+
state() {
|
|
1472
|
+
return this.log.state(this.liveSyncTask !== void 0);
|
|
1452
1473
|
}
|
|
1453
1474
|
/**
|
|
1454
|
-
*
|
|
1475
|
+
* Returns the retained canonical event list.
|
|
1455
1476
|
*/
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
sessionId: this.id,
|
|
1459
|
-
token: this.token,
|
|
1460
|
-
websocketBaseUrl: this.transport.websocketBaseUrl,
|
|
1461
|
-
websocketFactory: this.transport.websocketFactory,
|
|
1462
|
-
options
|
|
1463
|
-
}).subscribe(onBatch);
|
|
1477
|
+
events() {
|
|
1478
|
+
return this.log.events;
|
|
1464
1479
|
}
|
|
1465
1480
|
/**
|
|
1466
|
-
*
|
|
1481
|
+
* Streams canonical events as an async iterator.
|
|
1482
|
+
*
|
|
1483
|
+
* Replay semantics and schema validation mirror `session.on("event", ...)`.
|
|
1467
1484
|
*/
|
|
1468
|
-
|
|
1469
|
-
const {
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
+
tail(options = {}) {
|
|
1486
|
+
const { replay = true, schema, ...tailOptions } = options;
|
|
1487
|
+
const replayCutoffSeq = replay ? this.log.lastSeq : -1;
|
|
1488
|
+
const startCursor = tailOptions.cursor ?? this.log.lastSeq;
|
|
1489
|
+
const session = this;
|
|
1490
|
+
const parseEvent = (event) => session.parseTailEvent(event, schema);
|
|
1491
|
+
return {
|
|
1492
|
+
async *[Symbol.asyncIterator]() {
|
|
1493
|
+
if (replay) {
|
|
1494
|
+
for (const replayEvent of session.log.events) {
|
|
1495
|
+
yield {
|
|
1496
|
+
event: parseEvent(replayEvent),
|
|
1497
|
+
context: { phase: "replay", replayed: true }
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
yield* session.iterateLiveTail({
|
|
1502
|
+
parseEvent,
|
|
1503
|
+
replayCutoffSeq,
|
|
1504
|
+
startCursor,
|
|
1505
|
+
tailOptions
|
|
1506
|
+
});
|
|
1485
1507
|
}
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
parseOnEvent(event, schema) {
|
|
1511
|
+
if (!schema) {
|
|
1512
|
+
return event;
|
|
1486
1513
|
}
|
|
1487
|
-
|
|
1514
|
+
try {
|
|
1515
|
+
return schema.parse(event);
|
|
1516
|
+
} catch (error) {
|
|
1517
|
+
this.emitStreamError(
|
|
1518
|
+
new StarciteError(
|
|
1519
|
+
`session.on("event") schema validation failed for session '${this.id}': ${error instanceof Error ? error.message : String(error)}`
|
|
1520
|
+
)
|
|
1521
|
+
);
|
|
1522
|
+
return void 0;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
parseTailEvent(event, schema) {
|
|
1526
|
+
if (!schema) {
|
|
1527
|
+
return event;
|
|
1528
|
+
}
|
|
1529
|
+
try {
|
|
1530
|
+
return schema.parse(event);
|
|
1531
|
+
} catch (error) {
|
|
1532
|
+
throw new StarciteError(
|
|
1533
|
+
`session.tail() schema validation failed for session '${this.id}': ${error instanceof Error ? error.message : String(error)}`
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
resolveEventContext(eventSeq, replayCutoffSeq, forceReplay = false) {
|
|
1538
|
+
const replayed = forceReplay || eventSeq <= replayCutoffSeq;
|
|
1539
|
+
return replayed ? { phase: "replay", replayed: true } : { phase: "live", replayed: false };
|
|
1540
|
+
}
|
|
1541
|
+
observeEventListenerResult(result) {
|
|
1542
|
+
Promise.resolve(result).catch((error) => {
|
|
1543
|
+
this.emitStreamError(error);
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
createTailAbortController(outerSignal) {
|
|
1547
|
+
const controller = new AbortController();
|
|
1548
|
+
if (!outerSignal) {
|
|
1549
|
+
return { controller, detach: () => void 0 };
|
|
1550
|
+
}
|
|
1551
|
+
const abortFromOuterSignal = () => {
|
|
1552
|
+
controller.abort(outerSignal.reason);
|
|
1553
|
+
};
|
|
1554
|
+
if (outerSignal.aborted) {
|
|
1555
|
+
controller.abort(outerSignal.reason);
|
|
1556
|
+
return { controller, detach: () => void 0 };
|
|
1557
|
+
}
|
|
1558
|
+
outerSignal.addEventListener("abort", abortFromOuterSignal, { once: true });
|
|
1559
|
+
return {
|
|
1560
|
+
controller,
|
|
1561
|
+
detach: () => {
|
|
1562
|
+
outerSignal.removeEventListener("abort", abortFromOuterSignal);
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
createTailRuntime({
|
|
1567
|
+
parseEvent,
|
|
1568
|
+
replayCutoffSeq,
|
|
1569
|
+
startCursor,
|
|
1570
|
+
tailOptions
|
|
1571
|
+
}) {
|
|
1572
|
+
const queue = [];
|
|
1573
|
+
let notify;
|
|
1574
|
+
let done = false;
|
|
1575
|
+
let failure;
|
|
1576
|
+
const shouldApplyToLog = tailOptions.agent === void 0;
|
|
1577
|
+
const { controller, detach } = this.createTailAbortController(
|
|
1578
|
+
tailOptions.signal
|
|
1579
|
+
);
|
|
1580
|
+
const wake = () => {
|
|
1581
|
+
notify?.();
|
|
1582
|
+
};
|
|
1583
|
+
const streamTask = new TailStream({
|
|
1488
1584
|
sessionId: this.id,
|
|
1489
1585
|
token: this.token,
|
|
1490
1586
|
websocketBaseUrl: this.transport.websocketBaseUrl,
|
|
1491
1587
|
websocketFactory: this.transport.websocketFactory,
|
|
1492
1588
|
options: {
|
|
1493
1589
|
...tailOptions,
|
|
1494
|
-
cursor
|
|
1590
|
+
cursor: startCursor,
|
|
1591
|
+
signal: controller.signal
|
|
1592
|
+
}
|
|
1593
|
+
}).subscribe((batch) => {
|
|
1594
|
+
const queuedEvents = shouldApplyToLog ? this.log.applyBatch(batch) : batch;
|
|
1595
|
+
if (shouldApplyToLog && queuedEvents.length > 0) {
|
|
1596
|
+
this.persistLogState();
|
|
1495
1597
|
}
|
|
1598
|
+
for (const event of queuedEvents) {
|
|
1599
|
+
queue.push({
|
|
1600
|
+
event: parseEvent(event),
|
|
1601
|
+
context: this.resolveEventContext(event.seq, replayCutoffSeq)
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
wake();
|
|
1605
|
+
}).catch((error) => {
|
|
1606
|
+
failure = error;
|
|
1607
|
+
}).finally(() => {
|
|
1608
|
+
done = true;
|
|
1609
|
+
wake();
|
|
1496
1610
|
});
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
`consume() failed to save cursor for session '${this.id}': ${error instanceof Error ? error.message : String(error)}`
|
|
1505
|
-
);
|
|
1611
|
+
return {
|
|
1612
|
+
next: async () => {
|
|
1613
|
+
while (queue.length === 0 && !done && !failure) {
|
|
1614
|
+
await new Promise((resolve) => {
|
|
1615
|
+
notify = resolve;
|
|
1616
|
+
});
|
|
1617
|
+
notify = void 0;
|
|
1506
1618
|
}
|
|
1619
|
+
const next = queue.shift();
|
|
1620
|
+
return next;
|
|
1621
|
+
},
|
|
1622
|
+
getFailure: () => failure,
|
|
1623
|
+
dispose: async () => {
|
|
1624
|
+
controller.abort();
|
|
1625
|
+
detach();
|
|
1626
|
+
await streamTask;
|
|
1507
1627
|
}
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
async *iterateLiveTail({
|
|
1631
|
+
parseEvent,
|
|
1632
|
+
replayCutoffSeq,
|
|
1633
|
+
startCursor,
|
|
1634
|
+
tailOptions
|
|
1635
|
+
}) {
|
|
1636
|
+
const runtime = this.createTailRuntime({
|
|
1637
|
+
parseEvent,
|
|
1638
|
+
replayCutoffSeq,
|
|
1639
|
+
startCursor,
|
|
1640
|
+
tailOptions
|
|
1508
1641
|
});
|
|
1642
|
+
try {
|
|
1643
|
+
while (true) {
|
|
1644
|
+
const next = await runtime.next();
|
|
1645
|
+
if (next) {
|
|
1646
|
+
yield next;
|
|
1647
|
+
continue;
|
|
1648
|
+
}
|
|
1649
|
+
const failure = runtime.getFailure();
|
|
1650
|
+
if (failure) {
|
|
1651
|
+
throw failure;
|
|
1652
|
+
}
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
} finally {
|
|
1656
|
+
await runtime.dispose();
|
|
1657
|
+
}
|
|
1509
1658
|
}
|
|
1510
1659
|
emitStreamError(error) {
|
|
1511
1660
|
const streamError = error instanceof Error ? error : new StarciteError(`Session stream failed: ${String(error)}`);
|
|
@@ -1530,67 +1679,94 @@ var StarciteSession = class {
|
|
|
1530
1679
|
}).finally(() => {
|
|
1531
1680
|
this.liveSyncTask = void 0;
|
|
1532
1681
|
this.liveSyncController = void 0;
|
|
1682
|
+
if (this.eventSubscriptions.size > 0) {
|
|
1683
|
+
this.ensureLiveSync();
|
|
1684
|
+
}
|
|
1533
1685
|
});
|
|
1534
1686
|
}
|
|
1535
1687
|
async runLiveSync(signal) {
|
|
1688
|
+
let shouldRunCatchUpPass = this.log.lastSeq === 0;
|
|
1689
|
+
let retryDelayMs = 250;
|
|
1536
1690
|
while (!signal.aborted && this.eventSubscriptions.size > 0) {
|
|
1537
|
-
|
|
1538
|
-
sessionId: this.id,
|
|
1539
|
-
token: this.token,
|
|
1540
|
-
websocketBaseUrl: this.transport.websocketBaseUrl,
|
|
1541
|
-
websocketFactory: this.transport.websocketFactory,
|
|
1542
|
-
options: {
|
|
1543
|
-
cursor: this.log.lastSeq,
|
|
1544
|
-
signal
|
|
1545
|
-
}
|
|
1546
|
-
});
|
|
1691
|
+
this.liveSyncCatchUpActive = shouldRunCatchUpPass;
|
|
1547
1692
|
try {
|
|
1548
|
-
await
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
this.persistLogState();
|
|
1552
|
-
}
|
|
1553
|
-
});
|
|
1693
|
+
await this.subscribeLiveSyncPass(signal, !shouldRunCatchUpPass);
|
|
1694
|
+
shouldRunCatchUpPass = false;
|
|
1695
|
+
retryDelayMs = 250;
|
|
1554
1696
|
} catch (error) {
|
|
1555
1697
|
if (signal.aborted) {
|
|
1556
1698
|
return;
|
|
1557
1699
|
}
|
|
1558
1700
|
if (error instanceof SessionLogGapError) {
|
|
1701
|
+
shouldRunCatchUpPass = true;
|
|
1559
1702
|
continue;
|
|
1560
1703
|
}
|
|
1561
|
-
|
|
1704
|
+
this.emitStreamError(error);
|
|
1705
|
+
shouldRunCatchUpPass = true;
|
|
1706
|
+
await this.waitForLiveSyncRetry(retryDelayMs, signal);
|
|
1707
|
+
retryDelayMs = Math.min(retryDelayMs * 2, 5e3);
|
|
1708
|
+
} finally {
|
|
1709
|
+
this.liveSyncCatchUpActive = false;
|
|
1562
1710
|
}
|
|
1563
1711
|
}
|
|
1564
1712
|
}
|
|
1713
|
+
async subscribeLiveSyncPass(signal, follow) {
|
|
1714
|
+
const stream = new TailStream({
|
|
1715
|
+
sessionId: this.id,
|
|
1716
|
+
token: this.token,
|
|
1717
|
+
websocketBaseUrl: this.transport.websocketBaseUrl,
|
|
1718
|
+
websocketFactory: this.transport.websocketFactory,
|
|
1719
|
+
options: {
|
|
1720
|
+
cursor: this.log.lastSeq,
|
|
1721
|
+
follow,
|
|
1722
|
+
signal
|
|
1723
|
+
}
|
|
1724
|
+
});
|
|
1725
|
+
await stream.subscribe((batch) => {
|
|
1726
|
+
const appliedEvents = this.log.applyBatch(batch);
|
|
1727
|
+
if (appliedEvents.length > 0) {
|
|
1728
|
+
this.persistLogState();
|
|
1729
|
+
}
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1565
1732
|
persistLogState() {
|
|
1733
|
+
if (!this.store) {
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1566
1736
|
this.store.save(this.id, {
|
|
1567
1737
|
cursor: this.log.cursor,
|
|
1568
|
-
events: [...this.log.events]
|
|
1738
|
+
events: [...this.log.events],
|
|
1739
|
+
metadata: {
|
|
1740
|
+
schemaVersion: 1,
|
|
1741
|
+
updatedAtMs: Date.now()
|
|
1742
|
+
}
|
|
1569
1743
|
});
|
|
1570
1744
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1745
|
+
waitForLiveSyncRetry(delayMs, signal) {
|
|
1746
|
+
if (delayMs <= 0 || signal.aborted) {
|
|
1747
|
+
return Promise.resolve();
|
|
1748
|
+
}
|
|
1749
|
+
return new Promise((resolve) => {
|
|
1750
|
+
let settled = false;
|
|
1751
|
+
const timer = setTimeout(() => {
|
|
1752
|
+
if (settled) {
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
settled = true;
|
|
1756
|
+
signal.removeEventListener("abort", onAbort);
|
|
1757
|
+
resolve();
|
|
1758
|
+
}, delayMs);
|
|
1759
|
+
const onAbort = () => {
|
|
1760
|
+
if (settled) {
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
settled = true;
|
|
1764
|
+
clearTimeout(timer);
|
|
1765
|
+
signal.removeEventListener("abort", onAbort);
|
|
1766
|
+
resolve();
|
|
1767
|
+
};
|
|
1768
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1769
|
+
});
|
|
1594
1770
|
}
|
|
1595
1771
|
};
|
|
1596
1772
|
|
|
@@ -1630,7 +1806,7 @@ var Starcite = class {
|
|
|
1630
1806
|
}
|
|
1631
1807
|
this.authBaseUrl = resolveAuthBaseUrl(options.authUrl, apiKey);
|
|
1632
1808
|
const websocketFactory = options.websocketFactory ?? defaultWebSocketFactory;
|
|
1633
|
-
this.store = options.store
|
|
1809
|
+
this.store = options.store;
|
|
1634
1810
|
this.transport = {
|
|
1635
1811
|
baseUrl,
|
|
1636
1812
|
websocketBaseUrl: toWebSocketBaseUrl(baseUrl),
|
|
@@ -1801,45 +1977,83 @@ var Starcite = class {
|
|
|
1801
1977
|
}
|
|
1802
1978
|
};
|
|
1803
1979
|
|
|
1804
|
-
// src/
|
|
1980
|
+
// src/session-store.ts
|
|
1981
|
+
var import_zod5 = require("zod");
|
|
1805
1982
|
var DEFAULT_KEY_PREFIX = "starcite";
|
|
1806
|
-
var
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1983
|
+
var SessionStoreMetadataSchema = import_zod5.z.object({
|
|
1984
|
+
schemaVersion: import_zod5.z.literal(1),
|
|
1985
|
+
updatedAtMs: import_zod5.z.number().int().nonnegative()
|
|
1986
|
+
});
|
|
1987
|
+
var SessionStoreStateSchema = import_zod5.z.object({
|
|
1988
|
+
cursor: import_zod5.z.number().int().nonnegative(),
|
|
1989
|
+
events: import_zod5.z.array(TailEventSchema),
|
|
1990
|
+
metadata: SessionStoreMetadataSchema.optional()
|
|
1991
|
+
});
|
|
1992
|
+
function cloneEvents(events) {
|
|
1993
|
+
return events.map((event) => structuredClone(event));
|
|
1994
|
+
}
|
|
1995
|
+
function cloneState(state) {
|
|
1996
|
+
return {
|
|
1997
|
+
cursor: state.cursor,
|
|
1998
|
+
events: cloneEvents(state.events),
|
|
1999
|
+
metadata: state.metadata ? { ...state.metadata } : void 0
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
var MemoryStore = class {
|
|
2003
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1811
2004
|
load(sessionId) {
|
|
1812
|
-
|
|
2005
|
+
const stored = this.sessions.get(sessionId);
|
|
2006
|
+
return stored ? cloneState(stored) : void 0;
|
|
1813
2007
|
}
|
|
1814
|
-
save(sessionId,
|
|
1815
|
-
this.
|
|
2008
|
+
save(sessionId, state) {
|
|
2009
|
+
this.sessions.set(sessionId, cloneState(state));
|
|
2010
|
+
}
|
|
2011
|
+
clear(sessionId) {
|
|
2012
|
+
this.sessions.delete(sessionId);
|
|
1816
2013
|
}
|
|
1817
2014
|
};
|
|
1818
|
-
var
|
|
2015
|
+
var WebStorageSessionStore = class {
|
|
1819
2016
|
storage;
|
|
1820
2017
|
keyForSession;
|
|
2018
|
+
stateSchema;
|
|
1821
2019
|
constructor(storage, options = {}) {
|
|
1822
2020
|
this.storage = storage;
|
|
1823
2021
|
const prefix = options.keyPrefix?.trim() || DEFAULT_KEY_PREFIX;
|
|
1824
|
-
this.keyForSession = options.keyForSession ?? ((sessionId) => `${prefix}:${sessionId}:
|
|
2022
|
+
this.keyForSession = options.keyForSession ?? ((sessionId) => `${prefix}:${sessionId}:sessionStore`);
|
|
2023
|
+
this.stateSchema = options.stateSchema ?? SessionStoreStateSchema;
|
|
1825
2024
|
}
|
|
1826
2025
|
load(sessionId) {
|
|
1827
2026
|
const raw = this.storage.getItem(this.keyForSession(sessionId));
|
|
1828
2027
|
if (raw === null) {
|
|
1829
2028
|
return void 0;
|
|
1830
2029
|
}
|
|
1831
|
-
|
|
1832
|
-
|
|
2030
|
+
let decoded;
|
|
2031
|
+
try {
|
|
2032
|
+
decoded = JSON.parse(raw);
|
|
2033
|
+
} catch {
|
|
2034
|
+
return void 0;
|
|
2035
|
+
}
|
|
2036
|
+
const parsed = this.stateSchema.safeParse(decoded);
|
|
2037
|
+
if (!parsed.success) {
|
|
2038
|
+
return void 0;
|
|
2039
|
+
}
|
|
2040
|
+
return cloneState(parsed.data);
|
|
1833
2041
|
}
|
|
1834
|
-
save(sessionId,
|
|
1835
|
-
this.storage.setItem(
|
|
2042
|
+
save(sessionId, state) {
|
|
2043
|
+
this.storage.setItem(
|
|
2044
|
+
this.keyForSession(sessionId),
|
|
2045
|
+
JSON.stringify(cloneState(state))
|
|
2046
|
+
);
|
|
2047
|
+
}
|
|
2048
|
+
clear(sessionId) {
|
|
2049
|
+
this.storage.removeItem?.(this.keyForSession(sessionId));
|
|
1836
2050
|
}
|
|
1837
2051
|
};
|
|
1838
|
-
var
|
|
2052
|
+
var LocalStorageSessionStore = class extends WebStorageSessionStore {
|
|
1839
2053
|
constructor(options = {}) {
|
|
1840
2054
|
if (typeof localStorage === "undefined") {
|
|
1841
2055
|
throw new StarciteError(
|
|
1842
|
-
"localStorage is not available in this runtime. Use
|
|
2056
|
+
"localStorage is not available in this runtime. Use WebStorageSessionStore with a custom storage adapter."
|
|
1843
2057
|
);
|
|
1844
2058
|
}
|
|
1845
2059
|
super(localStorage, options);
|
|
@@ -1847,8 +2061,7 @@ var LocalStorageCursorStore = class extends WebStorageCursorStore {
|
|
|
1847
2061
|
};
|
|
1848
2062
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1849
2063
|
0 && (module.exports = {
|
|
1850
|
-
|
|
1851
|
-
LocalStorageCursorStore,
|
|
2064
|
+
LocalStorageSessionStore,
|
|
1852
2065
|
MemoryStore,
|
|
1853
2066
|
SessionLogConflictError,
|
|
1854
2067
|
SessionLogGapError,
|
|
@@ -1862,6 +2075,6 @@ var LocalStorageCursorStore = class extends WebStorageCursorStore {
|
|
|
1862
2075
|
StarciteSession,
|
|
1863
2076
|
StarciteTailError,
|
|
1864
2077
|
StarciteTokenExpiredError,
|
|
1865
|
-
|
|
2078
|
+
WebStorageSessionStore
|
|
1866
2079
|
});
|
|
1867
2080
|
//# sourceMappingURL=index.cjs.map
|