@loro-dev/streams-crdt 0.1.2 → 0.2.1

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 CHANGED
@@ -184,8 +184,31 @@ the transport also clears the stored replay cursor for that stream.
184
184
  - a sync callback returning a string
185
185
  - an async callback returning a string
186
186
 
187
- This is intended for refresh-token flows. `StreamsCrdt` asks for a token before
188
- requests; token refresh itself remains caller-owned.
187
+ `StreamsCrdt` asks for a token before each request. If a request comes back
188
+ with `401` or `403`, it calls `auth` one more time with
189
+ `{ reason: "unauthorized", status, previousToken }` and retries that request
190
+ once.
191
+
192
+ This lets the caller keep a cached token in the normal path, and only refresh
193
+ after the server rejects it:
194
+
195
+ ```ts
196
+ let token = readCachedGatewayJwt();
197
+
198
+ const transport = new StreamsCrdt({
199
+ streamUrl,
200
+ adapter,
201
+ auth: async (context) => {
202
+ if (context?.reason === "unauthorized") {
203
+ token = await refreshGatewayJwt();
204
+ }
205
+ return token;
206
+ },
207
+ });
208
+ ```
209
+
210
+ If your callback already returns a fresh token every time, you can ignore the
211
+ `context` argument.
189
212
 
190
213
  ## Remote Cursor Store
191
214
 
@@ -491,7 +514,7 @@ const transport = new StreamsCrdt({
491
514
  - `createStreamIfMissing` defaults to `false` and only affects the initial sync/join bootstrap
492
515
  - `deleteStream()` returns `{ deleted: false }` when the target stream is already missing
493
516
  - `sync()` and `join()` return `Result` objects instead of implicitly creating missing streams
494
- - `join()` resolves only after the initial replay has succeeded; without a stored remote cursor it prefers bootstrap-live SSE and falls back sticky to long-poll
517
+ - `join()` resolves only after the initial replay has succeeded; without a stored remote cursor it bootstraps once, then prefers ordinary live SSE and falls back sticky to long-poll
495
518
  - One transport instance maps to one DS stream
496
519
  - One adapter instance maps to one local CRDT instance
497
520
  - Built-in remote cursor stores implement `delete(streamUrl)` so `deleteStream()` can reset replay state cleanly
package/dist/flock.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as createInitialRemoteCursor, C as BeforeRemoteCursorSaveHook, D as RemoteCursor, E as IndexedDbRemoteCursorStoreOptions, O as RemoteCursorSaveSource, S as BeforeRemoteCursorSaveContext, T as IndexedDbRemoteCursorStore, _ as TransportError, a as CrdtAdapter, b as TransportSubscription, c as JsonValue, d as SnapshotTransformHook, f as SnapshotUploadOptions, g as TransportDeleteStreamSuccess, h as TransportCreateStreamSuccess, i as StreamsCrdt, k as RemoteCursorStore, l as Result, m as StreamsCrdtOptions, n as isValidBucketId, o as CrdtUpdateBatch, p as StreamsAuthProvider, r as isValidRillId, s as JsonObject, t as createStreamUrl, u as SnapshotCodec, v as TransportJoinParams, w as InMemoryRemoteCursorStore, x as TransportSyncSuccess, y as TransportRoomStatus } from "./stream-id-BfuQIfcB.js";
1
+ import { A as RemoteCursorStore, C as BeforeRemoteCursorSaveContext, D as IndexedDbRemoteCursorStoreOptions, E as IndexedDbRemoteCursorStore, O as RemoteCursor, S as TransportSyncSuccess, T as InMemoryRemoteCursorStore, _ as TransportDeleteStreamSuccess, a as CrdtAdapter, b as TransportRoomStatus, c as JsonValue, d as SnapshotTransformHook, f as SnapshotUploadOptions, g as TransportCreateStreamSuccess, h as StreamsCrdtOptions, i as StreamsCrdt, j as createInitialRemoteCursor, k as RemoteCursorSaveSource, l as Result, m as StreamsAuthProvider, n as isValidBucketId, o as CrdtUpdateBatch, p as StreamsAuthContext, r as isValidRillId, s as JsonObject, t as createStreamUrl, u as SnapshotCodec, v as TransportError, w as BeforeRemoteCursorSaveHook, x as TransportSubscription, y as TransportJoinParams } from "./stream-id-BxGVitUa.js";
2
2
  import { ExportBundle, Flock, VersionVector, VersionVector as VersionVector$1 } from "@loro-dev/flock-wasm";
3
3
 
4
4
  //#region src/flock.d.ts
@@ -7,5 +7,5 @@ import { ExportBundle, Flock, VersionVector, VersionVector as VersionVector$1 }
7
7
  */
8
8
  declare function createFlockAdapter(flock: Flock): CrdtAdapter<VersionVector$1>;
9
9
  //#endregion
10
- export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, type ExportBundle, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, type VersionVector, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
10
+ export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, type ExportBundle, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, type VersionVector, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
11
11
  //# sourceMappingURL=flock.d.ts.map
package/dist/flock.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-YOFZ4I6Y.js";
1
+ import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-kM9JM1F7.js";
2
2
  //#region src/flock.ts
3
3
  function isAfter(entry, current) {
4
4
  return entry.physicalTime > current.physicalTime || entry.physicalTime === current.physicalTime && entry.logicalCounter > current.logicalCounter;
@@ -87,10 +87,11 @@ function createFlockAdapter(flock) {
87
87
  peerId: flock.peerId()
88
88
  });
89
89
  if (!hasEntries(batchPayload)) return;
90
- recordExportFrontier(flock.version());
90
+ const exportedVersion = extractVersionFromBundle(batchPayload);
91
+ recordExportFrontier(exportedVersion);
91
92
  listener({
92
93
  updates: [new TextEncoder().encode(JSON.stringify(batchPayload))],
93
- version: extractVersionFromBundle(batchPayload)
94
+ version: exportedVersion
94
95
  });
95
96
  };
