@oatscenter/trellis 0.100.0-rc.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.
- package/README.md +59 -0
- package/auth/authorization/client_context.d.ts +61 -0
- package/auth/authorization/client_context.js +266 -0
- package/auth/authorization/nats_registry.d.ts +41 -0
- package/auth/authorization/nats_registry.js +283 -0
- package/auth/authorization/provider_cache.d.ts +90 -0
- package/auth/authorization/provider_cache.js +783 -0
- package/auth/authorization/refresh.d.ts +46 -0
- package/auth/authorization/refresh.js +246 -0
- package/auth/authorization/types.d.ts +207 -0
- package/auth/authorization/types.js +60 -0
- package/auth/authorization_context.d.ts +11 -0
- package/auth/authorization_context.js +10 -0
- package/auth/browser/flow_types.d.ts +84 -0
- package/auth/browser/flow_types.js +70 -0
- package/auth/browser/portal.d.ts +21 -0
- package/auth/browser/portal.js +307 -0
- package/auth/browser/storage.d.ts +26 -0
- package/auth/browser/storage.js +224 -0
- package/auth/browser.d.ts +11 -0
- package/auth/browser.js +10 -0
- package/auth/browser_recovery.d.ts +13 -0
- package/auth/browser_recovery.js +38 -0
- package/auth/device_activation.d.ts +134 -0
- package/auth/device_activation.js +376 -0
- package/auth/http_error.d.ts +12 -0
- package/auth/http_error.js +32 -0
- package/auth/keys.d.ts +5 -0
- package/auth/keys.js +63 -0
- package/auth/mod.d.ts +16 -0
- package/auth/mod.js +16 -0
- package/auth/proof.d.ts +31 -0
- package/auth/proof.js +95 -0
- package/auth/protocol_wasm/trellis_protocol_wasm.d.ts +138 -0
- package/auth/protocol_wasm/trellis_protocol_wasm.js +605 -0
- package/auth/protocol_wasm/trellis_protocol_wasm_bg.wasm +0 -0
- package/auth/protocol_wasm/trellis_protocol_wasm_bytes.d.ts +1 -0
- package/auth/protocol_wasm/trellis_protocol_wasm_bytes.js +2 -0
- package/auth/protocol_wasm.d.ts +179 -0
- package/auth/protocol_wasm.js +133 -0
- package/auth/session_auth.d.ts +30 -0
- package/auth/session_auth.js +77 -0
- package/auth/session_proof.d.ts +53 -0
- package/auth/session_proof.js +43 -0
- package/auth/time.d.ts +6 -0
- package/auth/time.js +7 -0
- package/auth/trellis_id.d.ts +1 -0
- package/auth/trellis_id.js +5 -0
- package/auth/utils.d.ts +10 -0
- package/auth/utils.js +69 -0
- package/auth.d.ts +1 -0
- package/auth.js +1 -0
- package/caller.d.ts +111 -0
- package/caller.js +85 -0
- package/client.d.ts +14 -0
- package/client.js +1 -0
- package/client_connect.d.ts +143 -0
- package/client_connect.js +1097 -0
- package/codec.d.ts +13 -0
- package/codec.js +192 -0
- package/connection.d.ts +107 -0
- package/connection.js +396 -0
- package/device/activation.d.ts +42 -0
- package/device/activation.js +319 -0
- package/device.d.ts +80 -0
- package/device.js +687 -0
- package/env.d.ts +2 -0
- package/env.js +10 -0
- package/errors/AuthError.d.ts +30 -0
- package/errors/AuthError.js +34 -0
- package/errors/KVError.d.ts +30 -0
- package/errors/KVError.js +36 -0
- package/errors/OperationLifecycleError.d.ts +110 -0
- package/errors/OperationLifecycleError.js +142 -0
- package/errors/RemoteError.d.ts +61 -0
- package/errors/RemoteError.js +72 -0
- package/errors/SchemaValidationError.d.ts +48 -0
- package/errors/SchemaValidationError.js +49 -0
- package/errors/StoreError.d.ts +21 -0
- package/errors/StoreError.js +31 -0
- package/errors/TransferError.d.ts +21 -0
- package/errors/TransferError.js +31 -0
- package/errors/TransportError.d.ts +29 -0
- package/errors/TransportError.js +33 -0
- package/errors/TrellisError.d.ts +15 -0
- package/errors/TrellisError.js +15 -0
- package/errors/ValidationError.d.ts +50 -0
- package/errors/ValidationError.js +59 -0
- package/errors/index.d.ts +254 -0
- package/errors/index.js +238 -0
- package/generated.d.ts +4 -0
- package/generated.js +3 -0
- package/generated_support.d.ts +120 -0
- package/generated_support.js +359 -0
- package/globals.d.ts +9 -0
- package/globals.js +16 -0
- package/health_transport.d.ts +18 -0
- package/health_transport.js +32 -0
- package/helpers.d.ts +11 -0
- package/helpers.js +53 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/internal_sdk/generated/apis/auth/mod.d.ts +805 -0
- package/internal_sdk/generated/apis/auth/mod.js +958 -0
- package/internal_sdk/generated/apis/core/mod.d.ts +79 -0
- package/internal_sdk/generated/apis/core/mod.js +93 -0
- package/internal_sdk/generated/apis/events/mod.d.ts +224 -0
- package/internal_sdk/generated/apis/events/mod.js +313 -0
- package/internal_sdk/generated/apis/health/mod.d.ts +106 -0
- package/internal_sdk/generated/apis/health/mod.js +132 -0
- package/internal_sdk/generated/apis/index.d.ts +7 -0
- package/internal_sdk/generated/apis/index.js +8 -0
- package/internal_sdk/generated/apis/jobs/mod.d.ts +176 -0
- package/internal_sdk/generated/apis/jobs/mod.js +209 -0
- package/internal_sdk/generated/apis/state/mod.d.ts +104 -0
- package/internal_sdk/generated/apis/state/mod.js +139 -0
- package/internal_sdk/generated/index.d.ts +4 -0
- package/internal_sdk/generated/index.js +5 -0
- package/internal_sdk/generated/package.json +18 -0
- package/internal_sdk/generated/participants/cli/mod.d.ts +402 -0
- package/internal_sdk/generated/participants/cli/mod.js +460 -0
- package/internal_sdk/generated/participants/cli/types.d.ts +2 -0
- package/internal_sdk/generated/participants/cli/types.js +3 -0
- package/internal_sdk/generated/participants/console/mod.d.ts +489 -0
- package/internal_sdk/generated/participants/console/mod.js +579 -0
- package/internal_sdk/generated/participants/console/types.d.ts +2 -0
- package/internal_sdk/generated/participants/console/types.js +3 -0
- package/internal_sdk/generated/participants/eventsRuntime/mod.d.ts +64 -0
- package/internal_sdk/generated/participants/eventsRuntime/mod.js +42 -0
- package/internal_sdk/generated/participants/eventsRuntime/types.d.ts +2 -0
- package/internal_sdk/generated/participants/eventsRuntime/types.js +3 -0
- package/internal_sdk/generated/participants/healthRuntime/mod.d.ts +58 -0
- package/internal_sdk/generated/participants/healthRuntime/mod.js +36 -0
- package/internal_sdk/generated/participants/healthRuntime/types.d.ts +2 -0
- package/internal_sdk/generated/participants/healthRuntime/types.js +3 -0
- package/internal_sdk/generated/participants/index.d.ts +8 -0
- package/internal_sdk/generated/participants/index.js +9 -0
- package/internal_sdk/generated/participants/jobsRuntime/mod.d.ts +64 -0
- package/internal_sdk/generated/participants/jobsRuntime/mod.js +42 -0
- package/internal_sdk/generated/participants/jobsRuntime/types.d.ts +2 -0
- package/internal_sdk/generated/participants/jobsRuntime/types.js +3 -0
- package/internal_sdk/generated/participants/platform/mod.d.ts +164 -0
- package/internal_sdk/generated/participants/platform/mod.js +149 -0
- package/internal_sdk/generated/participants/platform/types.d.ts +7 -0
- package/internal_sdk/generated/participants/platform/types.js +5 -0
- package/internal_sdk/generated/participants/portal/mod.d.ts +131 -0
- package/internal_sdk/generated/participants/portal/mod.js +109 -0
- package/internal_sdk/generated/participants/portal/types.d.ts +2 -0
- package/internal_sdk/generated/participants/portal/types.js +3 -0
- package/internal_sdk/generated/types/_internal/p0.d.ts +13511 -0
- package/internal_sdk/generated/types/_internal/p0.js +6105 -0
- package/internal_sdk/generated/types/index.d.ts +2951 -0
- package/internal_sdk/generated/types/index.js +1477 -0
- package/jobs.d.ts +309 -0
- package/jobs.js +389 -0
- package/kv.d.ts +85 -0
- package/kv.js +330 -0
- package/operations.d.ts +189 -0
- package/operations.js +847 -0
- package/package.json +73 -0
- package/pagination.d.ts +25 -0
- package/pagination.js +117 -0
- package/participant.d.ts +6 -0
- package/participant.js +6 -0
- package/participant_runtime/api.d.ts +131 -0
- package/participant_runtime/api.js +42 -0
- package/participant_runtime/json.d.ts +16 -0
- package/participant_runtime/json.js +193 -0
- package/participant_runtime/metadata.d.ts +34 -0
- package/participant_runtime/metadata.js +4 -0
- package/participant_runtime/participant.d.ts +200 -0
- package/participant_runtime/participant.js +330 -0
- package/participant_runtime/schemas.d.ts +347 -0
- package/participant_runtime/schemas.js +211 -0
- package/participant_runtime/surface_names.d.ts +16 -0
- package/participant_runtime/surface_names.js +34 -0
- package/portable.d.ts +30 -0
- package/portable.js +14 -0
- package/provider.d.ts +69 -0
- package/provider.js +68 -0
- package/runtime_transport.d.ts +25 -0
- package/runtime_transport.js +48 -0
- package/service/mod.d.ts +14 -0
- package/service/mod.js +13 -0
- package/service/outbox_inbox.d.ts +259 -0
- package/service/outbox_inbox.js +908 -0
- package/service/runtime/bootstrap.d.ts +56 -0
- package/service/runtime/bootstrap.js +226 -0
- package/service/runtime/core.d.ts +46 -0
- package/service/runtime/core.js +1747 -0
- package/service/runtime/health.d.ts +97 -0
- package/service/runtime/health.js +153 -0
- package/service/runtime/health_schemas.d.ts +23 -0
- package/service/runtime/health_schemas.js +20 -0
- package/service/runtime/internal_jobs/active-job.d.ts +40 -0
- package/service/runtime/internal_jobs/active-job.js +91 -0
- package/service/runtime/internal_jobs/bindings.d.ts +40 -0
- package/service/runtime/internal_jobs/bindings.js +1 -0
- package/service/runtime/internal_jobs/cancellation-registry.d.ts +13 -0
- package/service/runtime/internal_jobs/cancellation-registry.js +55 -0
- package/service/runtime/internal_jobs/heartbeat.d.ts +33 -0
- package/service/runtime/internal_jobs/heartbeat.js +65 -0
- package/service/runtime/internal_jobs/job-manager.d.ts +126 -0
- package/service/runtime/internal_jobs/job-manager.js +821 -0
- package/service/runtime/internal_jobs/key-coordinator.d.ts +280 -0
- package/service/runtime/internal_jobs/key-coordinator.js +747 -0
- package/service/runtime/internal_jobs/projection.d.ts +3 -0
- package/service/runtime/internal_jobs/projection.js +42 -0
- package/service/runtime/internal_jobs/runtime-worker.d.ts +126 -0
- package/service/runtime/internal_jobs/runtime-worker.js +454 -0
- package/service/runtime/internal_jobs/types.d.ts +246 -0
- package/service/runtime/internal_jobs/types.js +163 -0
- package/service/runtime/logger.d.ts +2 -0
- package/service/runtime/logger.js +7 -0
- package/service/runtime/runtime.d.ts +16 -0
- package/service/runtime/runtime.js +1 -0
- package/service/runtime/service.d.ts +372 -0
- package/service/runtime/service.js +1896 -0
- package/service/runtime/transfer/download.d.ts +27 -0
- package/service/runtime/transfer/download.js +1 -0
- package/service/runtime/transfer/protocol.d.ts +15 -0
- package/service/runtime/transfer/protocol.js +46 -0
- package/service/runtime/transfer/queue.d.ts +18 -0
- package/service/runtime/transfer/queue.js +136 -0
- package/service/runtime/transfer/upload.d.ts +40 -0
- package/service/runtime/transfer/upload.js +27 -0
- package/service/runtime/transfer.d.ts +62 -0
- package/service/runtime/transfer.js +763 -0
- package/session.d.ts +862 -0
- package/session.js +3407 -0
- package/store.d.ts +71 -0
- package/store.js +412 -0
- package/store_entry.d.ts +20 -0
- package/store_entry.js +75 -0
- package/tasks.d.ts +10 -0
- package/tasks.js +28 -0
- package/telemetry/carrier.d.ts +8 -0
- package/telemetry/carrier.js +20 -0
- package/telemetry/core.d.ts +7 -0
- package/telemetry/core.js +14 -0
- package/telemetry/env.d.ts +2 -0
- package/telemetry/env.js +10 -0
- package/telemetry/init.d.ts +2 -0
- package/telemetry/init.js +7 -0
- package/telemetry/metrics.d.ts +81 -0
- package/telemetry/metrics.js +267 -0
- package/telemetry/mod.d.ts +13 -0
- package/telemetry/mod.js +8 -0
- package/telemetry/nats.d.ts +6 -0
- package/telemetry/nats.js +6 -0
- package/telemetry/result.d.ts +1 -0
- package/telemetry/result.js +18 -0
- package/telemetry/runtime.d.ts +1 -0
- package/telemetry/runtime.js +141 -0
- package/telemetry/trace.d.ts +4 -0
- package/telemetry/trace.js +26 -0
- package/telemetry/trellis.d.ts +1 -0
- package/telemetry/trellis.js +1 -0
- package/telemetry.d.ts +1 -0
- package/telemetry.js +1 -0
- package/transfer.d.ts +112 -0
- package/transfer.js +525 -0
- package/transfer_protocol.d.ts +1 -0
- package/transfer_protocol.js +13 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
function fail(expected) {
|
|
2
|
+
throw new TypeError(`Expected ${expected}`);
|
|
3
|
+
}
|
|
4
|
+
function codec(decode, encode = decode) {
|
|
5
|
+
return Object.freeze({ decode, encode });
|
|
6
|
+
}
|
|
7
|
+
function integer(name, minimum, maximum) {
|
|
8
|
+
return codec((value) => typeof value === "number" && Number.isInteger(value) && value >= minimum &&
|
|
9
|
+
value <= maximum && !Object.is(value, -0)
|
|
10
|
+
? value
|
|
11
|
+
: fail(`${name} integer`));
|
|
12
|
+
}
|
|
13
|
+
function bigintCodec(name, minimum, maximum) {
|
|
14
|
+
const pattern = /^(?:0|-?[1-9][0-9]*)$/;
|
|
15
|
+
return codec((value) => {
|
|
16
|
+
if (typeof value !== "string" || !pattern.test(value)) {
|
|
17
|
+
return fail(`${name} decimal string`);
|
|
18
|
+
}
|
|
19
|
+
const decoded = BigInt(value);
|
|
20
|
+
return decoded >= minimum && decoded <= maximum
|
|
21
|
+
? decoded
|
|
22
|
+
: fail(`${name} decimal string`);
|
|
23
|
+
}, (value) => typeof value === "bigint" && value >= minimum && value <= maximum
|
|
24
|
+
? value.toString()
|
|
25
|
+
: fail(`${name} bigint`));
|
|
26
|
+
}
|
|
27
|
+
const stringCodec = codec((value) => typeof value === "string" ? value : fail("string"));
|
|
28
|
+
const boolCodec = codec((value) => typeof value === "boolean" ? value : fail("boolean"));
|
|
29
|
+
const numberCodec = codec((value) => typeof value === "number" && Number.isFinite(value)
|
|
30
|
+
? value
|
|
31
|
+
: fail("finite number"));
|
|
32
|
+
function decodeBase64(value) {
|
|
33
|
+
if (typeof value !== "string" || value.length % 4 !== 0 ||
|
|
34
|
+
!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {
|
|
35
|
+
return fail("padded standard base64 string");
|
|
36
|
+
}
|
|
37
|
+
const binary = atob(value);
|
|
38
|
+
const decoded = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
39
|
+
return encodeBase64(decoded) === value
|
|
40
|
+
? decoded
|
|
41
|
+
: fail("padded standard base64 string");
|
|
42
|
+
}
|
|
43
|
+
function encodeBase64(value) {
|
|
44
|
+
if (!(value instanceof Uint8Array))
|
|
45
|
+
return fail("Uint8Array");
|
|
46
|
+
let binary = "";
|
|
47
|
+
for (const byte of value)
|
|
48
|
+
binary += String.fromCharCode(byte);
|
|
49
|
+
return btoa(binary);
|
|
50
|
+
}
|
|
51
|
+
function timestamp(value) {
|
|
52
|
+
if (typeof value !== "string")
|
|
53
|
+
return fail("RFC 3339 timestamp");
|
|
54
|
+
const match = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/i
|
|
55
|
+
.exec(value);
|
|
56
|
+
if (!match)
|
|
57
|
+
return fail("RFC 3339 timestamp");
|
|
58
|
+
const [, seconds, fraction, offset] = match;
|
|
59
|
+
if (seconds.slice(-2) === "60") {
|
|
60
|
+
return fail("RFC 3339 timestamp without leap second");
|
|
61
|
+
}
|
|
62
|
+
const localSeconds = seconds.replace("t", "T");
|
|
63
|
+
const local = new Date(`${localSeconds}Z`);
|
|
64
|
+
if (Number.isNaN(local.getTime()) ||
|
|
65
|
+
local.toISOString().slice(0, 19) !== localSeconds) {
|
|
66
|
+
return fail("RFC 3339 timestamp");
|
|
67
|
+
}
|
|
68
|
+
if (offset.toUpperCase() !== "Z" &&
|
|
69
|
+
(+offset.slice(1, 3) > 23 || +offset.slice(4) > 59)) {
|
|
70
|
+
return fail("RFC 3339 timestamp");
|
|
71
|
+
}
|
|
72
|
+
const utc = new Date(`${localSeconds}${offset.toUpperCase()}`);
|
|
73
|
+
if (Number.isNaN(utc.getTime()))
|
|
74
|
+
return fail("RFC 3339 timestamp");
|
|
75
|
+
const canonical = utc.toISOString().slice(0, 19);
|
|
76
|
+
if (canonical[0] === "+" || canonical[0] === "-") {
|
|
77
|
+
return fail("RFC 3339 timestamp");
|
|
78
|
+
}
|
|
79
|
+
const digits = fraction?.replace(/0+$/, "");
|
|
80
|
+
return `${canonical}${digits ? `.${digits}` : ""}Z`;
|
|
81
|
+
}
|
|
82
|
+
const ulidPattern = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
|
|
83
|
+
/** Browser-safe codec vocabulary consumed by generated TypeScript packages. */
|
|
84
|
+
export const codecs = Object.freeze({
|
|
85
|
+
string: stringCodec,
|
|
86
|
+
bool: boolCodec,
|
|
87
|
+
i32: integer("i32", -0x8000_0000, 0x7fff_ffff),
|
|
88
|
+
u32: integer("u32", 0, 0xffff_ffff),
|
|
89
|
+
i64: bigintCodec("i64", -(1n << 63n), (1n << 63n) - 1n),
|
|
90
|
+
u64: bigintCodec("u64", 0n, (1n << 64n) - 1n),
|
|
91
|
+
f64: numberCodec,
|
|
92
|
+
bytes: codec(decodeBase64, encodeBase64),
|
|
93
|
+
timestamp: codec(timestamp),
|
|
94
|
+
ulid: codec((value) => typeof value === "string" && ulidPattern.test(value)
|
|
95
|
+
? value
|
|
96
|
+
: fail("canonical ULID")),
|
|
97
|
+
openEnum(values) {
|
|
98
|
+
if (values.some((value) => typeof value !== "string")) {
|
|
99
|
+
fail("generated string enum values");
|
|
100
|
+
}
|
|
101
|
+
return codec((value) => {
|
|
102
|
+
if (typeof value !== "string")
|
|
103
|
+
return fail("string enum value");
|
|
104
|
+
return value;
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
model(fields) {
|
|
108
|
+
return codec((value) => {
|
|
109
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
110
|
+
return fail("object");
|
|
111
|
+
}
|
|
112
|
+
const source = value;
|
|
113
|
+
const decoded = {};
|
|
114
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
115
|
+
if (source[name] === undefined && "optional" in field)
|
|
116
|
+
continue;
|
|
117
|
+
decoded[name] = field.decode(source[name]);
|
|
118
|
+
}
|
|
119
|
+
return decoded;
|
|
120
|
+
}, (value) => {
|
|
121
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
122
|
+
return fail("object");
|
|
123
|
+
}
|
|
124
|
+
const encoded = {};
|
|
125
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
126
|
+
const fieldValue = value[name];
|
|
127
|
+
if (fieldValue === undefined && "optional" in field)
|
|
128
|
+
continue;
|
|
129
|
+
encoded[name] = field.encode(fieldValue);
|
|
130
|
+
}
|
|
131
|
+
return encoded;
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
named(_name, value) {
|
|
135
|
+
return codec((input) => value.decode(input), (input) => value.encode(input));
|
|
136
|
+
},
|
|
137
|
+
list(item) {
|
|
138
|
+
return codec((value) => Array.isArray(value) ? value.map(item.decode) : fail("array"), (value) => Array.isArray(value) ? value.map(item.encode) : fail("array"));
|
|
139
|
+
},
|
|
140
|
+
map(item) {
|
|
141
|
+
return codec((value) => {
|
|
142
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
143
|
+
return fail("object map");
|
|
144
|
+
}
|
|
145
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, item.decode(entry)]));
|
|
146
|
+
}, (value) => Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, item.encode(entry)])));
|
|
147
|
+
},
|
|
148
|
+
nullable(value) {
|
|
149
|
+
return codec((input) => input === null ? null : value.decode(input), (input) => input === null ? null : value.encode(input));
|
|
150
|
+
},
|
|
151
|
+
optional(value) {
|
|
152
|
+
return Object.freeze({
|
|
153
|
+
optional: true,
|
|
154
|
+
decode: (input) => input === undefined ? undefined : value.decode(input),
|
|
155
|
+
encode: (input) => input === undefined ? undefined : value.encode(input),
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
ref(get) {
|
|
159
|
+
return codec((value) => get().decode(value), (value) => get().encode(value));
|
|
160
|
+
},
|
|
161
|
+
recursive(build) {
|
|
162
|
+
let resolved;
|
|
163
|
+
const get = () => {
|
|
164
|
+
resolved ??= build(self);
|
|
165
|
+
return resolved;
|
|
166
|
+
};
|
|
167
|
+
const self = codec((value) => get().decode(value), (value) => get().encode(value));
|
|
168
|
+
return self;
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
function isRecord(value) {
|
|
172
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
173
|
+
}
|
|
174
|
+
function isCodec(value) {
|
|
175
|
+
return isRecord(value) && typeof value.decode === "function" &&
|
|
176
|
+
typeof value.encode === "function";
|
|
177
|
+
}
|
|
178
|
+
function validateAction(name, action) {
|
|
179
|
+
if (!["rpc", "operation", "event", "feed"].includes(String(action.kind)) ||
|
|
180
|
+
action.descriptorName !== name || !name.startsWith(`${action.kind}:`) ||
|
|
181
|
+
name.length === String(action.kind).length + 1) {
|
|
182
|
+
fail("generated action descriptor");
|
|
183
|
+
}
|
|
184
|
+
switch (action.kind) {
|
|
185
|
+
case "rpc":
|
|
186
|
+
if (!isCodec(action.input) || !isCodec(action.output) ||
|
|
187
|
+
!Array.isArray(action.errors) ||
|
|
188
|
+
typeof action.download !== "boolean" ||
|
|
189
|
+
(action.pagination !== undefined && action.pagination !== "cursor"))
|
|
190
|
+
fail("generated RPC descriptor");
|
|
191
|
+
break;
|
|
192
|
+
case "operation":
|
|
193
|
+
if (!isCodec(action.input) || !isCodec(action.output) ||
|
|
194
|
+
(action.progress !== undefined && !isCodec(action.progress)) ||
|
|
195
|
+
!Array.isArray(action.errors) ||
|
|
196
|
+
!isRecord(action.signals) ||
|
|
197
|
+
Object.values(action.signals).some((value) => !isCodec(value)) ||
|
|
198
|
+
typeof action.upload !== "boolean")
|
|
199
|
+
fail("generated operation descriptor");
|
|
200
|
+
break;
|
|
201
|
+
case "event":
|
|
202
|
+
if (!isCodec(action.payload) || !Array.isArray(action.parameters) ||
|
|
203
|
+
action.parameters.some((path) => !Array.isArray(path) || path.some((part) => typeof part !== "string")))
|
|
204
|
+
fail("generated event descriptor");
|
|
205
|
+
break;
|
|
206
|
+
case "feed":
|
|
207
|
+
if (!isCodec(action.input) || !isCodec(action.event)) {
|
|
208
|
+
fail("generated feed descriptor");
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function validateApiDescriptor(descriptor) {
|
|
214
|
+
if (!descriptor || typeof descriptor !== "object" ||
|
|
215
|
+
typeof descriptor.identity !== "string" ||
|
|
216
|
+
descriptor.identity.length === 0 || !isRecord(descriptor.actions)) {
|
|
217
|
+
fail("API descriptor");
|
|
218
|
+
}
|
|
219
|
+
for (const [name, action] of Object.entries(descriptor.actions)) {
|
|
220
|
+
validateAction(name, action);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/** Validate and retain generated API evidence without deriving authority from it. */
|
|
224
|
+
export function apiDescriptor(descriptor) {
|
|
225
|
+
validateApiDescriptor(descriptor);
|
|
226
|
+
for (const action of Object.values(descriptor.actions))
|
|
227
|
+
Object.freeze(action);
|
|
228
|
+
Object.freeze(descriptor.actions);
|
|
229
|
+
return Object.freeze(descriptor);
|
|
230
|
+
}
|
|
231
|
+
function validateResource(resource) {
|
|
232
|
+
if (!["state", "kv", "store", "job", "consumer"].includes(String(resource.kind)) ||
|
|
233
|
+
!["required", "optional"].includes(String(resource.availability)))
|
|
234
|
+
fail("generated resource descriptor");
|
|
235
|
+
if (resource.kind === "state" || resource.kind === "kv") {
|
|
236
|
+
if (!isCodec(resource.codec) || typeof resource.version !== "number" ||
|
|
237
|
+
!Number.isInteger(resource.version) || resource.version <= 0 ||
|
|
238
|
+
!isRecord(resource.migrations))
|
|
239
|
+
fail("generated representation descriptor");
|
|
240
|
+
const currentVersion = resource.version;
|
|
241
|
+
if (Object.entries(resource.migrations).some(([version, value]) => !/^[1-9][0-9]*$/.test(version) || Number(version) >= currentVersion ||
|
|
242
|
+
!isCodec(value)))
|
|
243
|
+
fail("generated representation descriptor");
|
|
244
|
+
}
|
|
245
|
+
else if (resource.kind === "job" &&
|
|
246
|
+
(!isCodec(resource.payload) ||
|
|
247
|
+
(resource.result !== undefined && !isCodec(resource.result)) ||
|
|
248
|
+
(resource.update !== undefined && !isCodec(resource.update)))) {
|
|
249
|
+
fail("generated job descriptor");
|
|
250
|
+
}
|
|
251
|
+
if (resource.kind === "job") {
|
|
252
|
+
if (resource.deadlineMs !== undefined &&
|
|
253
|
+
(!Number.isInteger(resource.deadlineMs) ||
|
|
254
|
+
Number(resource.deadlineMs) <= 0))
|
|
255
|
+
fail("generated job descriptor");
|
|
256
|
+
if (resource.retry !== undefined) {
|
|
257
|
+
if (!isRecord(resource.retry) ||
|
|
258
|
+
!Number.isInteger(resource.retry.attempts) ||
|
|
259
|
+
Number(resource.retry.attempts) <= 0 ||
|
|
260
|
+
!Array.isArray(resource.retry.backoffMs) ||
|
|
261
|
+
resource.retry.backoffMs.length !==
|
|
262
|
+
Number(resource.retry.attempts) - 1 ||
|
|
263
|
+
resource.retry.backoffMs.some((value) => !Number.isInteger(value) || Number(value) <= 0))
|
|
264
|
+
fail("generated job descriptor");
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function validatePackageEvidence(evidence) {
|
|
269
|
+
if (!isRecord(evidence) || typeof evidence.rootPackage !== "string" ||
|
|
270
|
+
evidence.rootPackage.length === 0 ||
|
|
271
|
+
typeof evidence.rootDigest !== "string" ||
|
|
272
|
+
evidence.rootDigest.length === 0 || !Array.isArray(evidence.packages))
|
|
273
|
+
fail("package evidence");
|
|
274
|
+
let previous = "";
|
|
275
|
+
let rootFound = false;
|
|
276
|
+
for (const entry of evidence.packages) {
|
|
277
|
+
if (!isRecord(entry) || typeof entry.name !== "string" ||
|
|
278
|
+
entry.name.length === 0 || entry.name <= previous ||
|
|
279
|
+
typeof entry.version !== "string" || entry.version.length === 0 ||
|
|
280
|
+
typeof entry.digest !== "string" || entry.digest.length === 0 ||
|
|
281
|
+
typeof entry.source !== "string" || entry.source.length === 0)
|
|
282
|
+
fail("sorted package evidence");
|
|
283
|
+
previous = entry.name;
|
|
284
|
+
if (entry.name === evidence.rootPackage) {
|
|
285
|
+
if (entry.digest !== evidence.rootDigest) {
|
|
286
|
+
fail("matching root package evidence");
|
|
287
|
+
}
|
|
288
|
+
rootFound = true;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (!rootFound)
|
|
292
|
+
fail("root package evidence");
|
|
293
|
+
}
|
|
294
|
+
/** Validate and retain generated participant evidence without resolving grants or authority. */
|
|
295
|
+
export function participantDescriptor(descriptor) {
|
|
296
|
+
if (!descriptor || typeof descriptor !== "object" ||
|
|
297
|
+
!["service", "device", "app", "agent"].includes(descriptor.kind) ||
|
|
298
|
+
descriptor.id !== descriptor.identity ||
|
|
299
|
+
typeof descriptor.identity !== "string" ||
|
|
300
|
+
descriptor.identity.length === 0 ||
|
|
301
|
+
typeof descriptor.path !== "string" || descriptor.path.length === 0 ||
|
|
302
|
+
!isRecord(descriptor.packageEvidence) ||
|
|
303
|
+
typeof descriptor.packageEvidence.rootPackage !== "string" ||
|
|
304
|
+
descriptor.identity !==
|
|
305
|
+
`${descriptor.packageEvidence.rootPackage}.${descriptor.path}` ||
|
|
306
|
+
!Array.isArray(descriptor.implements) || !Array.isArray(descriptor.uses) ||
|
|
307
|
+
!isRecord(descriptor.actionNames) ||
|
|
308
|
+
!isRecord(descriptor.resources)) {
|
|
309
|
+
fail("participant descriptor");
|
|
310
|
+
}
|
|
311
|
+
for (const api of descriptor.implements)
|
|
312
|
+
validateApiDescriptor(api);
|
|
313
|
+
for (const selection of descriptor.uses) {
|
|
314
|
+
if (!isRecord(selection) || !isRecord(selection.api) ||
|
|
315
|
+
!Array.isArray(selection.actions) ||
|
|
316
|
+
!Array.isArray(selection.optionalCapabilities))
|
|
317
|
+
fail("generated action selection");
|
|
318
|
+
const api = selection.api;
|
|
319
|
+
validateApiDescriptor(api);
|
|
320
|
+
for (const selected of selection.actions) {
|
|
321
|
+
if (!isRecord(selected) || typeof selected.descriptorName !== "string" ||
|
|
322
|
+
!(selected.descriptorName in api.actions))
|
|
323
|
+
fail("generated action selection");
|
|
324
|
+
const kind = api.actions[selected.descriptorName].kind;
|
|
325
|
+
if ((kind === "rpc" && selected.direction !== "call") ||
|
|
326
|
+
(kind === "operation" && selected.direction !== "invoke") ||
|
|
327
|
+
(kind === "event" && selected.direction !== "publish" &&
|
|
328
|
+
selected.direction !== "subscribe") ||
|
|
329
|
+
(kind === "feed" && selected.direction !== "subscribe"))
|
|
330
|
+
fail("generated action direction");
|
|
331
|
+
}
|
|
332
|
+
if (selection.optionalCapabilities.some((value) => typeof value !== "string")) {
|
|
333
|
+
fail("generated action selection");
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
for (const resource of Object.values(descriptor.resources)) {
|
|
337
|
+
validateResource(resource);
|
|
338
|
+
Object.freeze(resource);
|
|
339
|
+
}
|
|
340
|
+
if (descriptor.companion) {
|
|
341
|
+
if (descriptor.kind !== "device" ||
|
|
342
|
+
!isRecord(descriptor.companion.participant) ||
|
|
343
|
+
!["required", "optional"].includes(descriptor.companion.availability) ||
|
|
344
|
+
!["app", "agent"].includes(descriptor.companion.participant.kind) ||
|
|
345
|
+
descriptor.companion.participant.identity.split(".").slice(0, -1).join(".") !== descriptor.identity ||
|
|
346
|
+
descriptor.companion.participant.companion !== undefined) {
|
|
347
|
+
fail("companion descriptor");
|
|
348
|
+
}
|
|
349
|
+
participantDescriptor(descriptor.companion.participant);
|
|
350
|
+
Object.freeze(descriptor.companion);
|
|
351
|
+
}
|
|
352
|
+
validatePackageEvidence(descriptor.packageEvidence);
|
|
353
|
+
Object.freeze(descriptor.implements);
|
|
354
|
+
Object.freeze(descriptor.uses);
|
|
355
|
+
Object.freeze(descriptor.resources);
|
|
356
|
+
Object.freeze(descriptor.packageEvidence.packages);
|
|
357
|
+
Object.freeze(descriptor.packageEvidence);
|
|
358
|
+
return Object.freeze(descriptor);
|
|
359
|
+
}
|
package/globals.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type LoggerLike = {
|
|
2
|
+
child(bindings: Record<string, unknown>): LoggerLike;
|
|
3
|
+
trace(...args: unknown[]): void;
|
|
4
|
+
debug(...args: unknown[]): void;
|
|
5
|
+
info(...args: unknown[]): void;
|
|
6
|
+
warn(...args: unknown[]): void;
|
|
7
|
+
error(...args: unknown[]): void;
|
|
8
|
+
};
|
|
9
|
+
export declare const logger: LoggerLike;
|
package/globals.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function createNoopLogger() {
|
|
2
|
+
const noop = () => { };
|
|
3
|
+
const logger = {
|
|
4
|
+
child: () => logger,
|
|
5
|
+
trace: noop,
|
|
6
|
+
debug: noop,
|
|
7
|
+
info: noop,
|
|
8
|
+
warn: noop,
|
|
9
|
+
error: noop,
|
|
10
|
+
};
|
|
11
|
+
return logger;
|
|
12
|
+
}
|
|
13
|
+
// Keep the root package browser-safe: importing `@oatscenter/trellis` must not
|
|
14
|
+
// pull in `pino` or Node-only side effects. Server-only entrypoints can inject
|
|
15
|
+
// a real logger, while the shared runtime defaults to a no-op logger here.
|
|
16
|
+
export const logger = createNoopLogger();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { NatsConnection } from "@nats-io/nats-core";
|
|
2
|
+
import { type HealthHeartbeatSample } from "./internal_sdk/generated/types/_internal/p0.js";
|
|
3
|
+
export type HealthHeartbeatSubjectIdentity = {
|
|
4
|
+
sessionKey: string;
|
|
5
|
+
participantKind: "service" | "device";
|
|
6
|
+
contractId: string;
|
|
7
|
+
contractDigest: string;
|
|
8
|
+
deploymentId: string;
|
|
9
|
+
instanceId: string;
|
|
10
|
+
};
|
|
11
|
+
/** Builds the exact runtime subject authorized for one heartbeat publisher. */
|
|
12
|
+
export declare function healthHeartbeatSubject(identity: HealthHeartbeatSubjectIdentity): string;
|
|
13
|
+
/** Publishes one validated-size heartbeat sample to its dedicated stream. */
|
|
14
|
+
export declare function publishHealthHeartbeatSample(args: {
|
|
15
|
+
nc: NatsConnection;
|
|
16
|
+
identity: HealthHeartbeatSubjectIdentity;
|
|
17
|
+
sample: HealthHeartbeatSample;
|
|
18
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jetstream } from "@nats-io/jetstream";
|
|
2
|
+
import { base64urlEncode } from "./auth/utils.js";
|
|
3
|
+
import { HealthHeartbeatSampleCodec, } from "./internal_sdk/generated/types/_internal/p0.js";
|
|
4
|
+
const HEALTH_HEARTBEAT_SUBJECT_PREFIX = "health.v1.heartbeat";
|
|
5
|
+
function subjectToken(value) {
|
|
6
|
+
if (value.length === 0)
|
|
7
|
+
throw new Error("health subject identity is empty");
|
|
8
|
+
return base64urlEncode(new TextEncoder().encode(value));
|
|
9
|
+
}
|
|
10
|
+
/** Builds the exact runtime subject authorized for one heartbeat publisher. */
|
|
11
|
+
export function healthHeartbeatSubject(identity) {
|
|
12
|
+
if (!/^[A-Za-z0-9_-]+$/.test(identity.sessionKey)) {
|
|
13
|
+
throw new Error("health session key is not canonical base64url");
|
|
14
|
+
}
|
|
15
|
+
return [
|
|
16
|
+
HEALTH_HEARTBEAT_SUBJECT_PREFIX,
|
|
17
|
+
identity.participantKind,
|
|
18
|
+
subjectToken(identity.contractId),
|
|
19
|
+
subjectToken(identity.contractDigest),
|
|
20
|
+
subjectToken(identity.deploymentId),
|
|
21
|
+
subjectToken(identity.instanceId),
|
|
22
|
+
identity.sessionKey,
|
|
23
|
+
].join(".");
|
|
24
|
+
}
|
|
25
|
+
/** Publishes one validated-size heartbeat sample to its dedicated stream. */
|
|
26
|
+
export async function publishHealthHeartbeatSample(args) {
|
|
27
|
+
const payload = new TextEncoder().encode(JSON.stringify(HealthHeartbeatSampleCodec.encode(args.sample)));
|
|
28
|
+
if (payload.byteLength > 65_536) {
|
|
29
|
+
throw new Error("health heartbeat sample exceeds 64 KiB");
|
|
30
|
+
}
|
|
31
|
+
await jetstream(args.nc).publish(healthHeartbeatSubject(args.identity), payload, { msgID: args.sample.sample.id });
|
|
32
|
+
}
|
package/helpers.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Encodes one canonical event parameter value as a NATS subject token. */
|
|
2
|
+
export declare function encodeEventSubjectParameterToken(value: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Compute the subject template from the message itself
|
|
5
|
+
*/
|
|
6
|
+
export declare function template(template: string, data: Record<string, string | number>, opts?: {
|
|
7
|
+
allowWildcards: boolean;
|
|
8
|
+
}): string;
|
|
9
|
+
export declare function escapeNats(token: string): string;
|
|
10
|
+
export declare function escapeKvKey(key: string): string;
|
|
11
|
+
export declare function decodeSubject(token: string): string;
|
package/helpers.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Pointer } from "typebox/value";
|
|
2
|
+
import { base64urlEncode, utf8 } from "./auth/utils.js";
|
|
3
|
+
/** Encodes one canonical event parameter value as a NATS subject token. */
|
|
4
|
+
export function encodeEventSubjectParameterToken(value) {
|
|
5
|
+
return base64urlEncode(utf8(value));
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Compute the subject template from the message itself
|
|
9
|
+
*/
|
|
10
|
+
export function template(template, data, opts = { allowWildcards: false }) {
|
|
11
|
+
return template.replace(/\{([^}]+)\}/g, (_, key) => {
|
|
12
|
+
const token = Pointer.Get(data, key);
|
|
13
|
+
if (token === undefined || token === null) {
|
|
14
|
+
if (!opts.allowWildcards) {
|
|
15
|
+
throw new Error("All option templates must have values at runtime.");
|
|
16
|
+
}
|
|
17
|
+
return "*";
|
|
18
|
+
}
|
|
19
|
+
if (typeof token !== "string" && typeof token !== "number") {
|
|
20
|
+
throw new Error("Subject template values must be strings or numbers.");
|
|
21
|
+
}
|
|
22
|
+
if (typeof token === "number" &&
|
|
23
|
+
(!Number.isFinite(token) ||
|
|
24
|
+
(Number.isInteger(token) && !Number.isSafeInteger(token)))) {
|
|
25
|
+
throw new Error("Subject template numbers must be finite and safe integers.");
|
|
26
|
+
}
|
|
27
|
+
return encodeEventSubjectParameterToken(`${Object.is(token, -0) ? 0 : token}`);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const NATS_SUBJECT_TOKEN_FORBIDDEN = /[\u0000\s.*>~]/gu;
|
|
31
|
+
export function escapeNats(token) {
|
|
32
|
+
const out = token.replace(NATS_SUBJECT_TOKEN_FORBIDDEN, (ch) => `~${ch.codePointAt(0).toString(16).toUpperCase()}~`);
|
|
33
|
+
// Protect start with $ due to NATS internal use of it
|
|
34
|
+
if (out.length === 0 || out.startsWith("$")) {
|
|
35
|
+
return `_${out}`;
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
const NATS_KV_KEY_FORBIDDEN = /[\u0000\s*>~]/gu;
|
|
40
|
+
export function escapeKvKey(key) {
|
|
41
|
+
const out = key.replace(NATS_KV_KEY_FORBIDDEN, (ch) => `~${ch.codePointAt(0).toString(16).toUpperCase()}~`);
|
|
42
|
+
if (out.length === 0 || out.startsWith("$")) {
|
|
43
|
+
return `_${out}`;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
export function decodeSubject(token) {
|
|
48
|
+
const out = token.replace(/~([0-9A-F]{1,6})~/g, (_, hex) => String.fromCodePoint(Number.parseInt(hex, 16)));
|
|
49
|
+
if (out.startsWith("_$")) {
|
|
50
|
+
return out.slice(1);
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
package/index.d.ts
ADDED
package/index.js
ADDED