@camstack/types 1.2.379 → 1.2.381
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/addon.d.ts +3 -0
- package/dist/addon.js +67 -2
- package/dist/addon.mjs +5 -3
- package/dist/capabilities/caller-stamp.d.ts +12 -0
- package/dist/capabilities/index.d.ts +7 -2
- package/dist/capabilities/record-entries.cap.d.ts +84 -0
- package/dist/capabilities/records.cap.d.ts +399 -0
- package/dist/capabilities/storage-admin.cap.d.ts +268 -0
- package/dist/client.js +57 -32
- package/dist/client.mjs +57 -32
- package/dist/device/device-context.d.ts +2 -0
- package/dist/ffmpeg/fmp4-fragment-child.d.ts +2 -2
- package/dist/ffmpeg/process.d.ts +21 -0
- package/dist/generated/addon-api.d.ts +191 -0
- package/dist/generated/cap-input-defaults.d.ts +1 -1
- package/dist/generated/capability-router-map.d.ts +8 -2
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/method-device-selectors.d.ts +2 -2
- package/dist/generated/system-proxy.d.ts +2 -0
- package/dist/helpers/addon-slice-handle.d.ts +4 -0
- package/dist/helpers/trpc-leaf.d.ts +17 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +724 -32
- package/dist/index.mjs +646 -33
- package/dist/interfaces/addon-peer-bytes.d.ts +19 -2
- package/dist/interfaces/addon.d.ts +13 -0
- package/dist/interfaces/config-ui.d.ts +4 -0
- package/dist/interfaces/event-bus.d.ts +32 -3
- package/dist/interfaces/storage.d.ts +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.js +4 -0
- package/dist/node.mjs +4 -1
- package/dist/records/constants.d.ts +23 -0
- package/dist/records/entry-kind.d.ts +31 -0
- package/dist/records/errors.d.ts +81 -0
- package/dist/records/in-memory-records.d.ts +21 -0
- package/dist/records/index.d.ts +9 -0
- package/dist/records/record-kind.d.ts +144 -0
- package/dist/records/records-access.d.ts +153 -0
- package/dist/records/secret-paths.d.ts +52 -0
- package/dist/records/settings-array-adopts-from.d.ts +12 -0
- package/dist/records/settings-brokers-adopts-from.d.ts +10 -0
- package/dist/{sleep-DQYi915G.js → sleep-D8yJVc4r.js} +1971 -290
- package/dist/{sleep-D-Q2_Mcd.mjs → sleep-bZIhUvjd.mjs} +1413 -128
- package/package.json +1 -1
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
* handle. A caller that minted an offer per frame would exhaust the outstanding
|
|
63
63
|
* bound inside a second and be refused by name.
|
|
64
64
|
*/
|
|
65
|
-
import type { IncomingMessage
|
|
65
|
+
import type { IncomingMessage } from 'node:http';
|
|
66
66
|
import type { Readable } from 'node:stream';
|
|
67
67
|
import { z } from 'zod';
|
|
68
68
|
/**
|
|
@@ -145,7 +145,23 @@ export interface PeerBytesOfferOptions {
|
|
|
145
145
|
readonly declaredBytes?: number;
|
|
146
146
|
readonly ttlMs?: number;
|
|
147
147
|
}
|
|
148
|
-
|
|
148
|
+
type PeerBytesResponseEvent = 'close' | 'drain' | 'data';
|
|
149
|
+
/** Minimal `ServerResponse` surface peer-bytes producers write through. */
|
|
150
|
+
export interface PeerBytesHandlerResponse {
|
|
151
|
+
readonly headersSent: boolean;
|
|
152
|
+
readonly writable: boolean;
|
|
153
|
+
readonly destroyed: boolean;
|
|
154
|
+
readonly writableEnded: boolean;
|
|
155
|
+
writeHead(statusCode: number, statusMessage?: string, headers?: Record<string, string>): this;
|
|
156
|
+
writeHead(statusCode: number, headers?: Record<string, string>): this;
|
|
157
|
+
write(chunk: Uint8Array | string): boolean;
|
|
158
|
+
end(cb?: () => void): this;
|
|
159
|
+
destroy(err?: Error): this;
|
|
160
|
+
on(event: PeerBytesResponseEvent, listener: (...args: readonly unknown[]) => void): this;
|
|
161
|
+
once(event: 'close' | 'drain', listener: () => void): this;
|
|
162
|
+
off(event: 'close' | 'drain', listener: () => void): this;
|
|
163
|
+
}
|
|
164
|
+
export type PeerBytesHandler = (req: IncomingMessage, res: PeerBytesHandlerResponse) => void | Promise<void>;
|
|
149
165
|
/** A buffer the producer already holds, written out in bounded chunks. */
|
|
150
166
|
export interface PeerBytesBufferOffer {
|
|
151
167
|
readonly bytes: Buffer;
|
|
@@ -242,3 +258,4 @@ export interface AddonPeerBytes {
|
|
|
242
258
|
offerFile(options: PeerBytesFileOffer): Promise<PeerBytesOfferResult>;
|
|
243
259
|
open(ticket: PeerBytesTicket): Promise<PeerBytesOpenResult>;
|
|
244
260
|
}
|
|
261
|
+
export {};
|
|
@@ -5,6 +5,7 @@ import type { PipelineSlot } from '../types/pipeline.js';
|
|
|
5
5
|
import type { AddonDataPlane } from './addon-data-plane.js';
|
|
6
6
|
import type { AddonPeerBytes } from './addon-peer-bytes.js';
|
|
7
7
|
import type { SecretsAccess } from '../keychain/secrets-access.js';
|
|
8
|
+
import type { RecordsAccess } from '../records/records-access.js';
|
|
8
9
|
import type { CapabilityDeclaration } from './capability.js';
|
|
9
10
|
import type { ConfigUISchemaWithValues, FieldProbeResult } from './config-ui.js';
|
|
10
11
|
import type { IEventBus, ReadinessScope } from './event-bus.js';
|
|
@@ -733,6 +734,18 @@ export interface AddonContext<TConfig = Record<string, unknown>> {
|
|
|
733
734
|
* kernel that predates the keychain.
|
|
734
735
|
*/
|
|
735
736
|
readonly secrets?: SecretsAccess;
|
|
737
|
+
/**
|
|
738
|
+
* This addon's record kinds (D881): its own facts and documents - ledgers,
|
|
739
|
+
* audit rows, config documents, mirrors - read by key, by device, by device
|
|
740
|
+
* and time. The owner is the addon's own id, closed over by the kernel; a kind
|
|
741
|
+
* is readable and writable by its owner only. A field marked
|
|
742
|
+
* `.meta({ secret: true })` in a kind's schema is kept in the keychain
|
|
743
|
+
* automatically. See `docs/architecture/records.md`.
|
|
744
|
+
*
|
|
745
|
+
* Absent when the addon is loaded in isolation (tests, tooling) or on a
|
|
746
|
+
* kernel that predates records.
|
|
747
|
+
*/
|
|
748
|
+
readonly records?: RecordsAccess;
|
|
736
749
|
/**
|
|
737
750
|
* Runtime accessor for the capability registry — exposed to addons that
|
|
738
751
|
* need to enumerate peers of a collection cap (e.g. `turn-provider`) or
|
|
@@ -1144,3 +1144,7 @@ export interface HydrateSchemaOptions {
|
|
|
1144
1144
|
readonly inlineSecretsAsViews?: boolean;
|
|
1145
1145
|
}
|
|
1146
1146
|
export declare function hydrateSchema(schema: ConfigUISchema, values: Record<string, unknown>, options?: HydrateSchemaOptions): ConfigUISchemaWithValues;
|
|
1147
|
+
/** Strip inline `value` from a hydrated field for schema-only round-trips. */
|
|
1148
|
+
export declare function dehydrateConfigField(field: ConfigFieldWithValue): ConfigField;
|
|
1149
|
+
/** Inverse of {@link hydrateSchema} — drops resolved values from every leaf field. */
|
|
1150
|
+
export declare function dehydrateSchema(schema: ConfigUISchemaWithValues): ConfigUISchema;
|
|
@@ -751,8 +751,21 @@ export interface TopologyNode {
|
|
|
751
751
|
* - `resumed` — demand returned and the dial came back. The matching
|
|
752
752
|
* end of a `suspended`, and equally deliberate.
|
|
753
753
|
* - `broker-destroyed` — the stream was unpublished/released. Deliberate.
|
|
754
|
+
*
|
|
755
|
+
* A BATTERY camera sleeping is none of these: a sleep or a plain wake emits no
|
|
756
|
+
* `stream.offline` / `stream.online` at all (D883). It reaches an outage only
|
|
757
|
+
* through the three battery reasons below, each naming a real failure:
|
|
758
|
+
*
|
|
759
|
+
* - `wake-timeout` — a wake was DEMANDED (live viewer, recording,
|
|
760
|
+
* explicit force) and no stream arrived inside its bound.
|
|
761
|
+
* - `connection-error` — the camera is reported awake and its stream is
|
|
762
|
+
* gone.
|
|
763
|
+
* - `battery-unreachable` — nothing heard from the camera past its battery
|
|
764
|
+
* refresh window (`BATTERY_UNREACHABLE_AFTER_MS`, D847).
|
|
765
|
+
*
|
|
766
|
+
* Their `stream.online` is `recovered`, exactly as for `stale-timeout`.
|
|
754
767
|
*/
|
|
755
|
-
export type StreamHealthReason = 'stale-timeout' | 'recovered' | 'first-packet' | 'suspended' | 'resumed' | 'broker-destroyed';
|
|
768
|
+
export type StreamHealthReason = 'stale-timeout' | 'wake-timeout' | 'connection-error' | 'battery-unreachable' | 'recovered' | 'first-packet' | 'suspended' | 'resumed' | 'broker-destroyed';
|
|
756
769
|
/** All known event categories with their typed payloads */
|
|
757
770
|
/**
|
|
758
771
|
* `keychain.changed` — a value was put or deleted. Never carries the value.
|
|
@@ -1556,21 +1569,37 @@ export interface EventCatalog {
|
|
|
1556
1569
|
*/
|
|
1557
1570
|
'stream.online': {
|
|
1558
1571
|
readonly deviceId: number;
|
|
1559
|
-
|
|
1572
|
+
/** Absent when a failed wake names no published stream (a sleeping camera after a restart). */
|
|
1573
|
+
readonly camStreamId?: string;
|
|
1560
1574
|
readonly profile: CamProfile | null;
|
|
1561
1575
|
readonly brokerId: string;
|
|
1562
1576
|
readonly sourceType: string;
|
|
1563
1577
|
readonly lastPacketAt: number;
|
|
1564
1578
|
readonly reason?: StreamHealthReason;
|
|
1579
|
+
/**
|
|
1580
|
+
* Every stream of the device this one transition covers. An outage and its
|
|
1581
|
+
* recovery are DEVICE transitions (D883): main and sub dropping together is
|
|
1582
|
+
* ONE event whose `camStreamId` / `profile` name the first and this list
|
|
1583
|
+
* names them all. Absent on per-broker lazy-dial telemetry.
|
|
1584
|
+
*/
|
|
1585
|
+
readonly camStreamIds?: readonly string[];
|
|
1565
1586
|
};
|
|
1566
1587
|
'stream.offline': {
|
|
1567
1588
|
readonly deviceId: number;
|
|
1568
|
-
|
|
1589
|
+
/** Absent when a failed wake names no published stream (a sleeping camera after a restart). */
|
|
1590
|
+
readonly camStreamId?: string;
|
|
1569
1591
|
readonly profile: CamProfile | null;
|
|
1570
1592
|
readonly brokerId: string;
|
|
1571
1593
|
readonly sourceType: string;
|
|
1572
1594
|
readonly lastPacketAt: number;
|
|
1573
1595
|
readonly reason?: StreamHealthReason;
|
|
1596
|
+
/**
|
|
1597
|
+
* Every stream of the device this one transition covers. An outage and its
|
|
1598
|
+
* recovery are DEVICE transitions (D883): main and sub dropping together is
|
|
1599
|
+
* ONE event whose `camStreamId` / `profile` name the first and this list
|
|
1600
|
+
* names them all. Absent on per-broker lazy-dial telemetry.
|
|
1601
|
+
*/
|
|
1602
|
+
readonly camStreamIds?: readonly string[];
|
|
1574
1603
|
};
|
|
1575
1604
|
'retention.cleanup': {
|
|
1576
1605
|
readonly deletedEvents?: number;
|
|
@@ -49,7 +49,7 @@ export interface IStorageProvider {
|
|
|
49
49
|
* All named collections that the settings backend manages.
|
|
50
50
|
* Each collection maps to a logical table/bucket in the backend.
|
|
51
51
|
*/
|
|
52
|
-
export type SettingsCollection = 'system-settings' | 'addon-settings' | '
|
|
52
|
+
export type SettingsCollection = 'system-settings' | 'addon-settings' | 'detection-events' | 'audio-levels' | 'track-trails' | 'recording-segments' | 'recording-thumbnails' | 'recording-policies' | 'recording-storage-config' | 'recording-cleanup-queue' | 'known-faces' | (string & {});
|
|
53
53
|
export interface SettingsGetInput {
|
|
54
54
|
readonly namespace?: string;
|
|
55
55
|
readonly collection: SettingsCollection;
|
package/dist/node.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ export type { Fmp4FragmentChildArgs, Fmp4FragmentChildDeps, } from './ffmpeg/fmp
|
|
|
10
10
|
export { Fmp4FragmentChild } from './ffmpeg/fmp4-fragment-child.js';
|
|
11
11
|
export type { Fmp4CloseReason, Fmp4Packet, Fmp4PrebufferOptions, Fmp4PrebufferStats, Fmp4SubscribeInput, Fmp4SubscriberInfo, Fmp4Subscription, } from './ffmpeg/fmp4-fragment-plane.js';
|
|
12
12
|
export { Fmp4FragmentPlane } from './ffmpeg/fmp4-fragment-plane.js';
|
|
13
|
-
export type { FfmpegExit, FfmpegExitClass, FfmpegProcessOptions } from './ffmpeg/process.js';
|
|
14
|
-
export { FfmpegProcess } from './ffmpeg/process.js';
|
|
13
|
+
export type { FfmpegExit, FfmpegExitClass, FfmpegProcessOptions, FfmpegSpawnFn, SpawnedFfmpegProcess, } from './ffmpeg/process.js';
|
|
14
|
+
export { defaultFfmpegSpawn, FfmpegProcess } from './ffmpeg/process.js';
|
|
15
15
|
export type { FfmpegArgvSpawn, PinnedFfmpegSpawnAnswer } from './ffmpeg/pinned-spawn.js';
|
|
16
16
|
export { NO_PINNED_FFMPEG_DETAIL, pinnedFfmpegSpawner, resolvePinnedFfmpegSpawner, } from './ffmpeg/pinned-spawn.js';
|
|
17
17
|
export type { ChildCostClaimHandle, ChildCostClaimInput, ProcStatReader, } from './process/child-cost-registry.js';
|
package/dist/node.js
CHANGED
|
@@ -1410,6 +1410,9 @@ var Fmp4FragmentPlane = class {
|
|
|
1410
1410
|
* that also owned the bytes would need a mode per consumer, which is the same
|
|
1411
1411
|
* mistake as one builder per consumer.
|
|
1412
1412
|
*/
|
|
1413
|
+
function defaultFfmpegSpawn(command, args, options) {
|
|
1414
|
+
return (0, node_child_process.spawn)(command, args, options ?? {});
|
|
1415
|
+
}
|
|
1413
1416
|
var DEFAULT_FIRST_DATA_TIMEOUT_MS = 8e3;
|
|
1414
1417
|
var DEFAULT_RESTART_DELAY_MS = 1e3;
|
|
1415
1418
|
var DEFAULT_STABLE_RUN_MS = 3e4;
|
|
@@ -2285,6 +2288,7 @@ exports.canonicalDeviceFingerprint = canonicalDeviceFingerprint;
|
|
|
2285
2288
|
exports.canonicalHash = require_composition.canonicalHash;
|
|
2286
2289
|
exports.containsOrEquals = containsOrEquals;
|
|
2287
2290
|
exports.customizationBlockName = customizationBlockName;
|
|
2291
|
+
exports.defaultFfmpegSpawn = defaultFfmpegSpawn;
|
|
2288
2292
|
exports.diffExportTargets = diffExportTargets;
|
|
2289
2293
|
exports.downloadBinary = downloadBinary;
|
|
2290
2294
|
exports.ensureBinary = ensureBinary;
|
package/dist/node.mjs
CHANGED
|
@@ -1387,6 +1387,9 @@ var Fmp4FragmentPlane = class {
|
|
|
1387
1387
|
* that also owned the bytes would need a mode per consumer, which is the same
|
|
1388
1388
|
* mistake as one builder per consumer.
|
|
1389
1389
|
*/
|
|
1390
|
+
function defaultFfmpegSpawn(command, args, options) {
|
|
1391
|
+
return spawn(command, args, options ?? {});
|
|
1392
|
+
}
|
|
1390
1393
|
var DEFAULT_FIRST_DATA_TIMEOUT_MS = 8e3;
|
|
1391
1394
|
var DEFAULT_RESTART_DELAY_MS = 1e3;
|
|
1392
1395
|
var DEFAULT_STABLE_RUN_MS = 3e4;
|
|
@@ -2247,4 +2250,4 @@ function resolveExportFingerprint(input) {
|
|
|
2247
2250
|
return input.persisted ?? input.fresh;
|
|
2248
2251
|
}
|
|
2249
2252
|
//#endregion
|
|
2250
|
-
export { ChildCostRegistry, FFMPEG_RELEASE, FfmpegProcess, FilesystemStorageProvider, Fmp4FragmentChild, Fmp4FragmentPlane, NO_COST_CLAIM, NO_PINNED_FFMPEG_DETAIL, OwnerOnlyError, PYTHON_VERSION, buildBinaryPath, canonicalDeviceFingerprint, canonicalHash, containsOrEquals, customizationBlockName, diffExportTargets, downloadBinary, ensureBinary, ensureFfmpeg, ensurePython, findInPath, getFfmpegArtifact, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, icaclsArgs, installPythonPackages, installPythonRequirements, mkdirOwnerOnlySync, nodeProcStatReader, parseFfmpegCapabilities, parseProcCpuSeconds, parseProcRssBytes, physicalRootOf, pinnedFfmpegSpawner, probeFfmpegBinary, readProcessCost, resolveExportFingerprint, resolvePinnedFfmpegSpawner, restrictDirToOwnerSync, restrictFileToOwnerSync, signExpiringUrl, verifyExpiringUrl, writeFileAtomic, writeOwnerOnlyFileSync };
|
|
2253
|
+
export { ChildCostRegistry, FFMPEG_RELEASE, FfmpegProcess, FilesystemStorageProvider, Fmp4FragmentChild, Fmp4FragmentPlane, NO_COST_CLAIM, NO_PINNED_FFMPEG_DETAIL, OwnerOnlyError, PYTHON_VERSION, buildBinaryPath, canonicalDeviceFingerprint, canonicalHash, containsOrEquals, customizationBlockName, defaultFfmpegSpawn, diffExportTargets, downloadBinary, ensureBinary, ensureFfmpeg, ensurePython, findInPath, getFfmpegArtifact, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, icaclsArgs, installPythonPackages, installPythonRequirements, mkdirOwnerOnlySync, nodeProcStatReader, parseFfmpegCapabilities, parseProcCpuSeconds, parseProcRssBytes, physicalRootOf, pinnedFfmpegSpawner, probeFfmpegBinary, readProcessCost, resolveExportFingerprint, resolvePinnedFfmpegSpawner, restrictDirToOwnerSync, restrictFileToOwnerSync, signExpiringUrl, verifyExpiringUrl, writeFileAtomic, writeOwnerOnlyFileSync };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
|
|
2
|
+
/** The files a kind may live in. `history` is device-history's (D850), never records. */
|
|
3
|
+
export declare const RECORD_DATABASES: readonly ['settings', 'events'];
|
|
4
|
+
export type RecordDatabase = (typeof RECORD_DATABASES)[number];
|
|
5
|
+
/** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
|
|
6
|
+
export declare const RECORD_CLASSES: readonly ['config', 'ledger', 'audit', 'mirror', 'ephemeral'];
|
|
7
|
+
export type RecordClass = (typeof RECORD_CLASSES)[number];
|
|
8
|
+
export declare const RECORD_DEFAULT_MAX_DATA_BYTES: number;
|
|
9
|
+
export declare const RECORD_MAX_DATA_BYTES: number;
|
|
10
|
+
export declare const RECORD_ID_MAX_LENGTH = 256;
|
|
11
|
+
export declare const RECORD_BATCH_MAX = 1000;
|
|
12
|
+
export declare const RECORD_BATCH_MAX_BYTES: number;
|
|
13
|
+
export declare const RECORD_QUERY_MAX = 1000;
|
|
14
|
+
/** Rows one DELETE statement removes before the serving thread yields. */
|
|
15
|
+
export declare const RECORD_DELETE_BATCH = 5000;
|
|
16
|
+
/** A kind in the `settings` file may not bound itself above this (D447: high volume is `events`). */
|
|
17
|
+
export declare const RECORD_SETTINGS_MAX_ROWS = 50000;
|
|
18
|
+
export declare const RECORD_MAX_INDEXES = 8;
|
|
19
|
+
export declare const RECORD_MAX_INDEX_FIELDS = 4;
|
|
20
|
+
/** The engine's TTL sweep cadence. */
|
|
21
|
+
export declare const RECORD_SWEEP_INTERVAL_MS: number;
|
|
22
|
+
/** The `Column`-free proof prefix: a ref collection for a kind's secret fields. */
|
|
23
|
+
export declare const RECORD_SECRET_COLLECTION_PREFIX = "records:";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry kinds (D881): every list of user-created entries (notification
|
|
3
|
+
* targets, brokers, LLM profiles) is ONE record kind with an id, a name and a
|
|
4
|
+
* kind-specific body, and the admin UI lists/adds/edits/deletes all of them
|
|
5
|
+
* through the `record-entries` capability with password fields derived from
|
|
6
|
+
* the schema (`.meta({ secret: true })`).
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import type { RecordIndex, DefinedRecordKind } from './record-kind.js';
|
|
10
|
+
/** What an entry kind adds to a bare record kind. */
|
|
11
|
+
export interface EntryKindSpec<S extends z.ZodType> {
|
|
12
|
+
/** The title the entries UI shows for the list. */
|
|
13
|
+
readonly title: string;
|
|
14
|
+
/** The body: a `name` field plus the kind's own. A discriminated union is fine. */
|
|
15
|
+
readonly schema: S;
|
|
16
|
+
readonly writeTrigger?: string;
|
|
17
|
+
readonly nameField?: string;
|
|
18
|
+
readonly database?: 'settings' | 'events';
|
|
19
|
+
readonly indexes?: readonly RecordIndex[];
|
|
20
|
+
readonly secretArrayKeys?: Readonly<Record<string, string>>;
|
|
21
|
+
readonly adoptsFrom?: string;
|
|
22
|
+
}
|
|
23
|
+
/** The body of a plain entry: a name and the fields the kind adds. */
|
|
24
|
+
export declare function entryBody<Shape extends z.ZodRawShape>(shape: Shape): z.ZodObject<{
|
|
25
|
+
name: z.ZodString;
|
|
26
|
+
} & Shape extends infer T ? { -readonly [P in keyof T]: T[P]; } : never, z.core.$strip>;
|
|
27
|
+
/**
|
|
28
|
+
* Declare one entry kind. An entry is configuration: never trimmed by the
|
|
29
|
+
* engine, never aged, belonging to no device.
|
|
30
|
+
*/
|
|
31
|
+
export declare function defineEntryKind<S extends z.ZodType>(name: string, spec: EntryKindSpec<S>): DefinedRecordKind<S>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The named failures of `ctx.records` (D881).
|
|
3
|
+
*
|
|
4
|
+
* A records call crosses a worker thread, a UDS channel and a tRPC router, and
|
|
5
|
+
* an error class does not survive any of them - only its message does. So
|
|
6
|
+
* every message STARTS with a stable code, `records/<code>: <detail>`, and
|
|
7
|
+
* {@link recordsErrorFromMessage} rebuilds the typed class on the caller's side.
|
|
8
|
+
* A failed read is never `[]` or `undefined`: it throws one of these.
|
|
9
|
+
*/
|
|
10
|
+
/** Every code a records failure carries. */
|
|
11
|
+
export declare const RECORDS_ERROR_CODES: readonly ['rev-conflict', 'too-large', 'unindexed-filter', 'filter-required', 'unknown-kind', 'not-found', 'unique-violation', 'plaintext-secret', 'foreign-marker', 'secret-unavailable', 'invalid-record', 'declaration-refused', 'adoption-pending', 'no-caller', 'batch-too-large'];
|
|
12
|
+
export type RecordsErrorCode = (typeof RECORDS_ERROR_CODES)[number];
|
|
13
|
+
/** The base of every records failure. `detail` is the human part of the message. */
|
|
14
|
+
export declare class RecordsError extends Error {
|
|
15
|
+
readonly code: RecordsErrorCode;
|
|
16
|
+
readonly detail: string;
|
|
17
|
+
constructor(code: RecordsErrorCode, detail: string);
|
|
18
|
+
}
|
|
19
|
+
/** A write guarded by `ifRev` found another revision (or an existing row for `ifRev: 0`). */
|
|
20
|
+
export declare class RevConflictError extends RecordsError {
|
|
21
|
+
constructor(detail: string);
|
|
22
|
+
}
|
|
23
|
+
/** `data` exceeds the kind's `maxDataBytes`. Bytes are never record fields (D613). */
|
|
24
|
+
export declare class RecordTooLargeError extends RecordsError {
|
|
25
|
+
constructor(detail: string);
|
|
26
|
+
}
|
|
27
|
+
/** A filter, order or aggregate names a field no declared index serves. */
|
|
28
|
+
export declare class UnindexedFilterError extends RecordsError {
|
|
29
|
+
constructor(detail: string);
|
|
30
|
+
}
|
|
31
|
+
/** A bulk delete with no predicate at all. */
|
|
32
|
+
export declare class FilterRequiredError extends RecordsError {
|
|
33
|
+
constructor(detail: string);
|
|
34
|
+
}
|
|
35
|
+
/** The caller declared no such kind (another addon's kind is indistinguishable from none). */
|
|
36
|
+
export declare class UnknownRecordKindError extends RecordsError {
|
|
37
|
+
constructor(detail: string);
|
|
38
|
+
}
|
|
39
|
+
export declare class RecordNotFoundError extends RecordsError {
|
|
40
|
+
constructor(detail: string);
|
|
41
|
+
}
|
|
42
|
+
/** A unique index refused the write. */
|
|
43
|
+
export declare class RecordUniqueViolationError extends RecordsError {
|
|
44
|
+
constructor(detail: string);
|
|
45
|
+
}
|
|
46
|
+
/** A secret field held a plain string where only a keychain marker may be stored. */
|
|
47
|
+
export declare class PlaintextSecretError extends RecordsError {
|
|
48
|
+
constructor(detail: string);
|
|
49
|
+
}
|
|
50
|
+
/** A marker whose ref is not the row's own (the D878 rule). */
|
|
51
|
+
export declare class ForeignSecretMarkerError extends RecordsError {
|
|
52
|
+
constructor(detail: string);
|
|
53
|
+
}
|
|
54
|
+
/** The keychain could not store or serve a value: explicit, never an empty string. */
|
|
55
|
+
export declare class RecordSecretUnavailableError extends RecordsError {
|
|
56
|
+
constructor(detail: string);
|
|
57
|
+
}
|
|
58
|
+
export declare class InvalidRecordError extends RecordsError {
|
|
59
|
+
constructor(detail: string);
|
|
60
|
+
}
|
|
61
|
+
/** A kind declaration the engine refuses (non-additive change, bad shape, wrong file). */
|
|
62
|
+
export declare class DeclarationRefusedError extends RecordsError {
|
|
63
|
+
constructor(detail: string);
|
|
64
|
+
}
|
|
65
|
+
/** A kind is empty while the legacy table it adopts from still holds rows. */
|
|
66
|
+
export declare class AdoptionPendingError extends RecordsError {
|
|
67
|
+
constructor(detail: string);
|
|
68
|
+
}
|
|
69
|
+
export declare class NoCallerError extends RecordsError {
|
|
70
|
+
constructor(detail: string);
|
|
71
|
+
}
|
|
72
|
+
export declare class BatchTooLargeError extends RecordsError {
|
|
73
|
+
constructor(detail: string);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Rebuild the typed error from a message that crossed a wire; `null` when the
|
|
77
|
+
* message is not a records failure.
|
|
78
|
+
*/
|
|
79
|
+
export declare function recordsErrorFromMessage(message: string): RecordsError | null;
|
|
80
|
+
/** The records failure inside any thrown value (a wrapped tRPC error included), or the value. */
|
|
81
|
+
export declare function asRecordsError(err: unknown): unknown;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type RecordsAccess } from './records-access.js';
|
|
2
|
+
interface StoredRow {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly at: number | null;
|
|
5
|
+
readonly rev: number;
|
|
6
|
+
readonly updatedAt: number;
|
|
7
|
+
readonly data: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export interface InMemoryRecords {
|
|
10
|
+
readonly records: RecordsAccess;
|
|
11
|
+
readonly tables: ReadonlyMap<string, ReadonlyMap<string, StoredRow>>;
|
|
12
|
+
seed(kind: string, row: {
|
|
13
|
+
id: string;
|
|
14
|
+
at?: number;
|
|
15
|
+
data: Record<string, unknown>;
|
|
16
|
+
}): void;
|
|
17
|
+
failWrites(on: boolean): void;
|
|
18
|
+
failReads(on: boolean): void;
|
|
19
|
+
}
|
|
20
|
+
export declare function makeInMemoryRecords(owner?: string): InMemoryRecords;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './constants.js';
|
|
2
|
+
export * from './errors.js';
|
|
3
|
+
export * from './record-kind.js';
|
|
4
|
+
export * from './secret-paths.js';
|
|
5
|
+
export * from './records-access.js';
|
|
6
|
+
export * from './entry-kind.js';
|
|
7
|
+
export * from './settings-brokers-adopts-from.js';
|
|
8
|
+
export * from './settings-array-adopts-from.js';
|
|
9
|
+
export { makeInMemoryRecords, type InMemoryRecords } from './in-memory-records.js';
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Record kinds (D881): the declaration an addon makes of one kind of fact, the
|
|
3
|
+
* Zod row schema that describes it, and the helper that turns the two into the
|
|
4
|
+
* JSON the engine stores.
|
|
5
|
+
*
|
|
6
|
+
* A kind is declared ONCE, next to the code that owns it, and the engine's
|
|
7
|
+
* reconciler creates its indexes - nothing creates DDL at runtime from a write.
|
|
8
|
+
* Everything the generic machinery needs (secret fields, the entry descriptor)
|
|
9
|
+
* is DERIVED from the Zod schema: `.meta({ secret: true })` marks a credential,
|
|
10
|
+
* never a field name (D879).
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import { RECORD_CLASSES, RECORD_DATABASES } from './constants.js';
|
|
14
|
+
/** A JSON field path: `trackId`, `auth.accessToken`. Charset-restricted: it is rendered into DDL. */
|
|
15
|
+
export declare const RecordFieldPathSchema: z.ZodString;
|
|
16
|
+
export declare const RecordIndexSchema: z.ZodObject<{
|
|
17
|
+
name: z.ZodString;
|
|
18
|
+
fields: z.ZodArray<z.ZodString>;
|
|
19
|
+
unique: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
export type RecordIndex = z.infer<typeof RecordIndexSchema>;
|
|
22
|
+
export declare const RecordRetentionSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
23
|
+
ttlMs: z.ZodNumber;
|
|
24
|
+
from: z.ZodEnum<{
|
|
25
|
+
at: "at";
|
|
26
|
+
write: "write";
|
|
27
|
+
}>;
|
|
28
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
29
|
+
maxRows: z.ZodOptional<z.ZodNumber>;
|
|
30
|
+
maxRowsPerDevice: z.ZodOptional<z.ZodNumber>;
|
|
31
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
32
|
+
owner: z.ZodString;
|
|
33
|
+
reason: z.ZodString;
|
|
34
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
35
|
+
none: z.ZodString;
|
|
36
|
+
}, z.core.$strict>]>;
|
|
37
|
+
export type RecordRetention = z.infer<typeof RecordRetentionSchema>;
|
|
38
|
+
/** The descriptor a generic entries UI reads (D881 "generic entries"). */
|
|
39
|
+
export declare const EntryDescriptorSchema: z.ZodObject<{
|
|
40
|
+
title: z.ZodString;
|
|
41
|
+
nameField: z.ZodString;
|
|
42
|
+
fields: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
43
|
+
}, z.core.$strict>;
|
|
44
|
+
export type EntryDescriptor = z.infer<typeof EntryDescriptorSchema>;
|
|
45
|
+
/** What crosses the wire and sits in the engine's catalog: JSON only, no Zod. */
|
|
46
|
+
export declare const RecordKindDeclarationSchema: z.ZodObject<{
|
|
47
|
+
database: z.ZodDefault<z.ZodEnum<{
|
|
48
|
+
events: "events";
|
|
49
|
+
settings: "settings";
|
|
50
|
+
}>>;
|
|
51
|
+
class: z.ZodEnum<{
|
|
52
|
+
audit: "audit";
|
|
53
|
+
config: "config";
|
|
54
|
+
ephemeral: "ephemeral";
|
|
55
|
+
ledger: "ledger";
|
|
56
|
+
mirror: "mirror";
|
|
57
|
+
}>;
|
|
58
|
+
writeTrigger: z.ZodString;
|
|
59
|
+
device: z.ZodDefault<z.ZodEnum<{
|
|
60
|
+
none: "none";
|
|
61
|
+
optional: "optional";
|
|
62
|
+
required: "required";
|
|
63
|
+
}>>;
|
|
64
|
+
at: z.ZodDefault<z.ZodEnum<{
|
|
65
|
+
none: "none";
|
|
66
|
+
required: "required";
|
|
67
|
+
}>>;
|
|
68
|
+
indexes: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
69
|
+
name: z.ZodString;
|
|
70
|
+
fields: z.ZodArray<z.ZodString>;
|
|
71
|
+
unique: z.ZodOptional<z.ZodBoolean>;
|
|
72
|
+
}, z.core.$strict>>>;
|
|
73
|
+
retention: z.ZodUnion<readonly [z.ZodObject<{
|
|
74
|
+
ttlMs: z.ZodNumber;
|
|
75
|
+
from: z.ZodEnum<{
|
|
76
|
+
at: "at";
|
|
77
|
+
write: "write";
|
|
78
|
+
}>;
|
|
79
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
80
|
+
maxRows: z.ZodOptional<z.ZodNumber>;
|
|
81
|
+
maxRowsPerDevice: z.ZodOptional<z.ZodNumber>;
|
|
82
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
83
|
+
owner: z.ZodString;
|
|
84
|
+
reason: z.ZodString;
|
|
85
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
86
|
+
none: z.ZodString;
|
|
87
|
+
}, z.core.$strict>]>;
|
|
88
|
+
deviceRemoval: z.ZodDefault<z.ZodEnum<{
|
|
89
|
+
delete: "delete";
|
|
90
|
+
none: "none";
|
|
91
|
+
}>>;
|
|
92
|
+
maxDataBytes: z.ZodDefault<z.ZodNumber>;
|
|
93
|
+
secretPaths: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
94
|
+
secretArrayKeys: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
95
|
+
adoptsFrom: z.ZodOptional<z.ZodString>;
|
|
96
|
+
transactFamily: z.ZodOptional<z.ZodString>;
|
|
97
|
+
entry: z.ZodOptional<z.ZodObject<{
|
|
98
|
+
title: z.ZodString;
|
|
99
|
+
nameField: z.ZodString;
|
|
100
|
+
fields: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
101
|
+
}, z.core.$strict>>;
|
|
102
|
+
}, z.core.$strict>;
|
|
103
|
+
export type RecordKindDeclaration = z.infer<typeof RecordKindDeclarationSchema>;
|
|
104
|
+
/** The kind names: lower-case, dash separated, short enough for an index name. */
|
|
105
|
+
export declare const RecordKindNameSchema: z.ZodString;
|
|
106
|
+
/** Cross-field rules a declaration must satisfy. Throws {@link DeclarationRefusedError}. */
|
|
107
|
+
export declare function assertValidDeclaration(name: string, decl: RecordKindDeclaration): void;
|
|
108
|
+
/** The input of {@link defineRecordKinds} for one kind. */
|
|
109
|
+
export interface RecordKindSpec<S extends z.ZodType = z.ZodType> {
|
|
110
|
+
readonly schema: S;
|
|
111
|
+
readonly database?: (typeof RECORD_DATABASES)[number];
|
|
112
|
+
readonly class: (typeof RECORD_CLASSES)[number];
|
|
113
|
+
readonly writeTrigger: string;
|
|
114
|
+
readonly device?: 'required' | 'optional' | 'none';
|
|
115
|
+
readonly at?: 'required' | 'none';
|
|
116
|
+
readonly indexes?: readonly RecordIndex[];
|
|
117
|
+
readonly retention: RecordRetention;
|
|
118
|
+
readonly deviceRemoval?: 'delete' | 'none';
|
|
119
|
+
readonly maxDataBytes?: number;
|
|
120
|
+
readonly secretArrayKeys?: Readonly<Record<string, string>>;
|
|
121
|
+
readonly adoptsFrom?: string;
|
|
122
|
+
readonly transactFamily?: string;
|
|
123
|
+
/** Makes this an ENTRY kind: it gets the generic list/add/edit/delete UI. */
|
|
124
|
+
readonly entry?: {
|
|
125
|
+
readonly title: string;
|
|
126
|
+
readonly nameField?: string;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** A declared kind: its name, the row schema and the JSON the engine stores. */
|
|
130
|
+
export interface DefinedRecordKind<S extends z.ZodType = z.ZodType> {
|
|
131
|
+
readonly name: string;
|
|
132
|
+
readonly schema: S;
|
|
133
|
+
readonly declaration: RecordKindDeclaration;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Declare the kinds of a package. The keys are the kind names. The result is
|
|
137
|
+
* what `ctx.records.declare` sends to the engine and what `ctx.records.kind`
|
|
138
|
+
* types its handle from.
|
|
139
|
+
*/
|
|
140
|
+
export declare function defineRecordKinds<const T extends Readonly<Record<string, RecordKindSpec>>>(specs: T): {
|
|
141
|
+
readonly [K in keyof T & string]: DefinedRecordKind<T[K]['schema']>;
|
|
142
|
+
};
|
|
143
|
+
/** The wire form of a package's kinds: name -> declaration. */
|
|
144
|
+
export declare function declarationsOf(kinds: Readonly<Record<string, DefinedRecordKind>>): Readonly<Record<string, RecordKindDeclaration>>;
|