slotlock 0.2.0 → 0.3.0

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 (51) hide show
  1. package/CHANGELOG.md +330 -0
  2. package/CONTRIBUTING.md +35 -8
  3. package/GOVERNANCE.md +4 -2
  4. package/MAINTAINERS.md +3 -2
  5. package/NOTICE +4 -4
  6. package/README.md +277 -53
  7. package/SECURITY.md +31 -2
  8. package/SPEC.md +142 -15
  9. package/agent-server.d.ts +12 -3
  10. package/agent-server.js +108 -78
  11. package/agent-store-backend.d.ts +6 -0
  12. package/agent-store-backend.js +355 -36
  13. package/api-keys.d.ts +11 -3
  14. package/api-keys.js +29 -6
  15. package/application-api.d.ts +14 -1
  16. package/application-api.js +274 -12
  17. package/config.d.ts +93 -0
  18. package/config.js +192 -0
  19. package/dashboard.d.ts +6 -0
  20. package/dashboard.js +17 -1
  21. package/ddl.d.ts +45 -3
  22. package/ddl.js +538 -12
  23. package/engine.js +20 -6
  24. package/event-schema.d.ts +59 -0
  25. package/event-schema.js +85 -0
  26. package/ical-worker-thread.js +4 -2
  27. package/ical-worker.d.ts +8 -5
  28. package/ical-worker.js +9 -2
  29. package/index.d.ts +5 -5
  30. package/index.js +3 -3
  31. package/maintenance.d.ts +15 -5
  32. package/maintenance.js +59 -27
  33. package/node-server.d.ts +9 -1
  34. package/node-server.js +7 -2
  35. package/openapi.json +6024 -0
  36. package/package.json +3 -2
  37. package/rate-limit.d.ts +71 -0
  38. package/rate-limit.js +718 -0
  39. package/rest-api.js +10 -0
  40. package/rules.js +6 -3
  41. package/self-host.d.ts +22 -8
  42. package/self-host.js +307 -34
  43. package/slotlock-manifest.schema.json +6 -1
  44. package/slotlock.manifest.json +7 -2
  45. package/store.d.ts +334 -24
  46. package/store.js +2029 -290
  47. package/sync.d.ts +43 -2
  48. package/sync.js +1515 -323
  49. package/timezone.d.ts +58 -3
  50. package/timezone.js +249 -20
  51. package/types.d.ts +213 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slotlock",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Resource-first, deterministic calendar and availability engine for AI agents.",
6
6
  "license": "Apache-2.0",
@@ -21,6 +21,7 @@
21
21
  },
22
22
  "./manifest": "./slotlock.manifest.json",
23
23
  "./manifest.schema": "./slotlock-manifest.schema.json",
24
+ "./openapi.json": "./openapi.json",
24
25
  "./package.json": "./package.json"
25
26
  },