96
97
  localExportQueue = localExportQueue.then(drain).catch(() => {});
package/dist/flock.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"flock.js","names":[],"sources":["../src/flock.ts"],"sourcesContent":["import { Flock } from \"@loro-dev/flock-wasm\";\nimport type {\n EventBatch,\n ExportBundle,\n ExportRecord,\n VersionVector,\n VersionVectorEntry,\n} from \"@loro-dev/flock-wasm\";\nimport type { CrdtAdapter } from \"./types\";\n\nexport { StreamsCrdt } from \"./transport\";\nexport * from \"./shared\";\nexport type { ExportBundle, VersionVector } from \"@loro-dev/flock-wasm\";\n\nfunction isAfter(\n entry: VersionVectorEntry,\n current: VersionVectorEntry,\n): boolean {\n return (\n entry.physicalTime > current.physicalTime ||\n (entry.physicalTime === current.physicalTime &&\n entry.logicalCounter > current.logicalCounter)\n );\n}\n\nfunction mergeVersions(\n base: VersionVector,\n incoming: VersionVector,\n): VersionVector {\n const merged: VersionVector = { ...base };\n for (const [peer, entry] of Object.entries(incoming)) {\n if (entry == null) {\n continue;\n }\n const current = merged[peer];\n if (current == null || isAfter(entry, current)) {\n merged[peer] = { ...entry };\n }\n }\n return merged;\n}\n\nfunction parseClock(\n record: ExportRecord,\n): { peerId: string; entry: VersionVectorEntry } | null {\n const firstComma = record.c.indexOf(\",\");\n const secondComma = record.c.indexOf(\",\", firstComma + 1);\n if (firstComma < 0 || secondComma < 0) {\n return null;\n }\n\n const physicalTime = Number(record.c.slice(0, firstComma));\n const logicalCounter = Number(record.c.slice(firstComma + 1, secondComma));\n if (!Number.isFinite(physicalTime) || !Number.isFinite(logicalCounter)) {\n return null;\n }\n\n return {\n peerId: record.c.slice(secondComma + 1),\n entry: { physicalTime, logicalCounter },\n };\n}\n\nfunction extractVersionFromBundle(bundle: ExportBundle): VersionVector {\n const out: VersionVector = {};\n for (const record of Object.values(bundle.entries)) {\n const parsed = parseClock(record);\n if (parsed == null) {\n continue;\n }\n const current = out[parsed.peerId];\n if (current == null || isAfter(parsed.entry, current)) {\n out[parsed.peerId] = parsed.entry;\n }\n }\n return out;\n}\n\nfunction hasEntries(bundle: ExportBundle): boolean {\n return Object.keys(bundle.entries).length > 0;\n}\n\n/**\n * Creates a `CrdtAdapter` for one Flock replica.\n */\nexport function createFlockAdapter(flock: Flock): CrdtAdapter<VersionVector> {\n let lastExportedVersion: VersionVector = {};\n let localExportQueue = Promise.resolve();\n\n const recordExportFrontier = (version: VersionVector): void => {\n lastExportedVersion = mergeVersions(lastExportedVersion, version);\n };\n\n return {\n emptyVersion: () => ({}),\n mergeVersions,\n exportSnapshot: () => flock.exportFile(),\n applySnapshot: (snapshot) => {\n flock.importFile(snapshot);\n return flock.version();\n },\n exportUpdates: (fromVersion) => {\n const bundle = flock.exportJson(fromVersion);\n recordExportFrontier(fromVersion);\n if (!hasEntries(bundle)) {\n return undefined;\n }\n const exportedVersion = extractVersionFromBundle(bundle);\n recordExportFrontier(exportedVersion);\n return {\n updates: [new TextEncoder().encode(JSON.stringify(bundle))],\n version: exportedVersion,\n };\n },\n applyRemoteUpdates: (updates, remoterVersion) => {\n let merged = remoterVersion;\n for (const update of updates) {\n const bundle = JSON.parse(\n new TextDecoder().decode(update),\n ) as ExportBundle;\n if (!hasEntries(bundle)) {\n continue;\n }\n flock.importJson(bundle);\n merged = mergeVersions(merged, extractVersionFromBundle(bundle));\n }\n return merged;\n },\n subscribeLocalUpdates: (listener) => {\n const unsubscribe = flock.subscribe((batch) => {\n if (batch.source !== \"local\") {\n return;\n }\n const drain = async () => {\n const batchPayload = await flock.exportJson({\n from: lastExportedVersion,\n peerId: flock.peerId(),\n });\n if (!hasEntries(batchPayload)) {\n return;\n }\n recordExportFrontier(flock.version());\n listener({\n updates: [new TextEncoder().encode(JSON.stringify(batchPayload))],\n version: extractVersionFromBundle(batchPayload),\n });\n };\n\n localExportQueue = localExportQueue.then(drain).catch(() => {\n /* swallow to keep queue alive */\n });\n });\n return unsubscribe;\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"flock.js","names":[],"sources":["../src/flock.ts"],"sourcesContent":["import { Flock } from \"@loro-dev/flock-wasm\";\nimport type {\n EventBatch,\n ExportBundle,\n ExportRecord,\n VersionVector,\n VersionVectorEntry,\n} from \"@loro-dev/flock-wasm\";\nimport type { CrdtAdapter } from \"./types\";\n\nexport { StreamsCrdt } from \"./transport\";\nexport * from \"./shared\";\nexport type { ExportBundle, VersionVector } from \"@loro-dev/flock-wasm\";\n\nfunction isAfter(\n entry: VersionVectorEntry,\n current: VersionVectorEntry,\n): boolean {\n return (\n entry.physicalTime > current.physicalTime ||\n (entry.physicalTime === current.physicalTime &&\n entry.logicalCounter > current.logicalCounter)\n );\n}\n\nfunction mergeVersions(\n base: VersionVector,\n incoming: VersionVector,\n): VersionVector {\n const merged: VersionVector = { ...base };\n for (const [peer, entry] of Object.entries(incoming)) {\n if (entry == null) {\n continue;\n }\n const current = merged[peer];\n if (current == null || isAfter(entry, current)) {\n merged[peer] = { ...entry };\n }\n }\n return merged;\n}\n\nfunction parseClock(\n record: ExportRecord,\n): { peerId: string; entry: VersionVectorEntry } | null {\n const firstComma = record.c.indexOf(\",\");\n const secondComma = record.c.indexOf(\",\", firstComma + 1);\n if (firstComma < 0 || secondComma < 0) {\n return null;\n }\n\n const physicalTime = Number(record.c.slice(0, firstComma));\n const logicalCounter = Number(record.c.slice(firstComma + 1, secondComma));\n if (!Number.isFinite(physicalTime) || !Number.isFinite(logicalCounter)) {\n return null;\n }\n\n return {\n peerId: record.c.slice(secondComma + 1),\n entry: { physicalTime, logicalCounter },\n };\n}\n\nfunction extractVersionFromBundle(bundle: ExportBundle): VersionVector {\n const out: VersionVector = {};\n for (const record of Object.values(bundle.entries)) {\n const parsed = parseClock(record);\n if (parsed == null) {\n continue;\n }\n const current = out[parsed.peerId];\n if (current == null || isAfter(parsed.entry, current)) {\n out[parsed.peerId] = parsed.entry;\n }\n }\n return out;\n}\n\nfunction hasEntries(bundle: ExportBundle): boolean {\n return Object.keys(bundle.entries).length > 0;\n}\n\n/**\n * Creates a `CrdtAdapter` for one Flock replica.\n */\nexport function createFlockAdapter(flock: Flock): CrdtAdapter<VersionVector> {\n let lastExportedVersion: VersionVector = {};\n let localExportQueue = Promise.resolve();\n\n const recordExportFrontier = (version: VersionVector): void => {\n lastExportedVersion = mergeVersions(lastExportedVersion, version);\n };\n\n return {\n emptyVersion: () => ({}),\n mergeVersions,\n exportSnapshot: () => flock.exportFile(),\n applySnapshot: (snapshot) => {\n flock.importFile(snapshot);\n return flock.version();\n },\n exportUpdates: (fromVersion) => {\n const bundle = flock.exportJson(fromVersion);\n recordExportFrontier(fromVersion);\n if (!hasEntries(bundle)) {\n return undefined;\n }\n const exportedVersion = extractVersionFromBundle(bundle);\n recordExportFrontier(exportedVersion);\n return {\n updates: [new TextEncoder().encode(JSON.stringify(bundle))],\n version: exportedVersion,\n };\n },\n applyRemoteUpdates: (updates, remoterVersion) => {\n let merged = remoterVersion;\n for (const update of updates) {\n const bundle = JSON.parse(\n new TextDecoder().decode(update),\n ) as ExportBundle;\n if (!hasEntries(bundle)) {\n continue;\n }\n flock.importJson(bundle);\n merged = mergeVersions(merged, extractVersionFromBundle(bundle));\n }\n return merged;\n },\n subscribeLocalUpdates: (listener) => {\n const unsubscribe = flock.subscribe((batch) => {\n if (batch.source !== \"local\") {\n return;\n }\n const drain = async () => {\n const batchPayload = await flock.exportJson({\n from: lastExportedVersion,\n peerId: flock.peerId(),\n });\n if (!hasEntries(batchPayload)) {\n return;\n }\n const exportedVersion = extractVersionFromBundle(batchPayload);\n recordExportFrontier(exportedVersion);\n listener({\n updates: [new TextEncoder().encode(JSON.stringify(batchPayload))],\n version: exportedVersion,\n });\n };\n\n localExportQueue = localExportQueue.then(drain).catch(() => {\n /* swallow to keep queue alive */\n });\n });\n return unsubscribe;\n },\n };\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as createInitialRemoteCursor, C as BeforeRemoteCursorSaveHook, D as RemoteCursor, E as IndexedDbRemoteCursorStoreOptions, O as RemoteCursorSaveSource, S as BeforeRemoteCursorSaveContext, T as IndexedDbRemoteCursorStore, _ as TransportError, a as CrdtAdapter, b as TransportSubscription, c as JsonValue, d as SnapshotTransformHook, f as SnapshotUploadOptions, g as TransportDeleteStreamSuccess, h as TransportCreateStreamSuccess, i as StreamsCrdt, k as RemoteCursorStore, l as Result, m as StreamsCrdtOptions, n as isValidBucketId, o as CrdtUpdateBatch, p as StreamsAuthProvider, r as isValidRillId, s as JsonObject, t as createStreamUrl, u as SnapshotCodec, v as TransportJoinParams, w as InMemoryRemoteCursorStore, x as TransportSyncSuccess, y as TransportRoomStatus } from "./stream-id-BfuQIfcB.js";
2
- export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
1
+ import { A as RemoteCursorStore, C as BeforeRemoteCursorSaveContext, D as IndexedDbRemoteCursorStoreOptions, E as IndexedDbRemoteCursorStore, O as RemoteCursor, S as TransportSyncSuccess, T as InMemoryRemoteCursorStore, _ as TransportDeleteStreamSuccess, a as CrdtAdapter, b as TransportRoomStatus, c as JsonValue, d as SnapshotTransformHook, f as SnapshotUploadOptions, g as TransportCreateStreamSuccess, h as StreamsCrdtOptions, i as StreamsCrdt, j as createInitialRemoteCursor, k as RemoteCursorSaveSource, l as Result, m as StreamsAuthProvider, n as isValidBucketId, o as CrdtUpdateBatch, p as StreamsAuthContext, r as isValidRillId, s as JsonObject, t as createStreamUrl, u as SnapshotCodec, v as TransportError, w as BeforeRemoteCursorSaveHook, x as TransportSubscription, y as TransportJoinParams } from "./stream-id-BxGVitUa.js";
2
+ export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-YOFZ4I6Y.js";
1
+ import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-kM9JM1F7.js";
2
2
  export { InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
package/dist/loro.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as createInitialRemoteCursor, C as BeforeRemoteCursorSaveHook, D as RemoteCursor, E as IndexedDbRemoteCursorStoreOptions, O as RemoteCursorSaveSource, S as BeforeRemoteCursorSaveContext, T as IndexedDbRemoteCursorStore, _ as TransportError, a as CrdtAdapter, b as TransportSubscription, c as JsonValue, d as SnapshotTransformHook, f as SnapshotUploadOptions, g as TransportDeleteStreamSuccess, h as TransportCreateStreamSuccess, i as StreamsCrdt, k as RemoteCursorStore, l as Result, m as StreamsCrdtOptions, n as isValidBucketId, o as CrdtUpdateBatch, p as StreamsAuthProvider, r as isValidRillId, s as JsonObject, t as createStreamUrl, u as SnapshotCodec, v as TransportJoinParams, w as InMemoryRemoteCursorStore, x as TransportSyncSuccess, y as TransportRoomStatus } from "./stream-id-BfuQIfcB.js";
1
+ import { A as RemoteCursorStore, C as BeforeRemoteCursorSaveContext, D as IndexedDbRemoteCursorStoreOptions, E as IndexedDbRemoteCursorStore, O as RemoteCursor, S as TransportSyncSuccess, T as InMemoryRemoteCursorStore, _ as TransportDeleteStreamSuccess, a as CrdtAdapter, b as TransportRoomStatus, c as JsonValue, d as SnapshotTransformHook, f as SnapshotUploadOptions, g as TransportCreateStreamSuccess, h as StreamsCrdtOptions, i as StreamsCrdt, j as createInitialRemoteCursor, k as RemoteCursorSaveSource, l as Result, m as StreamsAuthProvider, n as isValidBucketId, o as CrdtUpdateBatch, p as StreamsAuthContext, r as isValidRillId, s as JsonObject, t as createStreamUrl, u as SnapshotCodec, v as TransportError, w as BeforeRemoteCursorSaveHook, x as TransportSubscription, y as TransportJoinParams } from "./stream-id-BxGVitUa.js";
2
2
  import { LoroDoc } from "loro-crdt";
3
3
 
4
4
  //#region src/loro.d.ts
@@ -18,5 +18,5 @@ import { LoroDoc } from "loro-crdt";
18
18
  */
19
19
  declare function createLoroDocAdapter(doc: LoroDoc): CrdtAdapter<JsonObject>;
20
20
  //#endregion
21
- export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, createInitialRemoteCursor, createLoroDocAdapter, createStreamUrl, isValidBucketId, isValidRillId };
21
+ export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, createInitialRemoteCursor, createLoroDocAdapter, createStreamUrl, isValidBucketId, isValidRillId };
22
22
  //# sourceMappingURL=loro.d.ts.map
