@shiprev/server-sdk-node 0.0.0-stage → 0.1.0-beta.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.
Files changed (45) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +276 -2
  3. package/dist/auth/token-verifier.d.ts +56 -0
  4. package/dist/auth/token-verifier.js +257 -0
  5. package/dist/client/connection-supervisor.d.ts +148 -0
  6. package/dist/client/connection-supervisor.js +650 -0
  7. package/dist/client/shiprev-client.d.ts +139 -0
  8. package/dist/client/shiprev-client.js +400 -0
  9. package/dist/index.d.ts +16 -0
  10. package/dist/index.js +47 -0
  11. package/dist/metering/backlog/host-adapter-backlog.d.ts +10 -0
  12. package/dist/metering/backlog/host-adapter-backlog.js +27 -0
  13. package/dist/metering/backlog/journal-backlog.d.ts +14 -0
  14. package/dist/metering/backlog/journal-backlog.js +135 -0
  15. package/dist/metering/backlog/memory-backlog.d.ts +9 -0
  16. package/dist/metering/backlog/memory-backlog.js +34 -0
  17. package/dist/metering/usage-buffer.d.ts +59 -0
  18. package/dist/metering/usage-buffer.js +175 -0
  19. package/dist/mirror/degradation.d.ts +22 -0
  20. package/dist/mirror/degradation.js +67 -0
  21. package/dist/mirror/entitlement-mirror.d.ts +61 -0
  22. package/dist/mirror/entitlement-mirror.js +143 -0
  23. package/dist/nest/decorators.d.ts +15 -0
  24. package/dist/nest/decorators.js +21 -0
  25. package/dist/nest/index.d.ts +4 -0
  26. package/dist/nest/index.js +21 -0
  27. package/dist/nest/shiprev-auth.guard.d.ts +7 -0
  28. package/dist/nest/shiprev-auth.guard.js +52 -0
  29. package/dist/nest/shiprev-capability.guard.d.ts +9 -0
  30. package/dist/nest/shiprev-capability.guard.js +65 -0
  31. package/dist/nest/shiprev.module.d.ts +17 -0
  32. package/dist/nest/shiprev.module.js +57 -0
  33. package/dist/ruling/adjudicator.d.ts +15 -0
  34. package/dist/ruling/adjudicator.js +123 -0
  35. package/dist/transport/event-stream.d.ts +27 -0
  36. package/dist/transport/event-stream.js +92 -0
  37. package/dist/transport/http.transport.d.ts +69 -0
  38. package/dist/transport/http.transport.js +238 -0
  39. package/dist/transport/wire.d.ts +79 -0
  40. package/dist/transport/wire.js +8 -0
  41. package/dist/types.d.ts +252 -0
  42. package/dist/types.js +37 -0
  43. package/dist/util/decimal.d.ts +14 -0
  44. package/dist/util/decimal.js +57 -0
  45. package/package.json +96 -3
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.JournalBacklog = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ class JournalBacklog {
40
+ filePath;
41
+ queue = [];
42
+ isCompacting = false;
43
+ constructor(filePath) {
44
+ this.filePath = filePath || path.resolve(process.cwd(), '.shiprev-usage-journal.log');
45
+ this.recoverSync();
46
+ }
47
+ recoverSync() {
48
+ try {
49
+ if (!fs.existsSync(this.filePath)) {
50
+ return;
51
+ }
52
+ const content = fs.readFileSync(this.filePath, 'utf-8');
53
+ const lines = content.split('\n').filter((l) => l.trim().length > 0);
54
+ for (const line of lines) {
55
+ try {
56
+ const parsed = JSON.parse(line);
57
+ if (parsed.event && parsed.id) {
58
+ this.queue.push({
59
+ id: parsed.id,
60
+ event: parsed.event,
61
+ enqueued_at: parsed.enqueued_at ?? Date.now(),
62
+ attempts: parsed.attempts ?? 0,
63
+ });
64
+ }
65
+ }
66
+ catch {
67
+ // Ignore corrupt line to protect rest of journal
68
+ }
69
+ }
70
+ }
71
+ catch {
72
+ // Ignore recovery read error
73
+ }
74
+ }
75
+ async enqueue(event) {
76
+ const item = {
77
+ id: event.usage_event_id,
78
+ event,
79
+ enqueued_at: Date.now(),
80
+ attempts: 0,
81
+ };
82
+ this.queue.push(item);
83
+ // Atomically append line to journal file
84
+ try {
85
+ const line = JSON.stringify(item) + '\n';
86
+ await fs.promises.appendFile(this.filePath, line, 'utf-8');
87
+ }
88
+ catch {
89
+ // In-memory queue remains even if disk append encounters temporary issue
90
+ }
91
+ }
92
+ async peek(limit) {
93
+ return this.queue.slice(0, limit);
94
+ }
95
+ async ack(ids) {
96
+ const idSet = new Set(ids);
97
+ this.queue = this.queue.filter((item) => !idSet.has(item.id));
98
+ await this.compact();
99
+ }
100
+ async nack(ids) {
101
+ const idSet = new Set(ids);
102
+ for (const item of this.queue) {
103
+ if (idSet.has(item.id)) {
104
+ item.attempts += 1;
105
+ }
106
+ }
107
+ }
108
+ async size() {
109
+ return this.queue.length;
110
+ }
111
+ async compact() {
112
+ if (this.isCompacting)
113
+ return;
114
+ this.isCompacting = true;
115
+ try {
116
+ if (this.queue.length === 0) {
117
+ if (fs.existsSync(this.filePath)) {
118
+ await fs.promises.writeFile(this.filePath, '', 'utf-8');
119
+ }
120
+ }
121
+ else {
122
+ const lines = this.queue.map((item) => JSON.stringify(item)).join('\n') + '\n';
123
+ await fs.promises.writeFile(this.filePath, lines, 'utf-8');
124
+ }
125
+ }
126
+ catch {
127
+ // Best-effort compaction
128
+ }
129
+ finally {
130
+ this.isCompacting = false;
131
+ }
132
+ }
133
+ }
134
+ exports.JournalBacklog = JournalBacklog;
135
+ //# sourceMappingURL=journal-backlog.js.map
@@ -0,0 +1,9 @@
1
+ import { BacklogAdapter, BacklogItem, UsageEventItem } from '../../types';
2
+ export declare class MemoryBacklog implements BacklogAdapter {
3
+ private queue;
4
+ enqueue(event: UsageEventItem): Promise<void>;
5
+ peek(limit: number): Promise<BacklogItem[]>;
6
+ ack(ids: string[]): Promise<void>;
7
+ nack(ids: string[]): Promise<void>;
8
+ size(): Promise<number>;
9
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryBacklog = void 0;
4
+ class MemoryBacklog {
5
+ queue = [];
6
+ async enqueue(event) {
7
+ this.queue.push({
8
+ id: event.usage_event_id,
9
+ event,
10
+ enqueued_at: Date.now(),
11
+ attempts: 0,
12
+ });
13
+ }
14
+ async peek(limit) {
15
+ return this.queue.slice(0, limit);
16
+ }
17
+ async ack(ids) {
18
+ const idSet = new Set(ids);
19
+ this.queue = this.queue.filter((item) => !idSet.has(item.id));
20
+ }
21
+ async nack(ids) {
22
+ const idSet = new Set(ids);
23
+ for (const item of this.queue) {
24
+ if (idSet.has(item.id)) {
25
+ item.attempts += 1;
26
+ }
27
+ }
28
+ }
29
+ async size() {
30
+ return this.queue.length;
31
+ }
32
+ }
33
+ exports.MemoryBacklog = MemoryBacklog;
34
+ //# sourceMappingURL=memory-backlog.js.map
@@ -0,0 +1,59 @@
1
+ import { HttpTransport } from '../transport/http.transport';
2
+ import { BacklogConfig, RejectedEvent, UsageBufferConfig, UsageEventItem, UsageEventsResponse } from '../types';
3
+ export interface UsageBufferOptions {
4
+ transport: HttpTransport;
5
+ getToken: () => string | null;
6
+ isOnline: () => boolean;
7
+ /**
8
+ * Delivers a batch; defaults to `transport.reportUsageEvents(events, getToken())`. The client
9
+ * passes a sender that renews the token once on a 401.
10
+ */
11
+ send?: (events: UsageEventItem[]) => Promise<UsageEventsResponse>;
12
+ bufferConfig?: UsageBufferConfig;
13
+ backlogConfig?: BacklogConfig;
14
+ onFlushFailure?: (events: UsageEventItem[], error: Error) => void;
15
+ onRejected?: (rejected: RejectedEvent[]) => void;
16
+ }
17
+ export declare class UsageBuffer {
18
+ private readonly transport;
19
+ private readonly getToken;
20
+ private readonly isOnline;
21
+ private readonly send?;
22
+ private readonly batchSize;
23
+ private readonly flushIntervalMs;
24
+ private readonly backlog;
25
+ private readonly onFlushFailure?;
26
+ private readonly onRejected?;
27
+ private buffer;
28
+ private timer;
29
+ /** The pass in flight, if any; at most one runs at a time. */
30
+ private inFlight;
31
+ private isStopped;
32
+ constructor(options: UsageBufferOptions);
33
+ /**
34
+ * Appends an event to the buffer. If buffer reaches batchSize, triggers flush.
35
+ */
36
+ reportUsage(event: UsageEventItem): Promise<void>;
37
+ /**
38
+ * Delivers every usage event reported before this call: the backlog first, in batches, then the
39
+ * in-memory buffer. A flush already in flight is awaited first, then this call runs its own pass.
40
+ * Resolves once each of those events was accepted, refused (`onRejected`), or moved to the
41
+ * backlog because it could not be delivered (offline, no token, or a failed request).
42
+ */
43
+ flush(): Promise<void>;
44
+ private runPass;
45
+ /**
46
+ * One pass. With `drainBacklog` the backlog is sent in batches until it is empty or a batch
47
+ * fails; without it (timer ticks) at most one backlog batch is sent.
48
+ */
49
+ private pass;
50
+ private flushBacklog;
51
+ private deliver;
52
+ private startTimer;
53
+ /**
54
+ * Gracefully stops the buffer flusher and performs final drain.
55
+ */
56
+ stop(): Promise<void>;
57
+ getBufferSize(): number;
58
+ getBacklogSize(): Promise<number>;
59
+ }
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UsageBuffer = void 0;
4
+ const host_adapter_backlog_1 = require("./backlog/host-adapter-backlog");
5
+ const journal_backlog_1 = require("./backlog/journal-backlog");
6
+ const memory_backlog_1 = require("./backlog/memory-backlog");
7
+ class UsageBuffer {
8
+ transport;
9
+ getToken;
10
+ isOnline;
11
+ send;
12
+ batchSize;
13
+ flushIntervalMs;
14
+ backlog;
15
+ onFlushFailure;
16
+ onRejected;
17
+ buffer = [];
18
+ timer = null;
19
+ /** The pass in flight, if any; at most one runs at a time. */
20
+ inFlight = null;
21
+ isStopped = false;
22
+ constructor(options) {
23
+ this.transport = options.transport;
24
+ this.getToken = options.getToken;
25
+ this.isOnline = options.isOnline;
26
+ this.send = options.send;
27
+ this.batchSize = options.bufferConfig?.batch_size ?? 100;
28
+ this.flushIntervalMs = options.bufferConfig?.flush_interval_ms ?? 5000;
29
+ this.onFlushFailure = options.onFlushFailure;
30
+ this.onRejected = options.onRejected;
31
+ // Initialize backlog adapter based on mode
32
+ const mode = options.backlogConfig?.mode ?? 'memory';
33
+ if (mode === 'journal') {
34
+ this.backlog = new journal_backlog_1.JournalBacklog(options.backlogConfig?.journal_path);
35
+ }
36
+ else if (mode === 'host_adapter') {
37
+ this.backlog = new host_adapter_backlog_1.HostAdapterBacklog(options.backlogConfig?.adapter);
38
+ }
39
+ else {
40
+ this.backlog = new memory_backlog_1.MemoryBacklog();
41
+ }
42
+ this.startTimer();
43
+ }
44
+ /**
45
+ * Appends an event to the buffer. If buffer reaches batchSize, triggers flush.
46
+ */
47
+ async reportUsage(event) {
48
+ if (this.isStopped) {
49
+ throw new Error('UsageBuffer is stopped, cannot report new events');
50
+ }
51
+ this.buffer.push(event);
52
+ if (this.buffer.length >= this.batchSize) {
53
+ await this.flush();
54
+ }
55
+ }
56
+ /**
57
+ * Delivers every usage event reported before this call: the backlog first, in batches, then the
58
+ * in-memory buffer. A flush already in flight is awaited first, then this call runs its own pass.
59
+ * Resolves once each of those events was accepted, refused (`onRejected`), or moved to the
60
+ * backlog because it could not be delivered (offline, no token, or a failed request).
61
+ */
62
+ async flush() {
63
+ while (this.inFlight) {
64
+ await this.inFlight.catch(() => undefined);
65
+ }
66
+ await this.runPass(true);
67
+ }
68
+ runPass(drainBacklog) {
69
+ const pass = this.pass(drainBacklog).finally(() => {
70
+ if (this.inFlight === pass)
71
+ this.inFlight = null;
72
+ });
73
+ this.inFlight = pass;
74
+ return pass;
75
+ }
76
+ /**
77
+ * One pass. With `drainBacklog` the backlog is sent in batches until it is empty or a batch
78
+ * fails; without it (timer ticks) at most one backlog batch is sent.
79
+ */
80
+ async pass(drainBacklog) {
81
+ const token = this.getToken();
82
+ const online = this.isOnline();
83
+ // If offline or no token available, route memory events to backlog directly
84
+ if (!online || !token) {
85
+ if (this.buffer.length > 0) {
86
+ const eventsToBacklog = [...this.buffer];
87
+ this.buffer = [];
88
+ for (const ev of eventsToBacklog) {
89
+ await this.backlog.enqueue(ev);
90
+ }
91
+ }
92
+ return;
93
+ }
94
+ // 1. Drain pending backlog first; bounded by its size now, so later arrivals cannot extend it
95
+ const batches = drainBacklog ? Math.ceil((await this.backlog.size()) / this.batchSize) : 1;
96
+ for (let i = 0; i < batches; i++) {
97
+ if ((await this.flushBacklog(token)) !== 'delivered')
98
+ break;
99
+ }
100
+ // 2. Drain current memory buffer
101
+ if (this.buffer.length > 0) {
102
+ const eventsToSend = [...this.buffer];
103
+ this.buffer = [];
104
+ try {
105
+ const res = await this.deliver(eventsToSend, token);
106
+ if (res.rejected && res.rejected.length > 0) {
107
+ this.onRejected?.(res.rejected);
108
+ }
109
+ }
110
+ catch (err) {
111
+ const error = err instanceof Error ? err : new Error(String(err));
112
+ this.onFlushFailure?.(eventsToSend, error);
113
+ // Persist to backlog on failure
114
+ for (const ev of eventsToSend) {
115
+ await this.backlog.enqueue(ev);
116
+ }
117
+ }
118
+ }
119
+ }
120
+ async flushBacklog(token) {
121
+ const backlogItems = await this.backlog.peek(this.batchSize);
122
+ if (backlogItems.length === 0)
123
+ return 'empty';
124
+ const events = backlogItems.map((item) => item.event);
125
+ const ids = backlogItems.map((item) => item.id);
126
+ try {
127
+ const res = await this.deliver(events, token);
128
+ await this.backlog.ack(ids);
129
+ if (res.rejected && res.rejected.length > 0) {
130
+ this.onRejected?.(res.rejected);
131
+ }
132
+ return 'delivered';
133
+ }
134
+ catch {
135
+ await this.backlog.nack(ids);
136
+ return 'failed';
137
+ }
138
+ }
139
+ deliver(events, token) {
140
+ return this.send ? this.send(events) : this.transport.reportUsageEvents(events, token);
141
+ }
142
+ startTimer() {
143
+ if (this.flushIntervalMs <= 0)
144
+ return;
145
+ this.timer = setInterval(() => {
146
+ // A tick skips while a pass is in flight, and sends at most one backlog batch.
147
+ if (this.inFlight)
148
+ return;
149
+ void this.runPass(false);
150
+ }, this.flushIntervalMs);
151
+ // Don't keep Node process alive solely for background flush interval
152
+ if (this.timer.unref) {
153
+ this.timer.unref();
154
+ }
155
+ }
156
+ /**
157
+ * Gracefully stops the buffer flusher and performs final drain.
158
+ */
159
+ async stop() {
160
+ this.isStopped = true;
161
+ if (this.timer) {
162
+ clearInterval(this.timer);
163
+ this.timer = null;
164
+ }
165
+ await this.flush();
166
+ }
167
+ getBufferSize() {
168
+ return this.buffer.length;
169
+ }
170
+ async getBacklogSize() {
171
+ return this.backlog.size();
172
+ }
173
+ }
174
+ exports.UsageBuffer = UsageBuffer;
175
+ //# sourceMappingURL=usage-buffer.js.map
@@ -0,0 +1,22 @@
1
+ import { DegradationLevel, EffectiveLevel, OrganizationSnapshot } from '../types';
2
+ /** The five levels in their strict order; each blocks what the one before it blocks, and more. */
3
+ export declare const DEGRADATION_LEVELS: readonly DegradationLevel[];
4
+ /** What a replica past its `valid_until` is tightened to, at least. */
5
+ export declare const EXPIRED_FLOOR: DegradationLevel;
6
+ /**
7
+ * The level an organization entry carries. Anything that is not one of the five values (missing, another
8
+ * string, not a string) is read as RESTRICT_USAGE: a level this client does not know is never read as looser.
9
+ */
10
+ export declare function levelOf(entry: OrganizationSnapshot): DegradationLevel;
11
+ /** `valid_until` in epoch milliseconds, or undefined when missing or unparsable (read as already expired). */
12
+ export declare function validUntilOf(entry: OrganizationSnapshot): number | undefined;
13
+ /**
14
+ * Combines an entry with what the client observes:
15
+ *
16
+ * - answered (online, or a pull of this organization just succeeded): the entry's level;
17
+ * - not answered and `now < valid_until`: the entry's level, marked degraded;
18
+ * - not answered and `now >= valid_until` (or no usable `valid_until`): at least RESTRICT_USAGE, marked expired.
19
+ */
20
+ export declare function combineLevel(entry: OrganizationSnapshot, answered: boolean, now: number): EffectiveLevel;
21
+ /** True when two effective levels are the same triple. */
22
+ export declare function sameLevel(a: EffectiveLevel | undefined, b: EffectiveLevel | undefined): boolean;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EXPIRED_FLOOR = exports.DEGRADATION_LEVELS = void 0;
4
+ exports.levelOf = levelOf;
5
+ exports.validUntilOf = validUntilOf;
6
+ exports.combineLevel = combineLevel;
7
+ exports.sameLevel = sameLevel;
8
+ /** The five levels in their strict order; each blocks what the one before it blocks, and more. */
9
+ exports.DEGRADATION_LEVELS = [
10
+ 'NONE',
11
+ 'RESTRICT_NEW',
12
+ 'RESTRICT_USAGE',
13
+ 'RESTRICT_READ',
14
+ 'SUSPENDED',
15
+ ];
16
+ /** What a replica past its `valid_until` is tightened to, at least. */
17
+ exports.EXPIRED_FLOOR = 'RESTRICT_USAGE';
18
+ function rank(level) {
19
+ return exports.DEGRADATION_LEVELS.indexOf(level);
20
+ }
21
+ /**
22
+ * The level an organization entry carries. Anything that is not one of the five values (missing, another
23
+ * string, not a string) is read as RESTRICT_USAGE: a level this client does not know is never read as looser.
24
+ */
25
+ function levelOf(entry) {
26
+ const raw = entry.degradation_level;
27
+ return typeof raw === 'string' && exports.DEGRADATION_LEVELS.includes(raw)
28
+ ? raw
29
+ : exports.EXPIRED_FLOOR;
30
+ }
31
+ /** `valid_until` in epoch milliseconds, or undefined when missing or unparsable (read as already expired). */
32
+ function validUntilOf(entry) {
33
+ const raw = entry.valid_until;
34
+ if (typeof raw !== 'string')
35
+ return undefined;
36
+ const at = Date.parse(raw);
37
+ return Number.isFinite(at) ? at : undefined;
38
+ }
39
+ /**
40
+ * Combines an entry with what the client observes:
41
+ *
42
+ * - answered (online, or a pull of this organization just succeeded): the entry's level;
43
+ * - not answered and `now < valid_until`: the entry's level, marked degraded;
44
+ * - not answered and `now >= valid_until` (or no usable `valid_until`): at least RESTRICT_USAGE, marked expired.
45
+ */
46
+ function combineLevel(entry, answered, now) {
47
+ const level = levelOf(entry);
48
+ if (answered) {
49
+ return { level, source: 'answer', expired: false };
50
+ }
51
+ const validUntil = validUntilOf(entry);
52
+ if (validUntil !== undefined && now < validUntil) {
53
+ return { level, source: 'degraded', expired: false };
54
+ }
55
+ return {
56
+ level: rank(level) >= rank(exports.EXPIRED_FLOOR) ? level : exports.EXPIRED_FLOOR,
57
+ source: 'degraded',
58
+ expired: true,
59
+ };
60
+ }
61
+ /** True when two effective levels are the same triple. */
62
+ function sameLevel(a, b) {
63
+ if (!a || !b)
64
+ return a === b;
65
+ return a.level === b.level && a.source === b.source && a.expired === b.expired;
66
+ }
67
+ //# sourceMappingURL=degradation.js.map
@@ -0,0 +1,61 @@
1
+ import { OrganizationSnapshot, Snapshot, SnapshotItem } from '../types';
2
+ export declare const MAX_SUPPORTED_SCHEMA_VERSION = 1;
3
+ /**
4
+ * In-memory replica of the application-level entitlement snapshot served by
5
+ * `GET /sdk/v1/entitlements/snapshot`, indexed by organization and capability.
6
+ */
7
+ export declare class EntitlementMirror {
8
+ private readonly onChange?;
9
+ private readonly organizations;
10
+ /** When each organization's entry was received (local clock). */
11
+ private readonly receivedAt;
12
+ private currentSchemaVersion;
13
+ private projectId;
14
+ private lastUpdatedAt;
15
+ /**
16
+ * @param onChange called after the mirror changed: with the organizations whose entry was replaced or
17
+ * removed, or with `null` when the whole mirror was replaced.
18
+ */
19
+ constructor(onChange?: ((organizationIds: string[] | null) => void) | undefined);
20
+ /**
21
+ * Replaces the mirror with an application-level snapshot envelope.
22
+ * If schema_version is higher than supported, the update is rejected and the existing mirror is retained.
23
+ */
24
+ loadSnapshot(snapshot: Snapshot): boolean;
25
+ /**
26
+ * Merges the organizations of a (typically single-organization) snapshot envelope into the mirror,
27
+ * leaving every other organization untouched. Used to refill a mirror miss.
28
+ */
29
+ mergeSnapshot(snapshot: Snapshot): boolean;
30
+ /**
31
+ * Removes an organization, for a refill whose envelope no longer holds it (unsubscribed or deleted).
32
+ */
33
+ removeOrganization(organizationId: string): void;
34
+ /** The entry of one organization, or `undefined` when the mirror does not hold it. */
35
+ getOrganization(organizationId: string): OrganizationSnapshot | undefined;
36
+ /** When the entry of one organization was received (local clock), or `undefined`. */
37
+ getReceivedAt(organizationId: string): number | undefined;
38
+ /** The organizations the mirror holds. */
39
+ organizationIds(): string[];
40
+ /**
41
+ * Revision of one organization, or `undefined` when the mirror does not hold it.
42
+ */
43
+ getOrganizationRevision(organizationId: string): number | undefined;
44
+ /**
45
+ * Looks up the snapshot item of a capability for an organization.
46
+ */
47
+ getItem(organizationId: string, capabilityKey: string): SnapshotItem | undefined;
48
+ /**
49
+ * Checks whether the organization exists in the mirror.
50
+ */
51
+ hasOrganization(organizationId: string): boolean;
52
+ /**
53
+ * Highest organization revision in the mirror (0 when empty).
54
+ */
55
+ getRevision(): number;
56
+ getSchemaVersion(): number;
57
+ getProjectId(): string;
58
+ getLastUpdatedAt(): number;
59
+ isEmpty(): boolean;
60
+ private acceptsEnvelope;
61
+ }