slotlock 0.6.2 → 0.6.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/CHANGELOG.md +25 -0
- package/README.md +13 -6
- package/agent-server.js +1 -5
- package/agent-store-backend.d.ts +5 -6
- package/agent-store-backend.js +41 -67
- package/api-keys.js +1 -1
- package/application-api.d.ts +3 -3
- package/application-api.js +16 -24
- package/assistant.d.ts +18 -27
- package/assistant.js +36 -53
- package/booking-rules.d.ts +30 -41
- package/booking-rules.js +17 -25
- package/change-watch.d.ts +1 -0
- package/change-watch.js +1 -1
- package/cli.js +3 -4
- package/config.d.ts +17 -2
- package/config.js +44 -6
- package/dashboard.js +13 -0
- package/ddl.d.ts +20 -27
- package/ddl.js +166 -108
- package/engine.d.ts +6 -9
- package/engine.js +5 -9
- package/event-feed.d.ts +23 -31
- package/event-feed.js +30 -45
- package/event-schema.js +6 -9
- package/hooks.d.ts +2 -4
- package/hooks.js +2 -4
- package/ical-worker-thread.js +3 -4
- package/ical-worker.d.ts +8 -10
- package/ical-worker.js +7 -9
- package/interop.js +1 -4
- package/maintenance.d.ts +14 -10
- package/maintenance.js +43 -26
- package/mcp-app.d.ts +7 -9
- package/mcp-app.js +7 -9
- package/mcp-modern.d.ts +14 -16
- package/mcp-modern.js +23 -27
- package/metrics.js +0 -1
- package/node-server.d.ts +9 -9
- package/node-server.js +25 -34
- package/package-version.js +2 -4
- package/package.json +1 -1
- package/rate-limit.d.ts +6 -1
- package/rate-limit.js +24 -8
- package/rest-api.d.ts +13 -14
- package/rest-api.js +11 -12
- package/rules.d.ts +6 -7
- package/rules.js +30 -42
- package/self-host.d.ts +16 -2
- package/self-host.js +42 -21
- package/slotlock.manifest.json +1 -1
- package/store.d.ts +109 -161
- package/store.js +253 -371
- package/sync.d.ts +44 -59
- package/sync.js +325 -469
- package/timezone.d.ts +25 -33
- package/timezone.js +37 -52
- package/types.d.ts +35 -56
- package/webhooks.d.ts +22 -25
- package/webhooks.js +35 -40
- package/weekly-hours.d.ts +3 -0
- package/weekly-hours.js +1 -1
package/store.d.ts
CHANGED
|
@@ -5,15 +5,13 @@ import { SLOTLOCK_CALENDAR_HORIZON_DAYS } from './rules.js';
|
|
|
5
5
|
import type { AcquireHoldResult, CalendarCoverageCommand, CalendarCoverageResult, CalendarEventContent, CalendarEventRetentionResult, BookingRetentionResult, SlotlockTenantErasure, SlotlockBookingRuleDefaults, CalendarFreeBusy, CalendarHorizonRollCursor, CalendarHorizonRollResult, CancelCalendarEventResult, CancelExternalReservationResult, ConfirmHoldResult, CreateReservationResult, DeleteResourceResult, ExternalReservationCommand, ExternalReservationResult, Interval, SlotlockCalendarEvent, SlotlockAgendaEntry, SlotlockHold, SlotlockReservation, SlotlockResource, PutCalendarEventCommand, PutCalendarEventResult, ReleaseHoldResult, WeeklyAvailabilityRule } from './types.js';
|
|
6
6
|
export { SLOTLOCK_CALENDAR_HORIZON_DAYS };
|
|
7
7
|
/**
|
|
8
|
-
* Longest one-off event
|
|
9
|
-
*
|
|
10
|
-
* series is bounded by its 367-day materialization window instead.
|
|
8
|
+
* Longest one-off event (ten years and some leap days); a recurring series is bounded by its
|
|
9
|
+
* materialization window instead.
|
|
11
10
|
*/
|
|
12
11
|
export declare const SLOTLOCK_MAX_EVENT_DURATION_DAYS = 3660;
|
|
13
12
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* past its end, so that a search of the full 367 days from any moment of the day is complete.
|
|
13
|
+
* The 367-day search window plus two days: the rolling horizon starts a day before the current UTC
|
|
14
|
+
* day and must reach 367 days past its end.
|
|
17
15
|
*/
|
|
18
16
|
export declare const SLOTLOCK_MATERIALIZATION_WINDOW_DAYS: number;
|
|
19
17
|
/** Trusted provider/maintenance namespace used by backwards-compatible store callers. */
|
|
@@ -38,8 +36,8 @@ export declare const SLOTLOCK_EVENT_COMMAND_RETENTION_DAYS = 30;
|
|
|
38
36
|
/** Store-owned coverage source for bounded, locally materialized recurring events. */
|
|
39
37
|
export declare const SLOTLOCK_LOCAL_RECURRENCE_COVERAGE_SOURCE = "slotlock:local-recurrence-materialization";
|
|
40
38
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* From a day before the current UTC day to SLOTLOCK_MATERIALIZATION_WINDOW_DAYS past its end;
|
|
40
|
+
* shared by the writer, maintenance and readiness.
|
|
43
41
|
*/
|
|
44
42
|
export declare function calendarEventRollingHorizon(now?: Date): Interval;
|
|
45
43
|
/** What `createSlotlockStore` accepts: a `postgres` client, or a transaction/savepoint from one. */
|
|
@@ -48,9 +46,9 @@ type StoreSql = SlotlockSql;
|
|
|
48
46
|
/** How a migration (applySchema, applyTenantRls, grantApplicationRole) waits and how long it runs. */
|
|
49
47
|
export interface SlotlockMigrationOptions {
|
|
50
48
|
/**
|
|
51
|
-
* The longest it waits for any one lock, in milliseconds (1-600,000; default 1,000).
|
|
52
|
-
*
|
|
53
|
-
*
|
|
49
|
+
* The longest it waits for any one lock, in milliseconds (1-600,000; default 1,000). Queries on a
|
|
50
|
+
* table queue behind a migration waiting for its lock, so past this it gives up, lets them
|
|
51
|
+
* through, and retries.
|
|
54
52
|
*/
|
|
55
53
|
lockTimeoutMs?: number;
|
|
56
54
|
/** The longest any one of its statements runs, in milliseconds (1-86,400,000; default 900,000). */
|
|
@@ -59,21 +57,18 @@ export interface SlotlockMigrationOptions {
|
|
|
59
57
|
attempts?: number;
|
|
60
58
|
}
|
|
61
59
|
/**
|
|
62
|
-
* The schema version
|
|
63
|
-
*
|
|
64
|
-
* `slotlock migrate`, which grants it).
|
|
60
|
+
* The recorded schema version, or `null` when this role cannot read one: no schema, 0.2 or earlier,
|
|
61
|
+
* or the function not yet granted (`slotlock migrate` grants it).
|
|
65
62
|
*/
|
|
66
63
|
export declare function readSlotlockSchemaVersion(sql: SlotlockSql): Promise<number | null>;
|
|
67
64
|
/**
|
|
68
|
-
* Refuse
|
|
69
|
-
*
|
|
70
|
-
* migrated it, whose schema this release's code does not know. Both carry `installed` and `expected`.
|
|
65
|
+
* Refuse a schema another release migrated: `schema_outdated` (older or unversioned: run `slotlock
|
|
66
|
+
* migrate`) or `schema_newer`. Both carry `installed` and `expected`.
|
|
71
67
|
*/
|
|
72
68
|
export declare function assertSlotlockSchemaCurrent(sql: SlotlockSql): Promise<void>;
|
|
73
69
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* midnight: with it in the hash, a retry after midnight was refused as a different request.
|
|
70
|
+
* A put's identity and the event it asks for, not its storage window: a series' window moves with
|
|
71
|
+
* the UTC day, so a retry after midnight must still match.
|
|
77
72
|
*/
|
|
78
73
|
declare function putCommandHash(params: {
|
|
79
74
|
command: PutCalendarEventCommand;
|
|
@@ -81,9 +76,8 @@ declare function putCommandHash(params: {
|
|
|
81
76
|
event: CalendarEventContent;
|
|
82
77
|
}): string;
|
|
83
78
|
/**
|
|
84
|
-
* The hash 0.3.0 and earlier stored
|
|
85
|
-
*
|
|
86
|
-
* for a series. Remove once no stored command can be that old (commands are pruned after 30 days).
|
|
79
|
+
* The hash 0.3.0 and earlier stored (window included), so an older stored command still replays.
|
|
80
|
+
* Remove once no stored command can be that old (pruned after 30 days).
|
|
87
81
|
*/
|
|
88
82
|
export declare function legacyPutCommandHash(params: Parameters<typeof putCommandHash>[0] & {
|
|
89
83
|
window: Interval;
|
|
@@ -95,52 +89,38 @@ export declare const SLOTLOCK_MAX_POOL_RULE_DEFAULTS = 1000;
|
|
|
95
89
|
export declare function isPoolName(value: unknown): value is string;
|
|
96
90
|
export interface SlotlockStore extends SlotlockEventFeed {
|
|
97
91
|
/**
|
|
98
|
-
* Apply the Slotlock DDL (idempotent).
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* role
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* already migrated, unchanged since, is left as it is without a lock on any table. Constraints an
|
|
106
|
-
* upgrade adds NOT VALID are validated in a second transaction, whose locks no serving read or
|
|
107
|
-
* write waits for (one inside a caller's transaction shares its locks until it ends); one that old
|
|
108
|
-
* rows still break stays NOT VALID, and each later migration validates it again. Each lock it
|
|
109
|
-
* waits for is bounded by `lockTimeoutMs`, after which it tries again (`schema_lock_timeout` once
|
|
92
|
+
* Apply the Slotlock DDL (idempotent). Refuses (`unsafe_slotlock_schema`, with `reasons`) a
|
|
93
|
+
* `slotlock` schema that another role (superusers aside) owns, can create objects in, owns an
|
|
94
|
+
* object in, or can add triggers to; ownership through a group role counts, so deploy as the
|
|
95
|
+
* owning role itself (`SET ROLE`). Refuses (`schema_newer`) a schema a newer release migrated. An
|
|
96
|
+
* unchanged schema is left without locking any table. Constraints added NOT VALID are validated
|
|
97
|
+
* in a second transaction; one old rows still break stays NOT VALID and is retried on each
|
|
98
|
+
* migration. Each lock wait is bounded by `lockTimeoutMs` and retried (`schema_lock_timeout` once
|
|
110
99
|
* out of attempts).
|
|
111
100
|
*/
|
|
112
101
|
applySchema(options?: SlotlockMigrationOptions): Promise<void>;
|
|
113
102
|
/**
|
|
114
|
-
* Atomically enable forced tenant RLS using
|
|
115
|
-
*
|
|
116
|
-
* installs, bound to this setting, are left as they are without a lock on any table.
|
|
103
|
+
* Atomically enable forced tenant RLS using the store's context setting. Refuses the same schemas
|
|
104
|
+
* as `applySchema()`. Tables already set up exactly so are left without a lock.
|
|
117
105
|
*/
|
|
118
106
|
applyTenantRls(options?: {
|
|
119
107
|
allowRebind?: boolean;
|
|
120
108
|
} & SlotlockMigrationOptions): Promise<void>;
|
|
121
109
|
/**
|
|
122
110
|
* Grant an existing application role exactly what the store needs at runtime: USAGE on the
|
|
123
|
-
* `slotlock` schema and DML on its tables. Run as the deployment role after every
|
|
124
|
-
* Refuses (`unsafe_application_role`, with
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* ownership of the database, of anything in it (its temporary objects and its own default
|
|
128
|
-
* privileges aside) or of `btree_gist`, CREATE on the database or on any schema, TRUNCATE,
|
|
129
|
-
* REFERENCES or TRIGGER on a Slotlock table (or by default on new ones), or a right on a server
|
|
130
|
-
* setting. The check runs when the grant is made, so a right added later is not
|
|
131
|
-
* detected until it runs again, and it is not exhaustive (SECURITY.md lists what it does not read).
|
|
111
|
+
* `slotlock` schema and DML on its tables. Run as the deployment role after every
|
|
112
|
+
* `applySchema()`. Refuses (`unsafe_application_role`, with `reasons`) a role with a known way
|
|
113
|
+
* around forced RLS or the exclusion arbiter, directly, by membership or through PUBLIC
|
|
114
|
+
* (SECURITY.md lists the checks and what they do not cover). The check runs only at grant time.
|
|
132
115
|
*/
|
|
133
116
|
grantApplicationRole(role: string, options?: SlotlockMigrationOptions): Promise<void>;
|
|
134
117
|
/**
|
|
135
118
|
* Run store operations with one transaction-local tenant context on the same connection. On a
|
|
136
|
-
* client it opens a READ COMMITTED transaction
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* (grantApplicationRole grants it), and on a schema without it with `schema_outdated`. With the
|
|
142
|
-
* store's `maxTenantTransactions`, an aborted `signal` takes a call still waiting for its turn out
|
|
143
|
-
* of the line, refused with the signal's reason.
|
|
119
|
+
* client it opens a READ COMMITTED transaction. Inside a caller's transaction of another level,
|
|
120
|
+
* writes are refused (`invalid_transaction_isolation`), since that snapshot can miss a
|
|
121
|
+
* suspension. Needs EXECUTE on slotlock.tenant_suspended(text) (`application_role_not_granted`
|
|
122
|
+
* without it; grantApplicationRole grants it). With `maxTenantTransactions`, an aborted `signal`
|
|
123
|
+
* removes a waiting call from the line.
|
|
144
124
|
*/
|
|
145
125
|
withTenant<T>(tenantRef: string, callback: (store: SlotlockTenantStore) => Promise<T>, options?: {
|
|
146
126
|
isolation?: 'read committed';
|
|
@@ -162,14 +142,11 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
162
142
|
* INVARIANT (erasure completeness): a tenant integration must use a stable, erasable business
|
|
163
143
|
* identifier for externalRef and include this schema in its own data-erasure workflow.
|
|
164
144
|
*
|
|
165
|
-
* With `maxTenantResources` (1-100,000
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* one itself, and inside a caller's transaction of another level it refuses
|
|
171
|
-
* (`invalid_transaction_isolation`). Uncapped creates do not wait for the lock and are not
|
|
172
|
-
* counted against a cap until they commit.
|
|
145
|
+
* With `maxTenantResources` (1-100,000) or the store's `resources` limit (the lower applies), a
|
|
146
|
+
* new resource is refused (`resource_limit_reached`) once the tenant has that many; an existing
|
|
147
|
+
* `externalRef` is still updated. Capped creates serialize on a database lock, so racing servers
|
|
148
|
+
* stop at the cap; this needs READ COMMITTED (`invalid_transaction_isolation` inside another
|
|
149
|
+
* level).
|
|
173
150
|
*/
|
|
174
151
|
createResource(params: {
|
|
175
152
|
externalRef?: string;
|
|
@@ -298,13 +275,11 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
298
275
|
includeArchived?: boolean;
|
|
299
276
|
}): Promise<SlotlockResource[]>;
|
|
300
277
|
/**
|
|
301
|
-
* Insert a confirmed reservation with the EXCLUDE constraint as the overlap arbiter
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* `tenantRef`, only a resource of that tenant (`resource_not_found` otherwise, whatever the
|
|
307
|
-
* connection's row-level security).
|
|
278
|
+
* Insert a confirmed reservation with the EXCLUDE constraint as the overlap arbiter; expected
|
|
279
|
+
* outcomes (invalid window, lost race) come back structured. Expired holds overlapping the window
|
|
280
|
+
* are reaped in the same transaction, so they never block. On a tenant's resource the booking
|
|
281
|
+
* takes the tenant and its own id as reference. With `tenantRef`, only that tenant's resource
|
|
282
|
+
* (`resource_not_found` otherwise, whatever the connection's RLS).
|
|
308
283
|
*/
|
|
309
284
|
createReservation(params: {
|
|
310
285
|
resourceId: string;
|
|
@@ -328,11 +303,9 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
328
303
|
revision: number;
|
|
329
304
|
}): Promise<CancelExternalReservationResult>;
|
|
330
305
|
/**
|
|
331
|
-
* Occupy a window
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
* With `tenantRef`, only a resource of that tenant (`resource_not_found` otherwise, whatever the
|
|
335
|
-
* connection's row-level security).
|
|
306
|
+
* Occupy a window without committing: the hold blocks rivals under the same EXCLUDE arbiter until
|
|
307
|
+
* `expiresAt` (database clock: statement time + ttl), then frees via lazy reap. With `tenantRef`,
|
|
308
|
+
* only that tenant's resource (`resource_not_found` otherwise, whatever the connection's RLS).
|
|
336
309
|
*/
|
|
337
310
|
acquireHold(params: {
|
|
338
311
|
resourceId: string;
|
|
@@ -344,21 +317,18 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
344
317
|
/** Turnaround kept busy after `end`, 0 to 30 days; the confirmed booking keeps it. */
|
|
345
318
|
bufferAfterMs?: number;
|
|
346
319
|
/**
|
|
347
|
-
* A retry identity, 1-500 UTF-8 bytes, unique
|
|
348
|
-
* required
|
|
349
|
-
*
|
|
350
|
-
* kept until the hold would have expired, released or confirmed; a retry after that makes a new
|
|
351
|
-
* hold.
|
|
320
|
+
* A retry identity, 1-500 UTF-8 bytes, unique within `tenantRef` and `principalRef` (both
|
|
321
|
+
* required): the same key and body answer the first hold (`idempotent: true`); another body is
|
|
322
|
+
* `idempotency_conflict`. Kept until the hold would have expired, released or confirmed.
|
|
352
323
|
*/
|
|
353
324
|
idempotencyKey?: string;
|
|
354
325
|
/** Whose keys `idempotencyKey` is among, 1-500 UTF-8 bytes (an API key's subject, say). */
|
|
355
326
|
principalRef?: string;
|
|
356
327
|
/**
|
|
357
|
-
* Who the hold belongs to, 1-500 UTF-8 bytes,
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
* holder and no cap: an application holding for its customers.
|
|
328
|
+
* Who the hold belongs to, 1-500 UTF-8 bytes, within `tenantRef` (required): `releaseHold` and
|
|
329
|
+
* `fromHold` with a `holderRef` take only their own, and a holder may have `maxHoldsPerHolder`
|
|
330
|
+
* live at once (and `maxHeldHoursPerHolder` together, when set). Without it the hold has no
|
|
331
|
+
* holder and no cap.
|
|
362
332
|
*/
|
|
363
333
|
holderRef?: string;
|
|
364
334
|
/**
|
|
@@ -416,12 +386,10 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
416
386
|
};
|
|
417
387
|
}): Promise<SlotlockReservation[]>;
|
|
418
388
|
/**
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
* hold on one of that tenant's resources counts: another tenant's is `hold_not_found` whatever
|
|
424
|
-
* the connection's row-level security.
|
|
389
|
+
* Turn an unexpired hold into a confirmed booking. On a tenant's resource the booking takes the
|
|
390
|
+
* tenant and the hold's id as reference at revision 1 (`reservation_cancelled` when a cancel of
|
|
391
|
+
* that reference came first). Confirming again answers the booking as it now is. With
|
|
392
|
+
* `tenantRef`, another tenant's hold is `hold_not_found` whatever the connection's RLS.
|
|
425
393
|
*/
|
|
426
394
|
confirmHold(holdId: string, options?: SlotlockHoldTenantOptions): Promise<ConfirmHoldResult>;
|
|
427
395
|
/**
|
|
@@ -511,12 +479,10 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
511
479
|
expectedResourceId?: string;
|
|
512
480
|
}): Promise<CancelCalendarEventResult>;
|
|
513
481
|
/**
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
* window
|
|
518
|
-
* too far past it for the 367-day cap from the window's start, is not due: it is left as it is
|
|
519
|
-
* and not counted.
|
|
482
|
+
* Rematerialize due recurring masters into one rolling window without changing their revision. A
|
|
483
|
+
* collision rolls back that master and keeps its old coverage. A master is rolled from the
|
|
484
|
+
* window's start through the later of its stored end and the window's; one already covering the
|
|
485
|
+
* window, or too far past it for the 367-day cap, is not due and not counted.
|
|
520
486
|
*/
|
|
521
487
|
rollCalendarEventHorizon(params: {
|
|
522
488
|
tenantRef: string;
|
|
@@ -526,12 +492,10 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
526
492
|
after?: CalendarHorizonRollCursor;
|
|
527
493
|
}): Promise<CalendarHorizonRollResult>;
|
|
528
494
|
/**
|
|
529
|
-
* Delete one bounded batch of expired event bookkeeping
|
|
530
|
-
*
|
|
531
|
-
*
|
|
532
|
-
*
|
|
533
|
-
* waited on. After the window a retried command is evaluated afresh under its expected revision,
|
|
534
|
-
* and a pruned agent identity can be created again. Call until `hasMore` is false.
|
|
495
|
+
* Delete one bounded batch of expired event bookkeeping, which returns agent quota: commands
|
|
496
|
+
* older than the replay window (every owner), then agent-owned tombstones older than it with no
|
|
497
|
+
* remaining command. Active events and `internal` tombstones are never deleted. Locked rows are
|
|
498
|
+
* skipped. Afterwards a retried command is evaluated afresh. Call until `hasMore` is false.
|
|
535
499
|
*/
|
|
536
500
|
pruneCalendarEventRetention(params: {
|
|
537
501
|
tenantRef: string;
|
|
@@ -541,21 +505,17 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
541
505
|
retentionDays?: number;
|
|
542
506
|
}): Promise<CalendarEventRetentionResult>;
|
|
543
507
|
/**
|
|
544
|
-
* Delete one bounded batch of a tenant's dead booking rows: holds
|
|
545
|
-
* answers
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
* tenant the tenant and its own id as reference, as the migration does. Call until `hasMore` is
|
|
550
|
-
* false.
|
|
508
|
+
* Delete one bounded batch of a tenant's dead booking rows: expired holds and their keyed
|
|
509
|
+
* answers, and references retired with no booking past retention. A cancelled booking's tombstone
|
|
510
|
+
* is only deleted by erasure, so its reference is never booked again. Locked rows are skipped.
|
|
511
|
+
* Also gives a confirmed booking a 0.2 server wrote without a tenant its tenant and reference, as
|
|
512
|
+
* the migration does. Call until `hasMore` is false.
|
|
551
513
|
*/
|
|
552
514
|
/**
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
* unless that overlaps another booking: then it stays and `event.timezone_conflict` reports it,
|
|
558
|
-
* for a person or an agent to decide. Up to `limit` (1-1,000, default 100) events a call.
|
|
515
|
+
* Find future one-off events whose wall-clock time now falls at another instant (new tzdata),
|
|
516
|
+
* each reported once (`event.timezone_shifted`). With `move`, it is moved (a new revision,
|
|
517
|
+
* `event.updated` by `slotlock:tzdata`) unless that overlaps another booking, which
|
|
518
|
+
* `event.timezone_conflict` reports instead. Up to `limit` (1-1,000, default 100) a call.
|
|
559
519
|
*/
|
|
560
520
|
followZoneRules(params: {
|
|
561
521
|
tenantRef: string;
|
|
@@ -740,12 +700,11 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
740
700
|
limit?: number;
|
|
741
701
|
}): Promise<number>;
|
|
742
702
|
/**
|
|
743
|
-
* Offer freed time to the waitlist
|
|
744
|
-
*
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
* `next` back as `after` for the next, until it is null.
|
|
703
|
+
* Offer freed time to the waitlist in join order: each waiting entry whose window is free (on its
|
|
704
|
+
* resource, or the first free pool member) gets a hold for its holder (`waitlist.offered`).
|
|
705
|
+
* Started windows expire (`waitlist.expired`); finished entries past retention are deleted. One
|
|
706
|
+
* tenant's offers run one at a time. Pages of `limit` (1-100, default 50): pass `next` back as
|
|
707
|
+
* `after` until null.
|
|
749
708
|
*/
|
|
750
709
|
offerWaitlist(params: {
|
|
751
710
|
tenantRef: string;
|
|
@@ -773,11 +732,10 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
773
732
|
retentionDays?: number;
|
|
774
733
|
}): Promise<BookingRetentionResult>;
|
|
775
734
|
/**
|
|
776
|
-
* Suspend a tenant: from when this returns,
|
|
777
|
-
*
|
|
778
|
-
*
|
|
779
|
-
*
|
|
780
|
-
* schema, may suspend (`tenant_suspension_not_permitted` for another).
|
|
735
|
+
* Suspend a tenant: from when this returns, its withTenant transactions may read but writes are
|
|
736
|
+
* refused (`tenant_suspended`); one in flight is waited for. Its keys still authenticate but it
|
|
737
|
+
* gets no new key. Maintenance and erasure go on. True when it was not suspended already.
|
|
738
|
+
* Deployment role only (`tenant_suspension_not_permitted`).
|
|
781
739
|
*/
|
|
782
740
|
suspendTenant(params: {
|
|
783
741
|
tenantRef: string;
|
|
@@ -797,11 +755,9 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
797
755
|
tenantRef: string;
|
|
798
756
|
}): Promise<boolean>;
|
|
799
757
|
/**
|
|
800
|
-
* Erase a tenant: delete every row it owns
|
|
801
|
-
*
|
|
802
|
-
*
|
|
803
|
-
* digests are retired so no erased key works again; a suspension stays. Erasing again deletes
|
|
804
|
-
* nothing.
|
|
758
|
+
* Erase a tenant: delete every row it owns in one transaction, waiting for its transactions in
|
|
759
|
+
* flight. API key digests are retired so no erased key works again; a suspension stays. Erasing
|
|
760
|
+
* again deletes nothing.
|
|
805
761
|
*/
|
|
806
762
|
eraseTenant(params: {
|
|
807
763
|
tenantRef: string;
|
|
@@ -817,12 +773,11 @@ export interface SlotlockStore extends SlotlockEventFeed {
|
|
|
817
773
|
coverageMaxAgeMs?: number;
|
|
818
774
|
}): Promise<CalendarFreeBusy>;
|
|
819
775
|
/**
|
|
820
|
-
* The first slot of `durationMs` inside the hours
|
|
821
|
-
* getFreeBusy × expandRules
|
|
822
|
-
*
|
|
823
|
-
*
|
|
824
|
-
*
|
|
825
|
-
* issues is `resource_not_found`. A search window longer than 367 days is `invalid_window`.
|
|
776
|
+
* The first slot of `durationMs` inside the hours whose time and turnaround buffer are free
|
|
777
|
+
* (getFreeBusy × expandRules in the resource's timezone → findNextAvailable). Null when no slot
|
|
778
|
+
* is free or one cannot be proven free (coverage incomplete: a series not stored that far, or a
|
|
779
|
+
* required provider not read that far). Unknown or another tenant's resource is
|
|
780
|
+
* `resource_not_found`; a window over 367 days is `invalid_window`.
|
|
826
781
|
*/
|
|
827
782
|
findNextAvailableFor(params: {
|
|
828
783
|
/** The resource's tenant; `null` for a resource that has none (the legacy namespace). */
|
|
@@ -926,20 +881,16 @@ export interface SlotlockStoreOptions {
|
|
|
926
881
|
/** Hard retained-command ceiling per non-internal owner. Defaults to 10,000; cannot be disabled. */
|
|
927
882
|
agentOwnerCommandQuota?: number;
|
|
928
883
|
/**
|
|
929
|
-
* What one tenant may store, for a database
|
|
930
|
-
*
|
|
931
|
-
*
|
|
932
|
-
*
|
|
933
|
-
* only (`invalid_transaction_isolation` otherwise). Bookings and events count while they are still
|
|
934
|
-
* to come, and for 30 days after they were made, whatever time they hold; history older than that
|
|
935
|
-
* does not count, and a count stops reading at the limit.
|
|
884
|
+
* What one tenant may store, for a shared database (none by default). A write that would pass a
|
|
885
|
+
* limit is refused (`*_limit_reached`); existing rows can still be revised, cancelled and
|
|
886
|
+
* released. Counted under a per-tenant lock in READ COMMITTED transactions only. Bookings and
|
|
887
|
+
* events count while still to come and for 30 days after they were made.
|
|
936
888
|
*/
|
|
937
889
|
tenantLimits?: SlotlockTenantLimits;
|
|
938
890
|
/**
|
|
939
|
-
* How many withTenant transactions of one tenant run at once on this store (1-1,000;
|
|
940
|
-
* default). The rest wait in
|
|
941
|
-
*
|
|
942
|
-
* transaction, which holds its connection already.
|
|
891
|
+
* How many withTenant transactions of one tenant run at once on this store (1-1,000; unlimited by
|
|
892
|
+
* default). The rest wait in order, so one tenant waiting on its own locks holds at most this
|
|
893
|
+
* many pool connections. Not counted inside a caller's transaction.
|
|
943
894
|
*/
|
|
944
895
|
maxTenantTransactions?: number;
|
|
945
896
|
/**
|
|
@@ -949,11 +900,9 @@ export interface SlotlockStoreOptions {
|
|
|
949
900
|
*/
|
|
950
901
|
tenantLockTimeoutMs?: number;
|
|
951
902
|
/**
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
*
|
|
955
|
-
* answered as the first try was). Maintenance prunes what is older. A cursor before a change any
|
|
956
|
-
* store has pruned answers `cursor_expired`, so stores set differently never skip a change.
|
|
903
|
+
* Days history is kept, 1-3,650 (default 30): the change feed (and cursor reach) and ended
|
|
904
|
+
* waitlist entries; and, when longer than 30 days, retired idempotency answers, booking
|
|
905
|
+
* references and past events' commands. A cursor before a pruned change answers `cursor_expired`.
|
|
957
906
|
*/
|
|
958
907
|
retentionDays?: number;
|
|
959
908
|
/**
|
|
@@ -972,10 +921,9 @@ export interface SlotlockTenantLimits {
|
|
|
972
921
|
/** Resources, archived ones included; the lower of this and a call's `maxTenantResources`. */
|
|
973
922
|
resources?: number;
|
|
974
923
|
/**
|
|
975
|
-
* Holds
|
|
976
|
-
*
|
|
977
|
-
*
|
|
978
|
-
* answers, as maintenance does, so only live holds keep a new one out.
|
|
924
|
+
* Holds, expired ones included until deleted; a keyed hold keeps its place while its answer is
|
|
925
|
+
* kept. At the limit, expired holds and their answers are deleted first, so only live holds keep
|
|
926
|
+
* a new one out.
|
|
979
927
|
*/
|
|
980
928
|
holds?: number;
|
|
981
929
|
/**
|
|
@@ -984,9 +932,9 @@ export interface SlotlockTenantLimits {
|
|
|
984
932
|
*/
|
|
985
933
|
reservations?: number;
|
|
986
934
|
/**
|
|
987
|
-
*
|
|
988
|
-
*
|
|
989
|
-
*
|
|
935
|
+
* Events of every owner still to come (a series until known to have ended) or made in the last 30
|
|
936
|
+
* days, cancellations made in the last 30 days, and agent cancellations until pruned. Each event
|
|
937
|
+
* stores at most 2,000 occurrences.
|
|
990
938
|
*/
|
|
991
939
|
events?: number;
|
|
992
940
|
/** Provider coverage rows: one per resource and source. */
|