package/dist/loro.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-YOFZ4I6Y.js";
1
+ import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-kM9JM1F7.js";
2
2
  import { VersionVector, decodeImportBlobMeta } from "loro-crdt";
3
3
  //#region src/loro.ts
4
4
  function toDocVersionRecord(version) {
@@ -130,7 +130,27 @@ type JsonObject = {
130
130
  /**
131
131
  * Authorization source for gateway requests.
132
132
  */
133
- type StreamsAuthProvider = string | (() => MaybePromise<string | null | undefined>);
133
+ type StreamsAuthContext = {
134
+ /** Default path before sending a request. */readonly reason: "request";
135
+ } | {
136
+ /**
137
+ * The previous request was rejected by the server with 401/403.
138
+ *
139
+ * Return a freshly refreshed token here when the caller keeps its own
140
+ * token cache.
141
+ */
142
+ readonly reason: "unauthorized";
143
+ readonly status: 401 | 403;
144
+ readonly previousToken?: string;
145
+ };
146
+ /**
147
+ * Authorization source for gateway requests.
148
+ *
149
+ * The callback is invoked before each request. When a request comes back with
150
+ * 401/403, transport invokes it one more time with `reason: "unauthorized"`
151
+ * and retries that request once.
152
+ */
153
+ type StreamsAuthProvider = string | ((context?: StreamsAuthContext) => MaybePromise<string | null | undefined>);
134
154
  /**
135
155
  * Rust-style result for transport operations that may fail for expected reasons.
136
156
  */
@@ -333,6 +353,13 @@ interface StreamsCrdtOptions<TVersion extends JsonObject> {
333
353
  * Defaults to false so missing streams are surfaced explicitly.
334
354
  */
335
355
  readonly createStreamIfMissing?: boolean;
356
+ /**
357
+ * Auth token source.
358
+ *
359
+ * String and zero-arg callback forms keep the old behavior. Callbacks may
360
+ * also inspect `context.reason` to refresh a cached token only after the
361
+ * server rejects the current one.
362
+ */
336
363
  readonly auth?: StreamsAuthProvider;
337
364
  readonly fetch?: (input: string, init?: RequestInit) => Promise<Response>;
338
365
  /**
@@ -436,10 +463,6 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
436
463
  private syncOnce;
437
464
  private performInitialJoinSync;
438
465
  private bootstrapIntoState;
439
- private bootstrapIntoJoinedState;
440
- private bootstrapIntoJoinedStateViaSse;
441
- private continueJoinedSseSessionUntilCaughtUp;
442
- private bootstrapIntoJoinedStateViaLongPoll;
443
466
  private catchup;
444
467
  private appendLocalBatch;
445
468
  private exportUpdates;
@@ -461,8 +484,6 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
461
484
  private createStreamRequest;
462
485
  private deleteStreamRequest;
463
486
  private readFromOffsetSseItems;
464
- private openBootstrapSseSession;
465
- private readBootstrapLongPoll;
466
487
  private readFromOffset;
467
488
  private bootstrap;
468
489
  private readSnapshotOffsetValue;
@@ -491,5 +512,5 @@ declare function createStreamUrl(input: {
491
512
  baseUrl?: string;
492
513
  }): string;
493
514
  //#endregion
494
- export { createInitialRemoteCursor as A, BeforeRemoteCursorSaveHook as C, RemoteCursor as D, IndexedDbRemoteCursorStoreOptions as E, RemoteCursorSaveSource as O, BeforeRemoteCursorSaveContext as S, IndexedDbRemoteCursorStore as T, TransportError as _, CrdtAdapter as a, TransportSubscription as b, JsonValue as c, SnapshotTransformHook as d, SnapshotUploadOptions as f, TransportDeleteStreamSuccess as g, TransportCreateStreamSuccess as h, StreamsCrdt as i, RemoteCursorStore as k, Result as l, StreamsCrdtOptions as m, isValidBucketId as n, CrdtUpdateBatch as o, StreamsAuthProvider as p, isValidRillId as r, JsonObject as s, createStreamUrl as t, SnapshotCodec as u, TransportJoinParams as v, InMemoryRemoteCursorStore as w, TransportSyncSuccess as x, TransportRoomStatus as y };
495
- //# sourceMappingURL=stream-id-BfuQIfcB.d.ts.map
515
+ export { RemoteCursorStore as A, BeforeRemoteCursorSaveContext as C, IndexedDbRemoteCursorStoreOptions as D, IndexedDbRemoteCursorStore as E, RemoteCursor as O, TransportSyncSuccess as S, InMemoryRemoteCursorStore as T, TransportDeleteStreamSuccess as _, CrdtAdapter as a, TransportRoomStatus as b, JsonValue as c, SnapshotTransformHook as d, SnapshotUploadOptions as f, TransportCreateStreamSuccess as g, StreamsCrdtOptions as h, StreamsCrdt as i, createInitialRemoteCursor as j, RemoteCursorSaveSource as k, Result as l, StreamsAuthProvider as m, isValidBucketId as n, CrdtUpdateBatch as o, StreamsAuthContext as p, isValidRillId as r, JsonObject as s, createStreamUrl as t, SnapshotCodec as u, TransportError as v, BeforeRemoteCursorSaveHook as w, TransportSubscription as x, TransportJoinParams as y };
516
+ //# sourceMappingURL=stream-id-BxGVitUa.d.ts.map