@qlever-llc/trellis 0.10.13 → 0.10.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/esm/auth/browser.js +0 -1
  2. package/esm/auth.js +0 -1
  3. package/esm/browser.d.ts +29 -2
  4. package/esm/browser.d.ts.map +1 -1
  5. package/esm/browser.js +14 -3
  6. package/esm/client_connect.d.ts.map +1 -1
  7. package/esm/client_connect.js +7 -3
  8. package/esm/contract_support/mod.d.ts +5 -10
  9. package/esm/contract_support/mod.d.ts.map +1 -1
  10. package/esm/contract_support/mod.js +41 -31
  11. package/esm/contract_support/protocol.d.ts +8 -13
  12. package/esm/contract_support/protocol.d.ts.map +1 -1
  13. package/esm/contract_support/protocol.js +4 -5
  14. package/esm/contracts.js +0 -1
  15. package/esm/device.d.ts +0 -29
  16. package/esm/device.d.ts.map +1 -1
  17. package/esm/device.js +10 -1
  18. package/esm/errors/index.js +0 -1
  19. package/esm/index.d.ts +3 -4
  20. package/esm/index.d.ts.map +1 -1
  21. package/esm/index.js +3 -4
  22. package/esm/runtime_transport.d.ts.map +1 -1
  23. package/esm/runtime_transport.js +4 -2
  24. package/esm/server/health_rpc.d.ts +4 -2
  25. package/esm/server/health_rpc.d.ts.map +1 -1
  26. package/esm/server/health_rpc.js +6 -1
  27. package/esm/server/service.d.ts +14 -37
  28. package/esm/server/service.d.ts.map +1 -1
  29. package/esm/server/service.js +143 -103
  30. package/esm/server.js +3 -3
  31. package/esm/service/outbox_inbox.d.ts +1 -6
  32. package/esm/service/outbox_inbox.d.ts.map +1 -1
  33. package/esm/service/outbox_inbox.js +0 -21
  34. package/esm/telemetry/env.d.ts.map +1 -1
  35. package/esm/telemetry/env.js +5 -6
  36. package/esm/telemetry/metrics.d.ts +0 -7
  37. package/esm/telemetry/metrics.d.ts.map +1 -1
  38. package/esm/trellis.d.ts +1 -19
  39. package/esm/trellis.d.ts.map +1 -1
  40. package/esm/trellis.js +12 -8
  41. package/package.json +2 -2
  42. package/script/auth/browser.js +0 -1
  43. package/script/auth.js +0 -1
  44. package/script/browser.d.ts +29 -2
  45. package/script/browser.d.ts.map +1 -1
  46. package/script/browser.js +75 -17
  47. package/script/client_connect.d.ts.map +1 -1
  48. package/script/client_connect.js +7 -36
  49. package/script/contract_support/mod.d.ts +5 -10
  50. package/script/contract_support/mod.d.ts.map +1 -1
  51. package/script/contract_support/mod.js +43 -32
  52. package/script/contract_support/protocol.d.ts +8 -13
  53. package/script/contract_support/protocol.d.ts.map +1 -1
  54. package/script/contract_support/protocol.js +5 -6
  55. package/script/contracts.js +0 -1
  56. package/script/device.d.ts +0 -29
  57. package/script/device.d.ts.map +1 -1
  58. package/script/device.js +10 -1
  59. package/script/errors/index.js +0 -1
  60. package/script/index.d.ts +3 -4
  61. package/script/index.d.ts.map +1 -1
  62. package/script/index.js +1 -6
  63. package/script/runtime_transport.d.ts.map +1 -1
  64. package/script/runtime_transport.js +4 -2
  65. package/script/server/health_rpc.d.ts +4 -2
  66. package/script/server/health_rpc.d.ts.map +1 -1
  67. package/script/server/health_rpc.js +6 -1
  68. package/script/server/service.d.ts +14 -37
  69. package/script/server/service.d.ts.map +1 -1
  70. package/script/server/service.js +153 -112
  71. package/script/server.js +3 -3
  72. package/script/service/outbox_inbox.d.ts +1 -6
  73. package/script/service/outbox_inbox.d.ts.map +1 -1
  74. package/script/service/outbox_inbox.js +0 -21
  75. package/script/telemetry/env.d.ts.map +1 -1
  76. package/script/telemetry/env.js +5 -39
  77. package/script/telemetry/metrics.d.ts +0 -7
  78. package/script/telemetry/metrics.d.ts.map +1 -1
  79. package/script/trellis.d.ts +1 -19
  80. package/script/trellis.d.ts.map +1 -1
  81. package/script/trellis.js +12 -8
  82. package/src/browser.ts +200 -2
  83. package/src/client_connect.ts +10 -2
  84. package/src/contract_support/mod.ts +69 -45
  85. package/src/contract_support/protocol.ts +16 -7
  86. package/src/device.ts +10 -2
  87. package/src/index.ts +3 -4
  88. package/src/runtime_transport.ts +6 -1
  89. package/src/server/health_rpc.ts +7 -2
  90. package/src/server/service.ts +186 -126
  91. package/src/server.ts +3 -3
  92. package/src/service/outbox_inbox.ts +1 -28
  93. package/src/telemetry/env.ts +10 -7
  94. 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,
