@tinycloud/sdk-services 3.7.0-beta.2 → 3.7.0-beta.3
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/index.cjs +96 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -14
- package/dist/index.d.ts +40 -14
- package/dist/index.js +96 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1574,50 +1574,76 @@ interface StorageFullEvent {
|
|
|
1574
1574
|
type StorageFullHandler = (event: StorageFullEvent) => void;
|
|
1575
1575
|
/**
|
|
1576
1576
|
* Turns the storage rejections seen by any number of service contexts into
|
|
1577
|
-
*
|
|
1577
|
+
* `storage.full` events, and remembers whose storage is full so read paths
|
|
1578
1578
|
* can skip their best-effort writes.
|
|
1579
1579
|
*
|
|
1580
1580
|
* Storage is one budget per owner, so fullness is recorded per owner, apart
|
|
1581
1581
|
* from the event: a rejection on one owner's space never hides a later
|
|
1582
|
-
* rejection on another's.
|
|
1583
|
-
*
|
|
1582
|
+
* rejection on another's.
|
|
1583
|
+
*
|
|
1584
|
+
* The signed-in account's own storage has an episode. Its first rejection
|
|
1585
|
+
* starts the episode and emits `storage.full`; later rejections do not emit
|
|
1586
|
+
* again. The episode ends only when a write to that owner's storage succeeds,
|
|
1587
|
+
* or on {@link clear}. A status read does not end it. A rejection on another
|
|
1588
|
+
* owner's delegated space emits `storage.full` once per owner until a status
|
|
1589
|
+
* read or a successful write clears that owner, and starts no episode.
|
|
1584
1590
|
*
|
|
1585
1591
|
* The event goes through the rejecting context's `emit` (so telemetry and the
|
|
1586
1592
|
* debug log see it) and then to the handlers registered with {@link on}.
|
|
1587
1593
|
*/
|
|
1588
1594
|
declare class StorageFullMonitor {
|
|
1589
1595
|
private readonly ownerOf;
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1596
|
+
private readonly ownOwner;
|
|
1597
|
+
/** The own-storage episode, until a successful write or {@link clear} ends it. */
|
|
1598
|
+
private episode;
|
|
1599
|
+
/**
|
|
1600
|
+
* First rejection per owner key; {@link UNKNOWN_OWNER} when the space was unknown.
|
|
1601
|
+
* A status read or a successful write removes the owner, so best-effort writes resume.
|
|
1602
|
+
*/
|
|
1593
1603
|
private readonly full;
|
|
1594
1604
|
private readonly handlers;
|
|
1605
|
+
private readonly episodeListeners;
|
|
1606
|
+
private readonly delegatedListeners;
|
|
1595
1607
|
/**
|
|
1596
1608
|
* @param ownerOf Owner key of a space id. Spaces with one key share one
|
|
1597
1609
|
* storage budget. Defaults to the space id itself.
|
|
1610
|
+
* @param ownOwner Owner key of the signed-in account, or undefined when
|
|
1611
|
+
* there is none. Rejections on that owner's storage, or on a space that was
|
|
1612
|
+
* unknown, start the episode.
|
|
1598
1613
|
*/
|
|
1599
|
-
constructor(ownerOf?: (spaceId: string) => string);
|
|
1600
|
-
/** True while any owner's storage is known to be full. */
|
|
1614
|
+
constructor(ownerOf?: (spaceId: string) => string, ownOwner?: () => string | undefined);
|
|
1615
|
+
/** True while any owner's storage is known to be full for best-effort writes. */
|
|
1601
1616
|
get isFull(): boolean;
|
|
1602
|
-
/** The rejection
|
|
1617
|
+
/** The rejection that started the own-storage episode, until it ends. */
|
|
1603
1618
|
get rejection(): StorageFullEvent | undefined;
|
|
1604
1619
|
/**
|
|
1605
1620
|
* True when a write to `spaceId`'s owner was rejected, or a write whose
|
|
1606
1621
|
* space was unknown was: that one could have been any owner's.
|
|
1607
1622
|
*/
|
|
1608
1623
|
isFullFor(spaceId: string | undefined): boolean;
|
|
1609
|
-
/**
|
|
1624
|
+
/** Subscribe to the own-storage episode starting or ending. Returns the unsubscribe function. */
|
|
1625
|
+
onEpisode(listener: () => void): () => void;
|
|
1626
|
+
/**
|
|
1627
|
+
* Subscribe to a delegated owner's storage being found full: once per owner
|
|
1628
|
+
* until that owner is cleared. The own-storage episode does not fire it.
|
|
1629
|
+
* Returns the unsubscribe function.
|
|
1630
|
+
*/
|
|
1631
|
+
onDelegated(listener: () => void): () => void;
|
|
1632
|
+
/** Watch a context's service errors and successful writes. Returns the unsubscribe function. */
|
|
1610
1633
|
observe(context: IServiceContext): () => void;
|
|
1611
1634
|
/** Subscribe to the `storage.full` event. Returns the unsubscribe function. */
|
|
1612
1635
|
on(handler: StorageFullHandler): () => void;
|
|
1613
|
-
/** Forget
|
|
1636
|
+
/** Forget every owner's full state and end the episode; the next rejection starts a new one. */
|
|
1614
1637
|
clear(): void;
|
|
1615
1638
|
/**
|
|
1616
|
-
* Forget that `spaceId`'s owner is full (`undefined`:
|
|
1617
|
-
* space was unknown).
|
|
1618
|
-
* `storage.full` again.
|
|
1639
|
+
* Forget that `spaceId`'s owner is full for best-effort writes (`undefined`:
|
|
1640
|
+
* the rejections whose space was unknown). Does not end the episode.
|
|
1619
1641
|
*/
|
|
1620
1642
|
clearFor(spaceId: string | undefined): void;
|
|
1643
|
+
private isOwn;
|
|
1644
|
+
private ownerFor;
|
|
1645
|
+
private notifyEpisode;
|
|
1646
|
+
private notifyDelegated;
|
|
1621
1647
|
}
|
|
1622
1648
|
|
|
1623
1649
|
/** Where the owner frees up space or upgrades their plan. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1574,50 +1574,76 @@ interface StorageFullEvent {
|
|
|
1574
1574
|
type StorageFullHandler = (event: StorageFullEvent) => void;
|
|
1575
1575
|
/**
|
|
1576
1576
|
* Turns the storage rejections seen by any number of service contexts into
|
|
1577
|
-
*
|
|
1577
|
+
* `storage.full` events, and remembers whose storage is full so read paths
|
|
1578
1578
|
* can skip their best-effort writes.
|
|
1579
1579
|
*
|
|
1580
1580
|
* Storage is one budget per owner, so fullness is recorded per owner, apart
|
|
1581
1581
|
* from the event: a rejection on one owner's space never hides a later
|
|
1582
|
-
* rejection on another's.
|
|
1583
|
-
*
|
|
1582
|
+
* rejection on another's.
|
|
1583
|
+
*
|
|
1584
|
+
* The signed-in account's own storage has an episode. Its first rejection
|
|
1585
|
+
* starts the episode and emits `storage.full`; later rejections do not emit
|
|
1586
|
+
* again. The episode ends only when a write to that owner's storage succeeds,
|
|
1587
|
+
* or on {@link clear}. A status read does not end it. A rejection on another
|
|
1588
|
+
* owner's delegated space emits `storage.full` once per owner until a status
|
|
1589
|
+
* read or a successful write clears that owner, and starts no episode.
|
|
1584
1590
|
*
|
|
1585
1591
|
* The event goes through the rejecting context's `emit` (so telemetry and the
|
|
1586
1592
|
* debug log see it) and then to the handlers registered with {@link on}.
|
|
1587
1593
|
*/
|
|
1588
1594
|
declare class StorageFullMonitor {
|
|
1589
1595
|
private readonly ownerOf;
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1596
|
+
private readonly ownOwner;
|
|
1597
|
+
/** The own-storage episode, until a successful write or {@link clear} ends it. */
|
|
1598
|
+
private episode;
|
|
1599
|
+
/**
|
|
1600
|
+
* First rejection per owner key; {@link UNKNOWN_OWNER} when the space was unknown.
|
|
1601
|
+
* A status read or a successful write removes the owner, so best-effort writes resume.
|
|
1602
|
+
*/
|
|
1593
1603
|
private readonly full;
|
|
1594
1604
|
private readonly handlers;
|
|
1605
|
+
private readonly episodeListeners;
|
|
1606
|
+
private readonly delegatedListeners;
|
|
1595
1607
|
/**
|
|
1596
1608
|
* @param ownerOf Owner key of a space id. Spaces with one key share one
|
|
1597
1609
|
* storage budget. Defaults to the space id itself.
|
|
1610
|
+
* @param ownOwner Owner key of the signed-in account, or undefined when
|
|
1611
|
+
* there is none. Rejections on that owner's storage, or on a space that was
|
|
1612
|
+
* unknown, start the episode.
|
|
1598
1613
|
*/
|
|
1599
|
-
constructor(ownerOf?: (spaceId: string) => string);
|
|
1600
|
-
/** True while any owner's storage is known to be full. */
|
|
1614
|
+
constructor(ownerOf?: (spaceId: string) => string, ownOwner?: () => string | undefined);
|
|
1615
|
+
/** True while any owner's storage is known to be full for best-effort writes. */
|
|
1601
1616
|
get isFull(): boolean;
|
|
1602
|
-
/** The rejection
|
|
1617
|
+
/** The rejection that started the own-storage episode, until it ends. */
|
|
1603
1618
|
get rejection(): StorageFullEvent | undefined;
|
|
1604
1619
|
/**
|
|
1605
1620
|
* True when a write to `spaceId`'s owner was rejected, or a write whose
|
|
1606
1621
|
* space was unknown was: that one could have been any owner's.
|
|
1607
1622
|
*/
|
|
1608
1623
|
isFullFor(spaceId: string | undefined): boolean;
|
|
1609
|
-
/**
|
|
1624
|
+
/** Subscribe to the own-storage episode starting or ending. Returns the unsubscribe function. */
|
|
1625
|
+
onEpisode(listener: () => void): () => void;
|
|
1626
|
+
/**
|
|
1627
|
+
* Subscribe to a delegated owner's storage being found full: once per owner
|
|
1628
|
+
* until that owner is cleared. The own-storage episode does not fire it.
|
|
1629
|
+
* Returns the unsubscribe function.
|
|
1630
|
+
*/
|
|
1631
|
+
onDelegated(listener: () => void): () => void;
|
|
1632
|
+
/** Watch a context's service errors and successful writes. Returns the unsubscribe function. */
|
|
1610
1633
|
observe(context: IServiceContext): () => void;
|
|
1611
1634
|
/** Subscribe to the `storage.full` event. Returns the unsubscribe function. */
|
|
1612
1635
|
on(handler: StorageFullHandler): () => void;
|
|
1613
|
-
/** Forget
|
|
1636
|
+
/** Forget every owner's full state and end the episode; the next rejection starts a new one. */
|
|
1614
1637
|
clear(): void;
|
|
1615
1638
|
/**
|
|
1616
|
-
* Forget that `spaceId`'s owner is full (`undefined`:
|
|
1617
|
-
* space was unknown).
|
|
1618
|
-
* `storage.full` again.
|
|
1639
|
+
* Forget that `spaceId`'s owner is full for best-effort writes (`undefined`:
|
|
1640
|
+
* the rejections whose space was unknown). Does not end the episode.
|
|
1619
1641
|
*/
|
|
1620
1642
|
clearFor(spaceId: string | undefined): void;
|
|
1643
|
+
private isOwn;
|
|
1644
|
+
private ownerFor;
|
|
1645
|
+
private notifyEpisode;
|
|
1646
|
+
private notifyDelegated;
|
|
1621
1647
|
}
|
|
1622
1648
|
|
|
1623
1649
|
/** Where the owner frees up space or upgrades their plan. */
|
package/dist/index.js
CHANGED
|
@@ -5395,20 +5395,29 @@ var StorageFullMonitor = class {
|
|
|
5395
5395
|
/**
|
|
5396
5396
|
* @param ownerOf Owner key of a space id. Spaces with one key share one
|
|
5397
5397
|
* storage budget. Defaults to the space id itself.
|
|
5398
|
+
* @param ownOwner Owner key of the signed-in account, or undefined when
|
|
5399
|
+
* there is none. Rejections on that owner's storage, or on a space that was
|
|
5400
|
+
* unknown, start the episode.
|
|
5398
5401
|
*/
|
|
5399
|
-
constructor(ownerOf = (spaceId) => spaceId) {
|
|
5402
|
+
constructor(ownerOf = (spaceId) => spaceId, ownOwner = () => void 0) {
|
|
5400
5403
|
this.ownerOf = ownerOf;
|
|
5401
|
-
|
|
5404
|
+
this.ownOwner = ownOwner;
|
|
5405
|
+
/**
|
|
5406
|
+
* First rejection per owner key; {@link UNKNOWN_OWNER} when the space was unknown.
|
|
5407
|
+
* A status read or a successful write removes the owner, so best-effort writes resume.
|
|
5408
|
+
*/
|
|
5402
5409
|
this.full = /* @__PURE__ */ new Map();
|
|
5403
5410
|
this.handlers = /* @__PURE__ */ new Set();
|
|
5411
|
+
this.episodeListeners = /* @__PURE__ */ new Set();
|
|
5412
|
+
this.delegatedListeners = /* @__PURE__ */ new Set();
|
|
5404
5413
|
}
|
|
5405
|
-
/** True while any owner's storage is known to be full. */
|
|
5414
|
+
/** True while any owner's storage is known to be full for best-effort writes. */
|
|
5406
5415
|
get isFull() {
|
|
5407
5416
|
return this.full.size > 0;
|
|
5408
5417
|
}
|
|
5409
|
-
/** The rejection
|
|
5418
|
+
/** The rejection that started the own-storage episode, until it ends. */
|
|
5410
5419
|
get rejection() {
|
|
5411
|
-
return this.event;
|
|
5420
|
+
return this.episode?.event;
|
|
5412
5421
|
}
|
|
5413
5422
|
/**
|
|
5414
5423
|
* True when a write to `spaceId`'s owner was rejected, or a write whose
|
|
@@ -5417,13 +5426,31 @@ var StorageFullMonitor = class {
|
|
|
5417
5426
|
isFullFor(spaceId) {
|
|
5418
5427
|
return this.full.has(UNKNOWN_OWNER) || spaceId !== void 0 && this.full.has(this.ownerOf(spaceId));
|
|
5419
5428
|
}
|
|
5420
|
-
/**
|
|
5429
|
+
/** Subscribe to the own-storage episode starting or ending. Returns the unsubscribe function. */
|
|
5430
|
+
onEpisode(listener) {
|
|
5431
|
+
this.episodeListeners.add(listener);
|
|
5432
|
+
return () => {
|
|
5433
|
+
this.episodeListeners.delete(listener);
|
|
5434
|
+
};
|
|
5435
|
+
}
|
|
5436
|
+
/**
|
|
5437
|
+
* Subscribe to a delegated owner's storage being found full: once per owner
|
|
5438
|
+
* until that owner is cleared. The own-storage episode does not fire it.
|
|
5439
|
+
* Returns the unsubscribe function.
|
|
5440
|
+
*/
|
|
5441
|
+
onDelegated(listener) {
|
|
5442
|
+
this.delegatedListeners.add(listener);
|
|
5443
|
+
return () => {
|
|
5444
|
+
this.delegatedListeners.delete(listener);
|
|
5445
|
+
};
|
|
5446
|
+
}
|
|
5447
|
+
/** Watch a context's service errors and successful writes. Returns the unsubscribe function. */
|
|
5421
5448
|
observe(context) {
|
|
5422
|
-
|
|
5449
|
+
const stopErrors = context.on(TelemetryEvents.SERVICE_ERROR, (data) => {
|
|
5423
5450
|
const { service, error } = data;
|
|
5424
5451
|
if (!error || !isStorageFullError(error)) return;
|
|
5425
5452
|
const spaceId = context.session?.spaceId;
|
|
5426
|
-
const owner =
|
|
5453
|
+
const owner = this.ownerFor(spaceId);
|
|
5427
5454
|
if (this.full.has(owner)) return;
|
|
5428
5455
|
const meta = error.meta ?? {};
|
|
5429
5456
|
const event = { service: service ?? error.service, error };
|
|
@@ -5434,8 +5461,13 @@ var StorageFullMonitor = class {
|
|
|
5434
5461
|
}
|
|
5435
5462
|
if (spaceId) event.spaceId = spaceId;
|
|
5436
5463
|
this.full.set(owner, event);
|
|
5437
|
-
if (this.
|
|
5438
|
-
|
|
5464
|
+
if (this.isOwn(owner)) {
|
|
5465
|
+
if (this.episode) return;
|
|
5466
|
+
this.episode = { owner, event };
|
|
5467
|
+
this.notifyEpisode();
|
|
5468
|
+
} else {
|
|
5469
|
+
this.notifyDelegated();
|
|
5470
|
+
}
|
|
5439
5471
|
context.emit(TelemetryEvents.STORAGE_FULL, event);
|
|
5440
5472
|
for (const handler of this.handlers) {
|
|
5441
5473
|
try {
|
|
@@ -5445,6 +5477,19 @@ var StorageFullMonitor = class {
|
|
|
5445
5477
|
}
|
|
5446
5478
|
}
|
|
5447
5479
|
});
|
|
5480
|
+
const stopWrites = context.on(TelemetryEvents.SERVICE_RESPONSE, (data) => {
|
|
5481
|
+
const { service, action, ok: ok2 } = data;
|
|
5482
|
+
if (ok2 !== true || !isCommittingWrite(service, action)) return;
|
|
5483
|
+
const owner = this.ownerFor(context.session?.spaceId);
|
|
5484
|
+
this.full.delete(owner);
|
|
5485
|
+
if (this.episode?.owner !== owner) return;
|
|
5486
|
+
this.episode = void 0;
|
|
5487
|
+
this.notifyEpisode();
|
|
5488
|
+
});
|
|
5489
|
+
return () => {
|
|
5490
|
+
stopErrors();
|
|
5491
|
+
stopWrites();
|
|
5492
|
+
};
|
|
5448
5493
|
}
|
|
5449
5494
|
/** Subscribe to the `storage.full` event. Returns the unsubscribe function. */
|
|
5450
5495
|
on(handler) {
|
|
@@ -5453,22 +5498,56 @@ var StorageFullMonitor = class {
|
|
|
5453
5498
|
this.handlers.delete(handler);
|
|
5454
5499
|
};
|
|
5455
5500
|
}
|
|
5456
|
-
/** Forget
|
|
5501
|
+
/** Forget every owner's full state and end the episode; the next rejection starts a new one. */
|
|
5457
5502
|
clear() {
|
|
5458
5503
|
this.full.clear();
|
|
5459
|
-
this.
|
|
5504
|
+
if (!this.episode) return;
|
|
5505
|
+
this.episode = void 0;
|
|
5506
|
+
this.notifyEpisode();
|
|
5460
5507
|
}
|
|
5461
5508
|
/**
|
|
5462
|
-
* Forget that `spaceId`'s owner is full (`undefined`:
|
|
5463
|
-
* space was unknown).
|
|
5464
|
-
* `storage.full` again.
|
|
5509
|
+
* Forget that `spaceId`'s owner is full for best-effort writes (`undefined`:
|
|
5510
|
+
* the rejections whose space was unknown). Does not end the episode.
|
|
5465
5511
|
*/
|
|
5466
5512
|
clearFor(spaceId) {
|
|
5467
|
-
this.full.delete(
|
|
5468
|
-
|
|
5513
|
+
this.full.delete(this.ownerFor(spaceId));
|
|
5514
|
+
}
|
|
5515
|
+
isOwn(owner) {
|
|
5516
|
+
return owner === UNKNOWN_OWNER || owner === this.ownOwner();
|
|
5517
|
+
}
|
|
5518
|
+
ownerFor(spaceId) {
|
|
5519
|
+
return spaceId ? this.ownerOf(spaceId) : UNKNOWN_OWNER;
|
|
5520
|
+
}
|
|
5521
|
+
notifyEpisode() {
|
|
5522
|
+
for (const listener of this.episodeListeners) {
|
|
5523
|
+
try {
|
|
5524
|
+
listener();
|
|
5525
|
+
} catch (listenerError) {
|
|
5526
|
+
console.error("Error in storage episode listener:", listenerError);
|
|
5527
|
+
}
|
|
5528
|
+
}
|
|
5529
|
+
}
|
|
5530
|
+
notifyDelegated() {
|
|
5531
|
+
for (const listener of this.delegatedListeners) {
|
|
5532
|
+
try {
|
|
5533
|
+
listener();
|
|
5534
|
+
} catch (listenerError) {
|
|
5535
|
+
console.error("Error in delegated storage listener:", listenerError);
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5469
5538
|
}
|
|
5470
5539
|
};
|
|
5471
5540
|
var UNKNOWN_OWNER = "\0unknown";
|
|
5541
|
+
var COMMITTING_ACTIONS = /* @__PURE__ */ new Map([
|
|
5542
|
+
["kv", ["put", "batchPut"]],
|
|
5543
|
+
["vault", ["put"]],
|
|
5544
|
+
["sql", ["execute", "batch", "executeStatement", "migrations.apply"]],
|
|
5545
|
+
["duckdb", ["execute", "batch", "executeStatement", "import"]]
|
|
5546
|
+
]);
|
|
5547
|
+
function isCommittingWrite(service, action) {
|
|
5548
|
+
if (service === void 0 || action === void 0) return false;
|
|
5549
|
+
return COMMITTING_ACTIONS.get(service)?.includes(action) ?? false;
|
|
5550
|
+
}
|
|
5472
5551
|
|
|
5473
5552
|
// src/storage/status.ts
|
|
5474
5553
|
var STORAGE_MANAGE_URL = "https://account.tinycloud.xyz/storage";
|