26
27
  "bin": {
@@ -58,7 +59,7 @@
58
59
  "dependencies": {
59
60
  "ical.js": "2.2.1",
60
61
  "rrule": "2.8.1",
61
- "zod": "4.6.5"
62
+ "zod": "4.6.7"
62
63
  },
63
64
  "peerDependencies": {
64
65
  "postgres": "^3.4.5"
@@ -0,0 +1,71 @@
1
+ import { type SlotlockAgentOperation, type SlotlockAgentPrincipal } from './agent-server.js';
2
+ import { type SlotlockApplicationOperation } from './application-api.js';
3
+ import type { SlotlockLimits } from './config.js';
4
+ import type { SlotlockNodeServerFetchTarget } from './node-server.js';
5
+ export type SlotlockRefusalReason = 'auth_refused' | 'rate_limited' | 'lookup_overflow' | 'series_writes_busy' | 'maintenance_busy' | 'request_too_large';
6
+ /** One `refusals` log line: counts since the last one, and the most refused clients as pseudonyms. */
7
+ export interface SlotlockRefusalCounts extends Record<SlotlockRefusalReason, number> {
8
+ window_seconds: number;
9
+ /** Each client's address keyed by a per-process secret: comparable within one run, not reversible. */
10
+ clients: {
11
+ client: string;
12
+ refusals: number;
13
+ }[];
14
+ }
15
+ export interface SlotlockRequestLimitsOptions {
16
+ limits: SlotlockLimits;
17
+ /** A lower-case request field a trusted proxy sets to the client address (config.ts). */
18
+ trustedProxyHeader?: string;
19
+ /** The public URL's path, without a trailing slash (`''` at the origin root). */
20
+ basePath: string;
21
+ /** Paths, under the base path, of the dashboard's sign-in and its callback, when it is served. */
22
+ signInPaths?: readonly string[];
23
+ /** Resolve a request's credential: an API key lookup or the server token. Called once a request. */
24
+ authenticate(request: Request): Promise<SlotlockAgentPrincipal | null>;
25
+ onRefusals?: (counts: SlotlockRefusalCounts) => void;
26
+ /**
27
+ * Called once, with the field's name, when a request carries a field a proxy puts the client's
28
+ * address in while `trustedProxyHeader` is not set: every client behind that proxy is then
29
+ * counted as the proxy's one address.
30
+ */
31
+ onIgnoredClientField?: (field: string) => void;
32
+ /** Milliseconds, for tests; defaults to `performance.now`. */
33
+ now?: () => number;
34
+ }
35
+ export interface SlotlockRequestLimits {
36
+ /** The agent server's `authenticate`: the request's lookup from `fetch`, else a lookup now. */
37
+ authenticate(request: Request): Promise<SlotlockAgentPrincipal | null>;
38
+ /** The agent server's `consumeRateLimit`: the key, tenant and server buckets of the request. */
39
+ consumeRateLimit(args: {
40
+ principal: SlotlockAgentPrincipal;
41
+ operation: SlotlockAgentOperation | 'protocol';
42
+ }): Promise<boolean>;
43
+ /**
44
+ * For `authorize`: when the operation may expand a recurring series, or runs maintenance, take its
45
+ * write token and wait for a slot, holding it until the request is answered. Throws a
46
+ * `SlotlockAgentOperationError` when none comes: `rate_limited` (429) when the write is over its
47
+ * budget or the tenant's own writes fill its slots, `server_busy` (503) while the server's do.
48
+ */
49
+ admit(args: {
50
+ principal: SlotlockAgentPrincipal;
51
+ operation: SlotlockAgentOperation | SlotlockApplicationOperation;
52
+ input: Record<string, unknown>;
53
+ }): Promise<void>;
54
+ /** Put the limits in front of `target`: the listener's fetch target. */
55
+ serve(target: SlotlockNodeServerFetchTarget): SlotlockNodeServerFetchTarget;
56
+ /** The client address a request reaching `serve`'s target was counted as (the dashboard's). */
57
+ clientAddress(request: Request): string | undefined;
58
+ /** Log what is still counted and stop the timer. */
59
+ close(): void;
60
+ }
61
+ /** The bucket key of an address: an IPv4 address itself, an IPv6 address's /64; null if neither. */
62
+ export declare function slotlockClientKey(value: string): string | null;
63
+ /**
64
+ * The network a client key (slotlockClientKey) signs in from: an IPv6 /64's /48, any other key
65
+ * itself. A site is generally given a /48 or a /56 (RFC 6177) and may use each /64 in it, so a
66
+ * budget per /64 would let one site sign in as thousands of clients and use up a ceiling every
67
+ * client shares; per /48, a site counts as one, as a site behind one IPv4 address does.
68
+ */
69
+ export declare function slotlockClientNetwork(key: string): string;
70
+ /** The limits `slotlock serve` puts in front of its agent server and dashboard. */
71
+ export declare function createSlotlockRequestLimits(options: SlotlockRequestLimitsOptions): SlotlockRequestLimits;