@@ -968,13 +969,9 @@ export type ContractSourceJobs<TSchemaName extends string = string> = Record<
968
969
  ContractSourceJobQueue<TSchemaName>
969
970
  >;
970
971
 
971
- export type ContractSourceEventConsumerEvent = {
972
- use: string;
973
- event: string;
974
- };
975
-
976
972
  export type ContractSourceEventConsumerGroup = {
977
- events: readonly ContractSourceEventConsumerEvent[];
973
+ uses?: Record<string, readonly string[]>;
974
+ self?: readonly string[];
978
975
  replay?: "new" | "all";
979
976
  ordering?: "strict";
980
977
  concurrency?: number;
@@ -2641,7 +2638,9 @@ function projectDigestJobs(
2641
2638
  function projectDigestEventConsumers(
2642
2639
  eventConsumers: ContractEventConsumers | undefined,
2643
2640
  ): ContractEventConsumers | undefined {
2644
- return eventConsumers ? mapValues(eventConsumers, omitDocs) : undefined;
2641
+ return eventConsumers
2642
+ ? mapValues(eventConsumers, projectDigestEventConsumerGroup)
2643
+ : undefined;
2645
2644
  }
2646
2645
 
2647
2646
  function projectDigestUsesFlat(
@@ -3072,25 +3071,31 @@ function feed(feed: ContractFeed): ContractFeed {
3072
3071
  };
3073
3072
  }
3074
3073
 
3075
- function eventConsumerEvent(
3076
- event: ContractEventConsumerEvent,
3077
- ): ContractEventConsumerEvent {
3078
- return { use: event.use, event: event.event };
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
+ );
3079
3085
  }
3080
3086
 
3081
- function sortEventConsumerEvents(
3082
- events: readonly ContractEventConsumerEvent[],
3083
- ): ContractEventConsumerEvent[] {
3084
- return events.map(eventConsumerEvent).sort((left, right) =>
3085
- left.use.localeCompare(right.use) || left.event.localeCompare(right.event)
3086
- );
3087
+ function sortEventConsumerSelf(
3088
+ self: readonly string[] | undefined,
3089
+ ): string[] | undefined {
3090
+ return self ? sortedUnique(self) : undefined;
3087
3091
  }
3088
3092
 
3089
3093
  function eventConsumerGroup(
3090
- group: ContractEventConsumerGroup,
3094
+ group: ContractSourceEventConsumerGroup,
3091
3095
  ): ContractEventConsumerGroup {
3092
3096
  return {
3093
- events: sortEventConsumerEvents(group.events),
3097
+ ...(group.uses ? { uses: sortEventConsumerUses(group.uses) } : {}),
3098
+ ...(group.self ? { self: sortEventConsumerSelf(group.self) } : {}),
3094
3099
  replay: group.replay ?? "new",
3095
3100
  ordering: group.ordering ?? "strict",
3096
3101
  concurrency: group.concurrency ?? 1,
@@ -3101,6 +3106,12 @@ function eventConsumerGroup(
3101
3106
  };
3102
3107
  }
3103
3108
 
3109
+ function projectDigestEventConsumerGroup(
3110
+ group: ContractEventConsumerGroup,
3111
+ ): ContractEventConsumerGroup {
3112
+ return omitDocs(eventConsumerGroup(group));
3113
+ }
3114
+
3104
3115
  function errorDecl(error: ContractErrorDecl): ContractErrorDecl {
3105
3116
  return {
3106
3117
  type: error.type,
@@ -3244,7 +3255,11 @@ export function parseContractManifest(value: unknown): TrellisContractV1 {
3244
3255
  );
3245
3256
  }
3246
3257
  const contract = normalizeContractManifest(parsed);
3247
- assertValidEventConsumers(contract.eventConsumers, contract.uses);
3258
+ assertValidEventConsumers(
3259
+ contract.eventConsumers,
3260
+ contract.uses,
3261
+ contract.events,
3262
+ );
3248
3263
  return contract;
3249
3264
  }
3250
3265
 
@@ -3412,20 +3427,7 @@ function emitEventConsumers(
3412
3427
  return Object.fromEntries(
3413
3428
  Object.entries(eventConsumers).map(([groupName, group]) => [
3414
3429
  groupName,
3415
- {
3416
- events: sortEventConsumerEvents(group.events),
3417
- replay: group.replay ?? "new",
3418
- ordering: group.ordering ?? "strict",
3419
- concurrency: group.concurrency ?? 1,
3420
- ...(group.ackWaitMs !== undefined
3421
- ? { ackWaitMs: group.ackWaitMs }
3422
- : {}),
3423
- ...(group.maxDeliver !== undefined
3424
- ? { maxDeliver: group.maxDeliver }
3425
- : {}),
3426
- ...(group.backoffMs ? { backoffMs: [...group.backoffMs] } : {}),
3427
- ...(group.docs ? { docs: contractDocs(group.docs) } : {}),
3428
- } satisfies ContractEventConsumerGroup,
3430
+ eventConsumerGroup(group),
3429
3431
  ]),
3430
3432
  );
3431
3433
  }
@@ -3844,7 +3846,7 @@ function emitContract(source: TrellisContractSource): TrellisContractV1 {
3844
3846
  const state = emitState(source.state);
3845
3847
  const resources = emitResources(source.resources);
3846
3848
  const uses = emitUses(source.uses);
3847
- assertValidEventConsumers(eventConsumers, uses);
3849
+ assertValidEventConsumers(eventConsumers, uses, events);
3848
3850
 
3849
3851
  return {
3850
3852
  format: CONTRACT_FORMAT_V1,
@@ -4155,15 +4157,20 @@ function contractUseByAlias(
4155
4157
  function assertValidEventConsumers(
4156
4158
  eventConsumers: ContractEventConsumers | undefined,
4157
4159
  uses: ContractUses | undefined,
4160
+ ownedEvents: Record<string, ContractEvent> | undefined,
4158
4161
  ): void {
4159
4162
  if (!eventConsumers) {
4160
4163
  return;
4161
4164
  }
4162
4165
 
4163
4166
  for (const [groupName, group] of Object.entries(eventConsumers)) {
4164
- if (group.events.length === 0) {
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) {
4165
4172
  throw new Error(
4166
- `event consumer group '${groupName}' must declare events`,
4173
+ `event consumer group '${groupName}' must declare at least one dependency or self event`,
4167
4174
  );
4168
4175
  }
4169
4176
  if ((group.ordering ?? "strict") === "strict" && group.concurrency !== 1) {
@@ -4172,16 +4179,33 @@ function assertValidEventConsumers(
4172
4179
  );
4173
4180
  }
4174
4181
 
4175
- for (const eventRef of group.events) {
4176
- const use = contractUseByAlias(uses, eventRef.use);
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);
4177
4190
  if (!use) {
4178
4191
  throw new Error(
4179
- `event consumer group '${groupName}' references unknown use '${eventRef.use}'`,
4192
+ `event consumer group '${groupName}' references unknown use '${alias}'`,
4180
4193
  );
4181
4194
  }
4182
- if (!use.events?.subscribe?.includes(eventRef.event)) {
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)) {
4183
4207
  throw new Error(
4184
- `event consumer group '${groupName}' references event '${eventRef.event}' that use '${eventRef.use}' does not subscribe to`,
4208
+ `event consumer group '${groupName}' references unknown owned event '${eventName}'`,
4185
4209
  );
4186
4210
  }
4187
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 ContractEventConsumerEventSchema = Type.Object({
103
- use: Type.String({ minLength: 1 }),
104
- event: Type.String({ minLength: 1 }),
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 ContractEventConsumerEvent = Static<
108
- typeof ContractEventConsumerEventSchema
115
+ export type ContractEventConsumerSelf = Static<
116
+ typeof ContractEventConsumerSelfSchema
109
117
  >;
110
118
 
111
119
  export const ContractEventConsumerGroupSchema = Type.Object({
112
- events: Type.Array(ContractEventConsumerEventSchema, { minItems: 1 }),
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"),
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 { TypedKV, TypedKVEntry } from "./kv.js";
161
+ export { TypedKVEntry } from "./kv.js";
162
162
  export type { WatchEvent, WatchOptions } from "./kv.js";
163
- export { TypedStore, TypedStoreEntry } from "./store.js";
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 { createTransferHandle, FileInfoSchema } from "./transfer.js";
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";
@@ -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
- return typeof dntShim.dntGlobalThis !== "undefined" && typeof document !== "undefined";
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 {
@@ -10,7 +10,7 @@ import {
10
10
  type HealthRpcServer = {
11
11
  name: string;
12
12
  api: TrellisAPI;
13
- natsConnection: { isClosed(): boolean };
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
- nats: async () => Result.ok(!server.natsConnection.isClosed()),
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);