@qlever-llc/trellis 0.10.12 → 0.10.14
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/esm/auth/browser.js +0 -1
- package/esm/auth.js +0 -1
- package/esm/browser.d.ts +29 -2
- package/esm/browser.d.ts.map +1 -1
- package/esm/browser.js +14 -3
- package/esm/client_connect.d.ts.map +1 -1
- package/esm/client_connect.js +7 -3
- package/esm/contract_support/mod.d.ts +6 -10
- package/esm/contract_support/mod.d.ts.map +1 -1
- package/esm/contract_support/mod.js +42 -31
- package/esm/contract_support/protocol.d.ts +8 -13
- package/esm/contract_support/protocol.d.ts.map +1 -1
- package/esm/contract_support/protocol.js +4 -5
- package/esm/contract_support/schema_pointers.d.ts +6 -0
- package/esm/contract_support/schema_pointers.d.ts.map +1 -1
- package/esm/contract_support/schema_pointers.js +59 -7
- package/esm/contracts.js +0 -1
- package/esm/device.d.ts +0 -29
- package/esm/device.d.ts.map +1 -1
- package/esm/device.js +10 -1
- package/esm/errors/index.js +0 -1
- package/esm/index.d.ts +3 -4
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +3 -4
- package/esm/runtime_transport.d.ts.map +1 -1
- package/esm/runtime_transport.js +4 -2
- package/esm/server/health_rpc.d.ts +4 -2
- package/esm/server/health_rpc.d.ts.map +1 -1
- package/esm/server/health_rpc.js +6 -1
- package/esm/server/service.d.ts +12 -37
- package/esm/server/service.d.ts.map +1 -1
- package/esm/server/service.js +138 -103
- package/esm/server.js +3 -3
- package/esm/service/outbox_inbox.d.ts +1 -6
- package/esm/service/outbox_inbox.d.ts.map +1 -1
- package/esm/service/outbox_inbox.js +0 -21
- package/esm/telemetry/env.d.ts.map +1 -1
- package/esm/telemetry/env.js +5 -6
- package/esm/telemetry/metrics.d.ts +0 -7
- package/esm/telemetry/metrics.d.ts.map +1 -1
- package/esm/trellis.d.ts +1 -19
- package/esm/trellis.d.ts.map +1 -1
- package/esm/trellis.js +12 -8
- package/package.json +2 -2
- package/script/auth/browser.js +0 -1
- package/script/auth.js +0 -1
- package/script/browser.d.ts +29 -2
- package/script/browser.d.ts.map +1 -1
- package/script/browser.js +75 -17
- package/script/client_connect.d.ts.map +1 -1
- package/script/client_connect.js +7 -36
- package/script/contract_support/mod.d.ts +6 -10
- package/script/contract_support/mod.d.ts.map +1 -1
- package/script/contract_support/mod.js +46 -32
- package/script/contract_support/protocol.d.ts +8 -13
- package/script/contract_support/protocol.d.ts.map +1 -1
- package/script/contract_support/protocol.js +5 -6
- package/script/contract_support/schema_pointers.d.ts +6 -0
- package/script/contract_support/schema_pointers.d.ts.map +1 -1
- package/script/contract_support/schema_pointers.js +59 -7
- package/script/contracts.js +0 -1
- package/script/device.d.ts +0 -29
- package/script/device.d.ts.map +1 -1
- package/script/device.js +10 -1
- package/script/errors/index.js +0 -1
- package/script/index.d.ts +3 -4
- package/script/index.d.ts.map +1 -1
- package/script/index.js +1 -6
- package/script/runtime_transport.d.ts.map +1 -1
- package/script/runtime_transport.js +4 -2
- package/script/server/health_rpc.d.ts +4 -2
- package/script/server/health_rpc.d.ts.map +1 -1
- package/script/server/health_rpc.js +6 -1
- package/script/server/service.d.ts +12 -37
- package/script/server/service.d.ts.map +1 -1
- package/script/server/service.js +148 -112
- package/script/server.js +3 -3
- package/script/service/outbox_inbox.d.ts +1 -6
- package/script/service/outbox_inbox.d.ts.map +1 -1
- package/script/service/outbox_inbox.js +0 -21
- package/script/telemetry/env.d.ts.map +1 -1
- package/script/telemetry/env.js +5 -39
- package/script/telemetry/metrics.d.ts +0 -7
- package/script/telemetry/metrics.d.ts.map +1 -1
- package/script/trellis.d.ts +1 -19
- package/script/trellis.d.ts.map +1 -1
- package/script/trellis.js +12 -8
- package/src/browser.ts +200 -2
- package/src/client_connect.ts +10 -2
- package/src/contract_support/mod.ts +73 -45
- package/src/contract_support/protocol.ts +16 -7
- package/src/contract_support/schema_pointers.ts +80 -7
- package/src/device.ts +10 -2
- package/src/index.ts +3 -4
- package/src/runtime_transport.ts +6 -1
- package/src/server/health_rpc.ts +7 -2
- package/src/server/service.ts +177 -126
- package/src/server.ts +3 -3
- package/src/service/outbox_inbox.ts +1 -28
- package/src/telemetry/env.ts +10 -7
- package/src/trellis.ts +22 -13
|
@@ -57,7 +57,6 @@ import {
|
|
|
57
57
|
type SubjectParam,
|
|
58
58
|
} from "./schema_pointers.js";
|
|
59
59
|
import {
|
|
60
|
-
type ContractEventConsumerEvent,
|
|
61
60
|
type ContractEventConsumerGroup,
|
|
62
61
|
type ContractEventConsumers,
|
|
63
62
|
ContractEventConsumersSchema,
|
|
@@ -70,12 +69,14 @@ import {
|
|
|
70
69
|
export {
|
|
71
70
|
buildCursorPage,
|
|
72
71
|
buildPageResponse,
|
|
73
|
-
type ContractEventConsumerEvent,
|
|
74
|
-
ContractEventConsumerEventSchema,
|
|
75
72
|
type ContractEventConsumerGroup,
|
|
76
73
|
ContractEventConsumerGroupSchema,
|
|
77
74
|
type ContractEventConsumers,
|
|
75
|
+
type ContractEventConsumerSelf,
|
|
76
|
+
ContractEventConsumerSelfSchema,
|
|
78
77
|
ContractEventConsumersSchema,
|
|
78
|
+
type ContractEventConsumerUses,
|
|
79
|
+
ContractEventConsumerUsesSchema,
|
|
79
80
|
ContractJobQueueSchema,
|
|
80
81
|
ContractJobsSchema,
|
|
81
82
|
ContractKvResourceSchema,
|
|
@@ -115,6 +116,10 @@ export {
|
|
|
115
116
|
type StoreResourceBinding,
|
|
116
117
|
StoreResourceBindingSchema,
|
|
117
118
|
} from "./protocol.js";
|
|
119
|
+
export {
|
|
120
|
+
assertDataPointersExistAndAreTokenable,
|
|
121
|
+
getSubschemaAtDataPointer,
|
|
122
|
+
} from "./schema_pointers.js";
|
|
118
123
|
|
|
119
124
|
export const CONTRACT_FORMAT_V1 = "trellis.contract.v1" as const;
|
|
120
125
|
export const CATALOG_FORMAT_V1 = "trellis.catalog.v1" as const;
|
|
@@ -964,13 +969,9 @@ export type ContractSourceJobs<TSchemaName extends string = string> = Record<
|
|
|
964
969
|
ContractSourceJobQueue<TSchemaName>
|
|
965
970
|
>;
|
|
966
971
|
|
|
967
|
-
export type ContractSourceEventConsumerEvent = {
|
|
968
|
-
use: string;
|
|
969
|
-
event: string;
|
|
970
|
-
};
|
|
971
|
-
|
|
972
972
|
export type ContractSourceEventConsumerGroup = {
|
|
973
|
-
|
|
973
|
+
uses?: Record<string, readonly string[]>;
|
|
974
|
+
self?: readonly string[];
|
|
974
975
|
replay?: "new" | "all";
|
|
975
976
|
ordering?: "strict";
|
|
976
977
|
concurrency?: number;
|
|
@@ -2637,7 +2638,9 @@ function projectDigestJobs(
|
|
|
2637
2638
|
function projectDigestEventConsumers(
|
|
2638
2639
|
eventConsumers: ContractEventConsumers | undefined,
|
|
2639
2640
|
): ContractEventConsumers | undefined {
|
|
2640
|
-
return eventConsumers
|
|
2641
|
+
return eventConsumers
|
|
2642
|
+
? mapValues(eventConsumers, projectDigestEventConsumerGroup)
|
|
2643
|
+
: undefined;
|
|
2641
2644
|
}
|
|
2642
2645
|
|
|
2643
2646
|
function projectDigestUsesFlat(
|
|
@@ -3068,25 +3071,31 @@ function feed(feed: ContractFeed): ContractFeed {
|
|
|
3068
3071
|
};
|
|
3069
3072
|
}
|
|
3070
3073
|
|
|
3071
|
-
function
|
|
3072
|
-
|
|
3073
|
-
):
|
|
3074
|
-
|
|
3074
|
+
function sortEventConsumerUses(
|
|
3075
|
+
uses: Record<string, readonly string[]> | undefined,
|
|
3076
|
+
): Record<string, string[]> | undefined {
|
|
3077
|
+
if (!uses) {
|
|
3078
|
+
return undefined;
|
|
3079
|
+
}
|
|
3080
|
+
return Object.fromEntries(
|
|
3081
|
+
Object.entries(uses)
|
|
3082
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
3083
|
+
.map(([alias, events]) => [alias, sortedUnique(events)]),
|
|
3084
|
+
);
|
|
3075
3085
|
}
|
|
3076
3086
|
|
|
3077
|
-
function
|
|
3078
|
-
|
|
3079
|
-
):
|
|
3080
|
-
return
|
|
3081
|
-
left.use.localeCompare(right.use) || left.event.localeCompare(right.event)
|
|
3082
|
-
);
|
|
3087
|
+
function sortEventConsumerSelf(
|
|
3088
|
+
self: readonly string[] | undefined,
|
|
3089
|
+
): string[] | undefined {
|
|
3090
|
+
return self ? sortedUnique(self) : undefined;
|
|
3083
3091
|
}
|
|
3084
3092
|
|
|
3085
3093
|
function eventConsumerGroup(
|
|
3086
|
-
group:
|
|
3094
|
+
group: ContractSourceEventConsumerGroup,
|
|
3087
3095
|
): ContractEventConsumerGroup {
|
|
3088
3096
|
return {
|
|
3089
|
-
|
|
3097
|
+
...(group.uses ? { uses: sortEventConsumerUses(group.uses) } : {}),
|
|
3098
|
+
...(group.self ? { self: sortEventConsumerSelf(group.self) } : {}),
|
|
3090
3099
|
replay: group.replay ?? "new",
|
|
3091
3100
|
ordering: group.ordering ?? "strict",
|
|
3092
3101
|
concurrency: group.concurrency ?? 1,
|
|
@@ -3097,6 +3106,12 @@ function eventConsumerGroup(
|
|
|
3097
3106
|
};
|
|
3098
3107
|
}
|
|
3099
3108
|
|
|
3109
|
+
function projectDigestEventConsumerGroup(
|
|
3110
|
+
group: ContractEventConsumerGroup,
|
|
3111
|
+
): ContractEventConsumerGroup {
|
|
3112
|
+
return omitDocs(eventConsumerGroup(group));
|
|
3113
|
+
}
|
|
3114
|
+
|
|
3100
3115
|
function errorDecl(error: ContractErrorDecl): ContractErrorDecl {
|
|
3101
3116
|
return {
|
|
3102
3117
|
type: error.type,
|
|
@@ -3240,7 +3255,11 @@ export function parseContractManifest(value: unknown): TrellisContractV1 {
|
|
|
3240
3255
|
);
|
|
3241
3256
|
}
|
|
3242
3257
|
const contract = normalizeContractManifest(parsed);
|
|
3243
|
-
assertValidEventConsumers(
|
|
3258
|
+
assertValidEventConsumers(
|
|
3259
|
+
contract.eventConsumers,
|
|
3260
|
+
contract.uses,
|
|
3261
|
+
contract.events,
|
|
3262
|
+
);
|
|
3244
3263
|
return contract;
|
|
3245
3264
|
}
|
|
3246
3265
|
|
|
@@ -3408,20 +3427,7 @@ function emitEventConsumers(
|
|
|
3408
3427
|
return Object.fromEntries(
|
|
3409
3428
|
Object.entries(eventConsumers).map(([groupName, group]) => [
|
|
3410
3429
|
groupName,
|
|
3411
|
-
|
|
3412
|
-
events: sortEventConsumerEvents(group.events),
|
|
3413
|
-
replay: group.replay ?? "new",
|
|
3414
|
-
ordering: group.ordering ?? "strict",
|
|
3415
|
-
concurrency: group.concurrency ?? 1,
|
|
3416
|
-
...(group.ackWaitMs !== undefined
|
|
3417
|
-
? { ackWaitMs: group.ackWaitMs }
|
|
3418
|
-
: {}),
|
|
3419
|
-
...(group.maxDeliver !== undefined
|
|
3420
|
-
? { maxDeliver: group.maxDeliver }
|
|
3421
|
-
: {}),
|
|
3422
|
-
...(group.backoffMs ? { backoffMs: [...group.backoffMs] } : {}),
|
|
3423
|
-
...(group.docs ? { docs: contractDocs(group.docs) } : {}),
|
|
3424
|
-
} satisfies ContractEventConsumerGroup,
|
|
3430
|
+
eventConsumerGroup(group),
|
|
3425
3431
|
]),
|
|
3426
3432
|
);
|
|
3427
3433
|
}
|
|
@@ -3840,7 +3846,7 @@ function emitContract(source: TrellisContractSource): TrellisContractV1 {
|
|
|
3840
3846
|
const state = emitState(source.state);
|
|
3841
3847
|
const resources = emitResources(source.resources);
|
|
3842
3848
|
const uses = emitUses(source.uses);
|
|
3843
|
-
assertValidEventConsumers(eventConsumers, uses);
|
|
3849
|
+
assertValidEventConsumers(eventConsumers, uses, events);
|
|
3844
3850
|
|
|
3845
3851
|
return {
|
|
3846
3852
|
format: CONTRACT_FORMAT_V1,
|
|
@@ -4151,15 +4157,20 @@ function contractUseByAlias(
|
|
|
4151
4157
|
function assertValidEventConsumers(
|
|
4152
4158
|
eventConsumers: ContractEventConsumers | undefined,
|
|
4153
4159
|
uses: ContractUses | undefined,
|
|
4160
|
+
ownedEvents: Record<string, ContractEvent> | undefined,
|
|
4154
4161
|
): void {
|
|
4155
4162
|
if (!eventConsumers) {
|
|
4156
4163
|
return;
|
|
4157
4164
|
}
|
|
4158
4165
|
|
|
4159
4166
|
for (const [groupName, group] of Object.entries(eventConsumers)) {
|
|
4160
|
-
|
|
4167
|
+
const hasDependencyEvents = Object.values(group.uses ?? {}).some(
|
|
4168
|
+
(eventNames) => eventNames.length > 0,
|
|
4169
|
+
);
|
|
4170
|
+
const hasSelfEvents = (group.self?.length ?? 0) > 0;
|
|
4171
|
+
if (!hasDependencyEvents && !hasSelfEvents) {
|
|
4161
4172
|
throw new Error(
|
|
4162
|
-
`event consumer group '${groupName}' must declare
|
|
4173
|
+
`event consumer group '${groupName}' must declare at least one dependency or self event`,
|
|
4163
4174
|
);
|
|
4164
4175
|
}
|
|
4165
4176
|
if ((group.ordering ?? "strict") === "strict" && group.concurrency !== 1) {
|
|
@@ -4168,16 +4179,33 @@ function assertValidEventConsumers(
|
|
|
4168
4179
|
);
|
|
4169
4180
|
}
|
|
4170
4181
|
|
|
4171
|
-
for (const
|
|
4172
|
-
|
|
4182
|
+
for (const [alias, eventNames] of Object.entries(group.uses ?? {})) {
|
|
4183
|
+
if (eventNames.length === 0) {
|
|
4184
|
+
throw new Error(
|
|
4185
|
+
`event consumer group '${groupName}' use '${alias}' must declare events`,
|
|
4186
|
+
);
|
|
4187
|
+
}
|
|
4188
|
+
|
|
4189
|
+
const use = contractUseByAlias(uses, alias);
|
|
4173
4190
|
if (!use) {
|
|
4174
4191
|
throw new Error(
|
|
4175
|
-
`event consumer group '${groupName}' references unknown use '${
|
|
4192
|
+
`event consumer group '${groupName}' references unknown use '${alias}'`,
|
|
4176
4193
|
);
|
|
4177
4194
|
}
|
|
4178
|
-
|
|
4195
|
+
|
|
4196
|
+
for (const eventName of eventNames) {
|
|
4197
|
+
if (!use.events?.subscribe?.includes(eventName)) {
|
|
4198
|
+
throw new Error(
|
|
4199
|
+
`event consumer group '${groupName}' references event '${eventName}' that use '${alias}' does not subscribe to`,
|
|
4200
|
+
);
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
|
|
4205
|
+
for (const eventName of group.self ?? []) {
|
|
4206
|
+
if (!ownedEvents || !Object.hasOwn(ownedEvents, eventName)) {
|
|
4179
4207
|
throw new Error(
|
|
4180
|
-
`event consumer group '${groupName}' references
|
|
4208
|
+
`event consumer group '${groupName}' references unknown owned event '${eventName}'`,
|
|
4181
4209
|
);
|
|
4182
4210
|
}
|
|
4183
4211
|
}
|
|
@@ -99,17 +99,26 @@ export const ContractJobsSchema = Type.Record(
|
|
|
99
99
|
|
|
100
100
|
export type ContractJobs = Static<typeof ContractJobsSchema>;
|
|
101
101
|
|
|
102
|
-
export const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
export const ContractEventConsumerUsesSchema = Type.Record(
|
|
103
|
+
Type.String({ minLength: 1 }),
|
|
104
|
+
Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
export type ContractEventConsumerUses = Static<
|
|
108
|
+
typeof ContractEventConsumerUsesSchema
|
|
109
|
+
>;
|
|
110
|
+
|
|
111
|
+
export const ContractEventConsumerSelfSchema = Type.Array(
|
|
112
|
+
Type.String({ minLength: 1 }),
|
|
113
|
+
);
|
|
106
114
|
|
|
107
|
-
export type
|
|
108
|
-
typeof
|
|
115
|
+
export type ContractEventConsumerSelf = Static<
|
|
116
|
+
typeof ContractEventConsumerSelfSchema
|
|
109
117
|
>;
|
|
110
118
|
|
|
111
119
|
export const ContractEventConsumerGroupSchema = Type.Object({
|
|
112
|
-
|
|
120
|
+
uses: Type.Optional(ContractEventConsumerUsesSchema),
|
|
121
|
+
self: Type.Optional(ContractEventConsumerSelfSchema),
|
|
113
122
|
replay: Type.Optional(Type.Union([
|
|
114
123
|
Type.Literal("new"),
|
|
115
124
|
Type.Literal("all"),
|
|
@@ -62,7 +62,71 @@ function decodeJsonPointerSegment(segment: string): string {
|
|
|
62
62
|
return segment.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
type PointerResolution = {
|
|
66
|
+
found: boolean;
|
|
67
|
+
schemas: unknown[];
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function pointerResolution(
|
|
71
|
+
found: boolean,
|
|
72
|
+
schemas: unknown[],
|
|
73
|
+
): PointerResolution {
|
|
74
|
+
return { found, schemas };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function collectSubschemas(
|
|
78
|
+
schema: unknown,
|
|
79
|
+
segments: readonly string[],
|
|
80
|
+
): PointerResolution {
|
|
81
|
+
if (segments.length === 0) return pointerResolution(true, [schema]);
|
|
82
|
+
if (!isJsonObject(schema)) return pointerResolution(false, []);
|
|
83
|
+
|
|
84
|
+
const resolved: unknown[] = [];
|
|
85
|
+
let found = false;
|
|
86
|
+
|
|
87
|
+
const properties = schema.properties;
|
|
88
|
+
const segment = segments[0];
|
|
89
|
+
if (
|
|
90
|
+
segment !== undefined && isJsonObject(properties) &&
|
|
91
|
+
Object.hasOwn(properties, segment)
|
|
92
|
+
) {
|
|
93
|
+
const direct = collectSubschemas(properties[segment], segments.slice(1));
|
|
94
|
+
found = found || direct.found;
|
|
95
|
+
resolved.push(...direct.schemas);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const allOf = schema.allOf;
|
|
99
|
+
if (Array.isArray(allOf)) {
|
|
100
|
+
for (const branch of allOf) {
|
|
101
|
+
const branchResult = collectSubschemas(branch, segments);
|
|
102
|
+
found = found || branchResult.found;
|
|
103
|
+
resolved.push(...branchResult.schemas);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const key of ["anyOf", "oneOf"] as const) {
|
|
108
|
+
const variants = schema[key];
|
|
109
|
+
if (!Array.isArray(variants) || variants.length === 0) continue;
|
|
110
|
+
|
|
111
|
+
const variantSchemas: unknown[] = [];
|
|
112
|
+
let everyVariantResolved = true;
|
|
113
|
+
for (const variant of variants) {
|
|
114
|
+
const variantResult = collectSubschemas(variant, segments);
|
|
115
|
+
if (!variantResult.found) {
|
|
116
|
+
everyVariantResolved = false;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
variantSchemas.push(...variantResult.schemas);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (everyVariantResolved) found = true;
|
|
123
|
+
resolved.push(...variantSchemas);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return pointerResolution(found, resolved);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function resolveFirstSubschema(
|
|
66
130
|
schema: unknown,
|
|
67
131
|
segments: readonly string[],
|
|
68
132
|
): unknown | undefined {
|
|
@@ -73,7 +137,7 @@ function resolveSubschema(
|
|
|
73
137
|
const variants = schema[key];
|
|
74
138
|
if (!Array.isArray(variants)) continue;
|
|
75
139
|
for (const variant of variants) {
|
|
76
|
-
const resolved =
|
|
140
|
+
const resolved = resolveFirstSubschema(variant, segments);
|
|
77
141
|
if (resolved !== undefined) return resolved;
|
|
78
142
|
}
|
|
79
143
|
}
|
|
@@ -85,7 +149,7 @@ function resolveSubschema(
|
|
|
85
149
|
if (!isJsonObject(properties) || !Object.hasOwn(properties, segment)) {
|
|
86
150
|
return undefined;
|
|
87
151
|
}
|
|
88
|
-
return
|
|
152
|
+
return resolveFirstSubschema(properties[segment], segments.slice(1));
|
|
89
153
|
}
|
|
90
154
|
|
|
91
155
|
function isTokenableSchema(schema: unknown): boolean {
|
|
@@ -131,30 +195,39 @@ function isTokenableSchema(schema: unknown): boolean {
|
|
|
131
195
|
return false;
|
|
132
196
|
}
|
|
133
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Returns the first schema node reachable by following a payload JSON Pointer.
|
|
200
|
+
*/
|
|
134
201
|
export function getSubschemaAtDataPointer(
|
|
135
202
|
schema: unknown,
|
|
136
203
|
pointer: string,
|
|
137
204
|
): unknown | undefined {
|
|
138
|
-
return
|
|
205
|
+
return resolveFirstSubschema(
|
|
139
206
|
schema,
|
|
140
207
|
pointer.slice(1).split("/").map(decodeJsonPointerSegment),
|
|
141
208
|
);
|
|
142
209
|
}
|
|
143
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Verifies that event subject parameter pointers resolve to tokenable schemas.
|
|
213
|
+
*/
|
|
144
214
|
export function assertDataPointersExistAndAreTokenable(
|
|
145
215
|
name: string,
|
|
146
216
|
schema: unknown,
|
|
147
217
|
pointers: readonly string[],
|
|
148
218
|
): void {
|
|
149
219
|
for (const pointer of pointers) {
|
|
150
|
-
const
|
|
151
|
-
|
|
220
|
+
const resolved = collectSubschemas(
|
|
221
|
+
schema,
|
|
222
|
+
pointer.slice(1).split("/").map(decodeJsonPointerSegment),
|
|
223
|
+
);
|
|
224
|
+
if (!resolved.found) {
|
|
152
225
|
throw new Error(
|
|
153
226
|
`Invalid event subject param pointer '${pointer}' for event '${name}' (path not found in schema)`,
|
|
154
227
|
);
|
|
155
228
|
}
|
|
156
229
|
|
|
157
|
-
if (!isTokenableSchema
|
|
230
|
+
if (!resolved.schemas.every(isTokenableSchema)) {
|
|
158
231
|
throw new Error(
|
|
159
232
|
`Invalid event subject param pointer '${pointer}' for event '${name}' (must resolve to string/number schema)`,
|
|
160
233
|
);
|
package/src/device.ts
CHANGED
|
@@ -140,7 +140,6 @@ export type TrellisDeviceConnection<
|
|
|
140
140
|
readonly stream: string;
|
|
141
141
|
readonly api: TApi;
|
|
142
142
|
readonly connection: TrellisConnection;
|
|
143
|
-
readonly natsConnection: NatsConnection;
|
|
144
143
|
readonly health: ServiceHealth;
|
|
145
144
|
};
|
|
146
145
|
|
|
@@ -717,6 +716,9 @@ async function fetchDeviceBootstrap(args: {
|
|
|
717
716
|
});
|
|
718
717
|
}
|
|
719
718
|
|
|
719
|
+
/**
|
|
720
|
+
* @internal Exported for focused tests and platform-specific wrappers.
|
|
721
|
+
*/
|
|
720
722
|
export async function startDeviceActivationWithDeps<
|
|
721
723
|
TContract extends DeviceContract<TrellisAPI, {
|
|
722
724
|
state?: Readonly<Record<string, unknown>>;
|
|
@@ -758,6 +760,9 @@ export async function startDeviceActivationWithDeps<
|
|
|
758
760
|
});
|
|
759
761
|
}
|
|
760
762
|
|
|
763
|
+
/**
|
|
764
|
+
* @internal Exported for focused tests and platform-specific wrappers.
|
|
765
|
+
*/
|
|
761
766
|
export async function resumeDeviceActivationWithDeps<
|
|
762
767
|
TLocalState extends TrellisDeviceLocalActivationState,
|
|
763
768
|
TContract extends DeviceContract<TrellisAPI, {
|
|
@@ -783,6 +788,10 @@ export async function resumeDeviceActivationWithDeps<
|
|
|
783
788
|
});
|
|
784
789
|
}
|
|
785
790
|
|
|
791
|
+
/**
|
|
792
|
+
* @internal Exported for focused tests; applications should use
|
|
793
|
+
* `TrellisDevice.connect`.
|
|
794
|
+
*/
|
|
786
795
|
export async function connectDeviceWithDeps<
|
|
787
796
|
TContract extends DeviceContract<TrellisAPI, {
|
|
788
797
|
state?: Readonly<Record<string, unknown>>;
|
|
@@ -965,7 +974,6 @@ export async function connectDeviceWithDeps<
|
|
|
965
974
|
stream: trellis.stream,
|
|
966
975
|
api: trellis.api,
|
|
967
976
|
connection: trellis.connection,
|
|
968
|
-
natsConnection: trellis.natsConnection,
|
|
969
977
|
health,
|
|
970
978
|
};
|
|
971
979
|
}
|
package/src/index.ts
CHANGED
|
@@ -158,9 +158,9 @@ export type {
|
|
|
158
158
|
TerminalJob,
|
|
159
159
|
WorkerInfo,
|
|
160
160
|
} from "./jobs.js";
|
|
161
|
-
export {
|
|
161
|
+
export { TypedKVEntry } from "./kv.js";
|
|
162
162
|
export type { WatchEvent, WatchOptions } from "./kv.js";
|
|
163
|
-
export {
|
|
163
|
+
export { TypedStoreEntry } from "./store.js";
|
|
164
164
|
export type {
|
|
165
165
|
StoreBody,
|
|
166
166
|
StoreInfo,
|
|
@@ -169,7 +169,7 @@ export type {
|
|
|
169
169
|
StoreStatus,
|
|
170
170
|
StoreWaitOptions,
|
|
171
171
|
} from "./store.js";
|
|
172
|
-
export {
|
|
172
|
+
export { FileInfoSchema } from "./transfer.js";
|
|
173
173
|
export type {
|
|
174
174
|
FileInfo,
|
|
175
175
|
ReceiveTransferGrant,
|
|
@@ -249,5 +249,4 @@ export type {
|
|
|
249
249
|
TrellisFor,
|
|
250
250
|
ValueStateStoreClient,
|
|
251
251
|
} from "./trellis.js";
|
|
252
|
-
export { Trellis } from "./trellis.js";
|
|
253
252
|
export type { TrellisDeviceConnection } from "./device.js";
|
package/src/runtime_transport.ts
CHANGED
|
@@ -29,6 +29,8 @@ export type RuntimeTransport = {
|
|
|
29
29
|
|
|
30
30
|
type NativeGlobalThis = typeof dntShim.dntGlobalThis & {
|
|
31
31
|
Deno?: { version?: { deno?: string } };
|
|
32
|
+
document?: unknown;
|
|
33
|
+
window?: unknown;
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
export function selectRuntimeTransportServers(
|
|
@@ -54,7 +56,10 @@ export function selectRuntimeTransportServers(
|
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
function isBrowserRuntime(): boolean {
|
|
57
|
-
|
|
59
|
+
const load = new Function("return globalThis") as () => NativeGlobalThis;
|
|
60
|
+
const browserGlobal = load();
|
|
61
|
+
return typeof browserGlobal.window !== "undefined" &&
|
|
62
|
+
typeof browserGlobal.document !== "undefined";
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
function isDenoRuntime(): boolean {
|
package/src/server/health_rpc.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type HealthRpcServer = {
|
|
11
11
|
name: string;
|
|
12
12
|
api: TrellisAPI;
|
|
13
|
-
|
|
13
|
+
connection?: { status: { phase: string } };
|
|
14
14
|
mount<M extends keyof TrellisAPI["rpc"] & string>(
|
|
15
15
|
method: M,
|
|
16
16
|
handler: (...args: unknown[]) => unknown,
|
|
@@ -40,10 +40,15 @@ export async function mountStandardHealthRpc(
|
|
|
40
40
|
|
|
41
41
|
const method = rpcName as keyof TrellisAPI["rpc"] & string;
|
|
42
42
|
await server.mount(method, async () => {
|
|
43
|
+
const connection = server.connection;
|
|
43
44
|
const response = opts?.response
|
|
44
45
|
? await opts.response()
|
|
45
46
|
: await runAllHealthChecks(server.name, {
|
|
46
|
-
|
|
47
|
+
...(connection
|
|
48
|
+
? {
|
|
49
|
+
nats: async () => Result.ok(connection.status.phase !== "closed"),
|
|
50
|
+
}
|
|
51
|
+
: {}),
|
|
47
52
|
...(opts?.checks ?? {}),
|
|
48
53
|
});
|
|
49
54
|
return Result.ok(response);
|