@tsplc/debug 0.2.17 → 0.2.19

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/index.js CHANGED
@@ -1,11 +1,19 @@
1
1
  // packages/debug/src/debugger.ts
2
2
  import { join } from "node:path";
3
3
  import { setTimeout as delay } from "node:timers/promises";
4
- import { telemetryBatch } from "@tsplc/server/telemetry";
5
- import { z as z2 } from "zod";
4
+ import { requestsReply } from "@tsplc/server/requests";
5
+ import { telemetryReply } from "@tsplc/server/telemetry";
6
+
7
+ // packages/debug/src/errors.ts
8
+ var errorMessage = (error) => error instanceof Error ? error.message : String(error);
6
9
 
7
10
  // packages/debug/src/adapter.ts
8
- var qualityRank = { good: 0, uncertain: 1, bad: 2, unavailable: 3 };
11
+ var qualityRank = {
12
+ good: 0,
13
+ uncertain: 1,
14
+ bad: 2,
15
+ unavailable: 3
16
+ };
9
17
  var worstQuality = (samples) => samples.reduce((worst, sample) => {
10
18
  const quality = sample?.quality ?? "unavailable";
11
19
  return qualityRank[quality] > qualityRank[worst] ? quality : worst;
@@ -32,16 +40,31 @@ var deviceReads = (device, io, typeOf) => [
32
40
  ...device.assemblies.flatMap(({ raw, bytes }) => raw ? [
33
41
  {
34
42
  target: raw,
35
- type: { kind: "array", element: "BYTE", length: bytes, lower: 0 }
43
+ type: {
44
+ kind: "array",
45
+ element: "BYTE",
46
+ length: bytes,
47
+ lower: 0
48
+ }
36
49
  }
37
50
  ] : []),
38
51
  ...io.filter((binding) => binding.device === device.id).map(({ target, type }) => ({ target, type: typeOf(type) })),
39
- ...Object.values(device.status ?? {}).map((target) => ({ target, type: untyped }))
52
+ ...Object.values(device.status ?? {}).map((target) => ({
53
+ target,
54
+ type: untyped
55
+ }))
40
56
  ];
57
+ var healthState = (quality, faulted) => {
58
+ if (quality !== "good") {
59
+ return "stale";
60
+ }
61
+ return faulted ? "fault" : "ok";
62
+ };
41
63
  var health = (device, value) => {
42
64
  const { state, states, diagnostic, message } = device.health ?? {};
43
- if (!state && !diagnostic)
65
+ if (!state && !diagnostic) {
44
66
  return { state: "unknown", quality: "unavailable" };
67
+ }
45
68
  const stateSample = state ? value(state) : undefined;
46
69
  const diagnosticSample = diagnostic ? value(diagnostic) : undefined;
47
70
  const messageSample = message ? value(message) : undefined;
@@ -53,11 +76,15 @@ var health = (device, value) => {
53
76
  const name = Object.entries(states ?? {}).find(([, member]) => member === number)?.[0] ?? (typeof stateSample?.value === "string" ? stateSample.value : undefined);
54
77
  const faulted = diagnosticSample?.value === true || name !== undefined && faultyState.test(name);
55
78
  return {
56
- state: quality !== "good" ? "stale" : faulted ? "fault" : "ok",
79
+ state: healthState(quality, faulted),
57
80
  ...number !== undefined && { value: number },
58
81
  ...name && { name },
59
- ...typeof diagnosticSample?.value === "boolean" && { diagnostic: diagnosticSample.value },
60
- ...typeof messageSample?.value === "string" && { message: messageSample.value },
82
+ ...typeof diagnosticSample?.value === "boolean" && {
83
+ diagnostic: diagnosticSample.value
84
+ },
85
+ ...typeof messageSample?.value === "string" && {
86
+ message: messageSample.value
87
+ },
61
88
  quality
62
89
  };
63
90
  };
@@ -83,7 +110,10 @@ var deviceState = (device, io, value) => ({
83
110
  ] : []),
84
111
  status: Object.fromEntries(Object.entries(device.status ?? {}).map(([label, target]) => [
85
112
  label,
86
- { value: value(target)?.value ?? null, quality: value(target)?.quality ?? "unavailable" }
113
+ {
114
+ value: value(target)?.value ?? null,
115
+ quality: value(target)?.quality ?? "unavailable"
116
+ }
87
117
  ]))
88
118
  });
89
119
 
@@ -97,7 +127,10 @@ var detailReads = (job) => [
97
127
  { target: `${job.target}.step`, type: scalar("UINT") },
98
128
  { target: `${job.target}.cancelling`, type: scalar("BOOL") },
99
129
  ...job.startedAt ? [{ target: job.startedAt, type: scalar("UDINT") }] : [],
100
- ...job.requests.map(({ sequence }) => ({ target: sequence, type: scalar("UDINT") }))
130
+ ...job.requests.map(({ sequence }) => ({
131
+ target: sequence,
132
+ type: scalar("UDINT")
133
+ }))
101
134
  ];
102
135
  var busyOf = (job, value, traced) => {
103
136
  const read = value(busyRead(job).target);
@@ -116,15 +149,21 @@ var jobState = (job, inputs) => {
116
149
  ...startedAt && { startedAt },
117
150
  ...inputs.failure && { failure: inputs.failure }
118
151
  };
119
- if (!busy)
152
+ if (!busy) {
120
153
  return base;
121
- if (inputs.unread)
154
+ }
155
+ if (inputs.unread) {
122
156
  return { ...base, unread: true };
157
+ }
123
158
  const step = numberOf(value(`${job.target}.step`));
124
159
  const current = job.steps.find((candidate) => candidate.step === step);
125
160
  const calls = job.requests.map((call) => {
126
161
  const sequence = numberOf(value(call.sequence)) ?? 0;
127
- const trace = inputs.request({ job: call.job ?? job.id, service: call.service, sequence });
162
+ const trace = inputs.request({
163
+ job: call.job ?? job.id,
164
+ service: call.service,
165
+ sequence
166
+ });
128
167
  return { service: call.service, sequence, trace };
129
168
  }).filter(({ sequence }) => sequence > 0);
130
169
  const later = (call, than) => call.trace && than.trace ? call.trace.startedAt > than.trace.startedAt : call.sequence > than.sequence;
@@ -132,9 +171,13 @@ var jobState = (job, inputs) => {
132
171
  const cancelling = value(`${job.target}.cancelling`);
133
172
  return {
134
173
  ...base,
135
- ...cancelling?.quality === "good" && { cancelling: cancelling.value === true },
174
+ ...cancelling?.quality === "good" && {
175
+ cancelling: cancelling.value === true
176
+ },
136
177
  ...step !== undefined && { step },
137
- ...current && { current: { kind: current.kind, range: current.range, text: current.text } },
178
+ ...current && {
179
+ current: { kind: current.kind, range: current.range, text: current.text }
180
+ },
138
181
  ...startedAt && { elapsedMs: Math.max(0, now - Date.parse(startedAt)) },
139
182
  ...latest && {
140
183
  request: {
@@ -147,15 +190,22 @@ var jobState = (job, inputs) => {
147
190
  };
148
191
 
149
192
  // packages/debug/src/paths.ts
150
- var segmentPattern = /[^.[\]]+|\[(?:-?\d+|\*)\]/g;
151
- var pathSegments = (path) => path.match(segmentPattern) ?? [];
193
+ import { isArrayIndexSegment, symbolPathSegments } from "@tsplc/opcua/contract";
194
+ var anyIndex = "[*]";
195
+ var pathSegments = (path) => path.split(anyIndex).flatMap((part, index) => [
196
+ ...index > 0 ? [anyIndex] : [],
197
+ ...symbolPathSegments(part)
198
+ ]);
152
199
  var joinPath = (prefix, segment) => segment.startsWith("[") || !prefix ? `${prefix}${segment}` : `${prefix}.${segment}`;
153
200
  var typeOf = (manifest, id) => manifest.types[id] ?? { kind: "scalar", iec: id };
154
201
  var createPaths = (manifest) => {
155
202
  const typeOfId = (id) => typeOf(manifest, id);
156
203
  const bases = new Map([
157
204
  ...manifest.roots.map(({ path, ...root }) => [path, { kind: "target", path, ...root }]),
158
- ...manifest.io.map(({ path, target, type }) => [path, { kind: "target", path, target, type, readable: true }])
205
+ ...manifest.io.map(({ path, target, type }) => [
206
+ path,
207
+ { kind: "target", path, target, type, readable: true }
208
+ ])
159
209
  ]);
160
210
  const constants = new Map(manifest.constants.map(({ path, ...constant }) => [
161
211
  path,
@@ -166,28 +216,32 @@ var createPaths = (manifest) => {
166
216
  let prefix = "";
167
217
  for (const segment of pathSegments(path)) {
168
218
  let children = groups.get(prefix);
169
- if (!children)
219
+ if (!children) {
170
220
  groups.set(prefix, children = new Set);
221
+ }
171
222
  children.add(segment);
172
223
  prefix = joinPath(prefix, segment);
173
224
  }
174
225
  }
175
226
  const resolve = (path) => {
176
227
  const constant = constants.get(path);
177
- if (constant)
228
+ if (constant) {
178
229
  return constant;
230
+ }
179
231
  const segments = pathSegments(path);
180
232
  for (let length = segments.length;length > 0; length--) {
181
233
  const base = bases.get(segments.slice(0, length).reduce(joinPath, ""));
182
- if (!base)
234
+ if (!base) {
183
235
  continue;
236
+ }
184
237
  let resolved = base;
185
238
  for (const segment of segments.slice(length)) {
186
239
  const type = typeOfId(resolved.type);
187
240
  if (type.kind === "record") {
188
241
  const field = type.fields.find(({ name }) => name === segment);
189
- if (!field)
242
+ if (!field) {
190
243
  return;
244
+ }
191
245
  resolved = {
192
246
  kind: "target",
193
247
  path: joinPath(resolved.path, segment),
@@ -198,32 +252,42 @@ var createPaths = (manifest) => {
198
252
  ...field.declaration && { declaration: field.declaration }
199
253
  };
200
254
  } else if (type.kind === "array") {
201
- const index = /^\[(-?\d+)\]$/.exec(segment)?.[1];
202
- if (index === undefined || Number(index) < type.lower || Number(index) >= type.lower + type.length)
255
+ if (!isArrayIndexSegment(segment)) {
203
256
  return;
257
+ }
258
+ const index = Number(segment.slice(1, -1));
259
+ if (index < type.lower || index >= type.lower + type.length) {
260
+ return;
261
+ }
204
262
  resolved = {
205
263
  kind: "target",
206
264
  path: joinPath(resolved.path, segment),
207
- target: `${resolved.target}[${index}]`,
265
+ target: `${resolved.target}${segment}`,
208
266
  type: type.element,
209
267
  readable: resolved.readable
210
268
  };
211
- } else
269
+ } else {
212
270
  return;
271
+ }
213
272
  }
214
273
  return resolved;
215
274
  }
216
275
  };
217
276
  const typedChildren = (resolved) => {
218
277
  const type = resolved?.kind === "target" ? typeOfId(resolved.type) : undefined;
219
- if (type?.kind === "record")
278
+ if (type?.kind === "record") {
220
279
  return type.fields.map(({ name }) => name);
221
- if (type?.kind === "array")
280
+ }
281
+ if (type?.kind === "array") {
222
282
  return Array.from({ length: type.length }, (_, index) => `[${type.lower + index}]`);
283
+ }
223
284
  return [];
224
285
  };
225
286
  const children = (path) => {
226
- const names = [...typedChildren(resolve(path)), ...groups.get(path) ?? []];
287
+ const names = [
288
+ ...typedChildren(resolve(path)),
289
+ ...groups.get(path) ?? []
290
+ ];
227
291
  return [...new Set(names)].map((name) => {
228
292
  const child = joinPath(path, name);
229
293
  return { name, ...resolve(child) ?? { kind: "group", path: child } };
@@ -234,48 +298,184 @@ var createPaths = (manifest) => {
234
298
  const leaves = new Set;
235
299
  const add = (path) => {
236
300
  leaves.add(path);
237
- if (leaves.size > limit)
301
+ if (leaves.size > limit) {
238
302
  throw new RangeError(`More than ${limit} values match`);
303
+ }
239
304
  };
240
305
  const below = (node) => {
241
- if (isLeaf(node))
306
+ if (isLeaf(node)) {
242
307
  return add(node.path);
243
- for (const child of children(node.path))
244
- if (!(child.kind === "target" && child.generated))
308
+ }
309
+ for (const child of children(node.path)) {
310
+ if (!(child.kind === "target" && child.generated)) {
245
311
  below(child);
312
+ }
313
+ }
246
314
  };
247
315
  const match = (prefix, rest, wildcard) => {
248
316
  const [head, ...tail] = rest;
249
317
  if (head === undefined) {
250
318
  const node = resolve(prefix) ?? (groups.has(prefix) ? { kind: "group", path: prefix } : undefined);
251
- if (node)
319
+ if (node) {
252
320
  below(node);
253
- else if (!wildcard)
321
+ } else if (!wildcard) {
254
322
  add(prefix);
323
+ }
255
324
  return;
256
325
  }
257
- if (head === "*" || head === "[*]") {
258
- for (const child of children(prefix))
259
- if (!(child.kind === "target" && child.generated) && (head === "*" || child.name.startsWith("[")))
326
+ if (head === "*" || head === anyIndex) {
327
+ for (const child of children(prefix)) {
328
+ if (!(child.kind === "target" && child.generated) && (head === "*" || child.name.startsWith("["))) {
260
329
  match(child.path, tail, true);
261
- } else
330
+ }
331
+ }
332
+ } else {
262
333
  match(joinPath(prefix, head), tail, wildcard);
334
+ }
263
335
  };
264
- for (const pattern of patterns)
336
+ for (const pattern of patterns) {
265
337
  match("", pathSegments(pattern), false);
338
+ }
266
339
  return [...leaves];
267
340
  };
268
341
  const decode = (typeId, raw) => {
269
342
  const type = typeOfId(typeId);
270
- if (type.kind === "enum")
343
+ if (type.kind === "enum") {
271
344
  return Object.entries(type.members).find(([, value]) => value === raw)?.[0] ?? raw;
272
- if (type.kind === "scalar" && type.iec === "BOOL" && typeof raw === "number")
345
+ }
346
+ if (type.kind === "scalar" && type.iec === "BOOL" && typeof raw === "number") {
273
347
  return raw !== 0;
348
+ }
274
349
  return raw;
275
350
  };
276
351
  return { typeOf: typeOfId, resolve, children, isLeaf, expand, decode };
277
352
  };
278
353
 
354
+ // packages/debug/src/timeline.ts
355
+ var timelineEntries = {
356
+ app: (at, task, sequence, record) => ({
357
+ at,
358
+ approximate: true,
359
+ source: "plc",
360
+ kind: "app",
361
+ task,
362
+ sequence,
363
+ record
364
+ }),
365
+ trace: (at, trace) => ({
366
+ at,
367
+ approximate: true,
368
+ source: "plc",
369
+ kind: "trace",
370
+ trace
371
+ }),
372
+ request: (request) => ({
373
+ at: request.startedAt,
374
+ approximate: true,
375
+ source: "server",
376
+ kind: "request",
377
+ request
378
+ }),
379
+ deploy: (at, details) => ({
380
+ at,
381
+ approximate: false,
382
+ source: "agent",
383
+ kind: "deploy",
384
+ ...details
385
+ }),
386
+ audit: (at, details) => ({
387
+ at,
388
+ approximate: false,
389
+ source: "agent",
390
+ kind: "audit",
391
+ ...details
392
+ }),
393
+ connection: (at, state, error) => ({
394
+ at,
395
+ approximate: false,
396
+ source: "debugger",
397
+ kind: "connection",
398
+ state,
399
+ ...error && { error }
400
+ })
401
+ };
402
+ var insertInTimeOrder = (entries, entry) => {
403
+ for (let index = entries.length;index >= 0; index--) {
404
+ const before = entries[index - 1];
405
+ if (before === undefined || before.at <= entry.at) {
406
+ entries.splice(index, 0, entry);
407
+ return;
408
+ }
409
+ }
410
+ };
411
+ var createTimeline = (limit = 1e4) => {
412
+ const entries = [];
413
+ return {
414
+ add: (entry) => {
415
+ insertInTimeOrder(entries, entry);
416
+ if (entries.length > limit) {
417
+ entries.splice(0, entries.length - limit);
418
+ }
419
+ },
420
+ since: (at) => at ? entries.filter((entry) => entry.at > at) : [...entries]
421
+ };
422
+ };
423
+
424
+ // packages/debug/src/requests.ts
425
+ var callsRemembered = 1000;
426
+ var createRequestFollower = () => {
427
+ let newest = 0;
428
+ const pending = new Map;
429
+ const calls = new Map;
430
+ return {
431
+ since: () => pending.size ? Math.min(...pending.keys()) - 1 : newest,
432
+ accept: (reply, changed) => {
433
+ if (reply.latest < newest) {
434
+ newest = 0;
435
+ pending.clear();
436
+ return;
437
+ }
438
+ const oldest = reply.requests[0]?.seq;
439
+ if (oldest !== undefined) {
440
+ for (const seq of pending.keys()) {
441
+ if (seq < oldest) {
442
+ pending.delete(seq);
443
+ }
444
+ }
445
+ }
446
+ for (const request of reply.requests) {
447
+ const waiting = pending.get(request.seq);
448
+ if (waiting && request.status === "pending") {
449
+ continue;
450
+ }
451
+ if (!waiting && request.seq <= newest) {
452
+ continue;
453
+ }
454
+ const entry = waiting ?? timelineEntries.request(request);
455
+ entry.request = request;
456
+ newest = Math.max(newest, request.seq);
457
+ if (request.status === "pending") {
458
+ pending.set(request.seq, entry);
459
+ } else {
460
+ pending.delete(request.seq);
461
+ }
462
+ if (request.job !== undefined && request.sequence !== undefined) {
463
+ calls.set(`${request.job}:${request.service}:${request.sequence}`, request);
464
+ for (const key of [...calls.keys()].slice(0, -callsRemembered)) {
465
+ calls.delete(key);
466
+ }
467
+ }
468
+ changed({ request, entry, added: !waiting });
469
+ }
470
+ },
471
+ call: ({
472
+ job,
473
+ service,
474
+ sequence
475
+ }) => calls.get(`${job}:${service}:${sequence}`)
476
+ };
477
+ };
478
+
279
479
  // packages/debug/src/telemetry.ts
280
480
  import { recordedAt } from "@tsplc/server/telemetry-log";
281
481
  var clockWindowMs = 60000;
@@ -290,8 +490,9 @@ var createTelemetryFollower = (manifest) => {
290
490
  let newest = 0;
291
491
  let build = null;
292
492
  const clock = () => {
293
- if (!estimates.length)
493
+ if (!estimates.length) {
294
494
  return null;
495
+ }
295
496
  const offsets = estimates.map(({ offsetMs }) => offsetMs);
296
497
  const best = estimates.reduce((best, estimate) => estimate.offsetMs > best.offsetMs ? estimate : best);
297
498
  return {
@@ -302,8 +503,9 @@ var createTelemetryFollower = (manifest) => {
302
503
  const observe = (batch, receivedAt) => {
303
504
  const rate = rates.get(batch.task);
304
505
  const restarted = rate !== undefined && batch.now < rate.now;
305
- if (restarted)
506
+ if (restarted) {
306
507
  estimates = [];
508
+ }
307
509
  estimates = estimates.filter(({ at }) => receivedAt - at < clockWindowMs);
308
510
  estimates.push({
309
511
  offsetMs: batch.now - receivedAt,
@@ -316,9 +518,9 @@ var createTelemetryFollower = (manifest) => {
316
518
  scan: batch.scan,
317
519
  dropped: batch.dropped
318
520
  };
319
- if (!rate || restarted)
521
+ if (!rate || restarted) {
320
522
  rates.set(batch.task, { scan: batch.scan, now: batch.now });
321
- else if (batch.now - rate.now >= 1000) {
523
+ } else if (batch.now - rate.now >= 1000) {
322
524
  stats.scansPerSecond = (batch.scan - rate.scan) * 1000 / (batch.now - rate.now);
323
525
  rates.set(batch.task, { scan: batch.scan, now: batch.now });
324
526
  }
@@ -333,8 +535,9 @@ var createTelemetryFollower = (manifest) => {
333
535
  }
334
536
  const received = [];
335
537
  for (const batch of reply.batches) {
336
- if (batch.seq <= newest)
538
+ if (batch.seq <= newest) {
337
539
  continue;
540
+ }
338
541
  newest = batch.seq;
339
542
  build = batch.build;
340
543
  const receivedAt = new Date(batch.receivedAt);
@@ -355,69 +558,6 @@ var createTelemetryFollower = (manifest) => {
355
558
  };
356
559
  };
357
560
 
358
- // packages/debug/src/timeline.ts
359
- var timelineEntries = {
360
- app: (at, task, sequence, record) => ({
361
- at,
362
- approximate: true,
363
- source: "plc",
364
- kind: "app",
365
- task,
366
- sequence,
367
- record
368
- }),
369
- trace: (at, trace) => ({
370
- at,
371
- approximate: true,
372
- source: "plc",
373
- kind: "trace",
374
- trace
375
- }),
376
- request: (request) => ({
377
- at: request.startedAt,
378
- approximate: true,
379
- source: "server",
380
- kind: "request",
381
- request
382
- }),
383
- deploy: (at, details) => ({
384
- at,
385
- approximate: false,
386
- source: "agent",
387
- kind: "deploy",
388
- ...details
389
- }),
390
- audit: (at, details) => ({
391
- at,
392
- approximate: false,
393
- source: "agent",
394
- kind: "audit",
395
- ...details
396
- }),
397
- connection: (at, state, error) => ({
398
- at,
399
- approximate: false,
400
- source: "debugger",
401
- kind: "connection",
402
- state,
403
- ...error && { error }
404
- })
405
- };
406
- var createTimeline = (limit = 1e4) => {
407
- const entries = [];
408
- return {
409
- add: (entry) => {
410
- let index = entries.length;
411
- while (index > 0 && entry.at < (entries[index - 1]?.at ?? entry.at))
412
- index--;
413
- entries.splice(index, 0, entry);
414
- if (entries.length > limit)
415
- entries.splice(0, entries.length - limit);
416
- },
417
- since: (at) => at ? entries.filter((entry) => entry.at > at) : [...entries]
418
- };
419
- };
420
-
421
561
  // packages/debug/src/trace.ts
422
562
  var createTraceDecoder = (manifest, paths) => {
423
563
  const sites = new Map(manifest.events.map((site) => [site.id, site]));
@@ -429,8 +569,9 @@ var createTraceDecoder = (manifest, paths) => {
429
569
  return {
430
570
  decode: (record, task, sequence) => {
431
571
  const site = sites.get(record.site);
432
- if (!site)
572
+ if (!site) {
433
573
  return;
574
+ }
434
575
  return {
435
576
  sequence,
436
577
  task,
@@ -457,7 +598,7 @@ import { dirname } from "node:path";
457
598
  import { isDeepStrictEqual } from "node:util";
458
599
  import { z } from "zod";
459
600
  var watchGroupsSchema = z.strictObject({
460
- groups: z.record(z.string().min(1), z.array(z.string().min(1)))
601
+ groups: z.record(z.string().nonempty(), z.array(z.string().nonempty()))
461
602
  });
462
603
  var loadWatchGroups = async (file, configured) => {
463
604
  let saved = existsSync(file) ? watchGroupsSchema.parse(JSON.parse(await readFile(file, "utf8"))).groups : {};
@@ -485,40 +626,22 @@ var pullMs = 1000;
485
626
  var buildCheckMs = 1e4;
486
627
  var hardwareMs = 5000;
487
628
  var detailedJobs = 16;
488
- var requestsRemembered = 1000;
489
- var requestTrace = z2.object({
490
- seq: z2.number().int(),
491
- service: z2.string(),
492
- job: z2.number().int().optional(),
493
- sequence: z2.number().int().optional(),
494
- startedAt: z2.string(),
495
- durationMs: z2.number().optional(),
496
- status: z2.enum(["pending", "ok", "error", "timeout"]),
497
- httpStatus: z2.number().int().optional(),
498
- error: z2.string().optional(),
499
- input: z2.unknown().optional(),
500
- output: z2.unknown().optional()
501
- });
502
- var requestsReply = z2.object({ requests: z2.array(requestTrace), latest: z2.number().int() });
503
- var telemetryReply = z2.object({
504
- batches: z2.array(telemetryBatch.extend({ seq: z2.number().int(), receivedAt: z2.string() })),
505
- latest: z2.number().int()
506
- });
507
- var message = (error) => error instanceof Error ? error.message : String(error);
508
629
  var createShown = () => {
509
630
  const counts = new Map;
510
631
  return {
511
632
  has: (key) => counts.has(key),
512
633
  add: (keys) => {
513
- for (const key of keys)
634
+ for (const key of keys) {
514
635
  counts.set(key, (counts.get(key) ?? 0) + 1);
636
+ }
515
637
  return () => {
516
638
  for (const key of keys) {
517
639
  const count = counts.get(key) - 1;
518
- if (count)
640
+ if (count) {
519
641
  counts.set(key, count);
520
- else
642
+ } else {
521
643
  counts.delete(key);
644
+ }
522
645
  }
523
646
  };
524
647
  }
@@ -529,6 +652,7 @@ var createDebugger = async (options) => {
529
652
  const paths = createPaths(manifest);
530
653
  const trace = createTraceDecoder(manifest, paths);
531
654
  const telemetry = createTelemetryFollower(manifest);
655
+ const requests = createRequestFollower();
532
656
  const timeline = createTimeline();
533
657
  const watchGroups = await loadWatchGroups(join(options.stateDirectory, "watch.json"), options.watch);
534
658
  const listeners = new Set;
@@ -536,15 +660,17 @@ var createDebugger = async (options) => {
536
660
  const { signal } = controller;
537
661
  let controllerBuild = null;
538
662
  const emit = (message) => {
539
- for (const listener of listeners)
663
+ for (const listener of listeners) {
540
664
  listener(message);
665
+ }
541
666
  };
542
667
  const record = (entry) => {
543
668
  timeline.add(entry);
544
669
  emit({ event: "timeline", data: entry });
545
670
  };
546
- for (const entry of options.timeline ?? [])
671
+ for (const entry of options.timeline ?? []) {
547
672
  timeline.add(entry);
673
+ }
548
674
  const session = () => ({
549
675
  build: {
550
676
  manifest: manifest.build.id,
@@ -559,8 +685,9 @@ var createDebugger = async (options) => {
559
685
  ...options.projectRoot && { projectRoot: options.projectRoot }
560
686
  });
561
687
  const setBuild = (build) => {
562
- if (build === controllerBuild)
688
+ if (build === controllerBuild) {
563
689
  return;
690
+ }
564
691
  controllerBuild = build;
565
692
  emit({ event: "session", data: session() });
566
693
  };
@@ -573,9 +700,10 @@ var createDebugger = async (options) => {
573
700
  });
574
701
  const locate = (path) => {
575
702
  const resolved = paths.resolve(path);
576
- if (!resolved)
703
+ if (!resolved) {
577
704
  return unavailable(path, "Unknown path");
578
- if (resolved.kind === "constant")
705
+ }
706
+ if (resolved.kind === "constant") {
579
707
  return {
580
708
  path,
581
709
  value: resolved.value,
@@ -583,11 +711,16 @@ var createDebugger = async (options) => {
583
711
  status: "Constant",
584
712
  receivedAt: new Date().toISOString()
585
713
  };
586
- if (!resolved.readable)
714
+ }
715
+ if (!resolved.readable) {
587
716
  return unavailable(path, "Not readable");
717
+ }
588
718
  return resolved;
589
719
  };
590
- const targetRead = ({ target, type }) => ({
720
+ const targetRead = ({
721
+ target,
722
+ type
723
+ }) => ({
591
724
  target,
592
725
  type: paths.typeOf(type)
593
726
  });
@@ -605,38 +738,51 @@ var createDebugger = async (options) => {
605
738
  listener(located);
606
739
  continue;
607
740
  }
608
- if (!watched.has(located.target))
741
+ if (!watched.has(located.target)) {
609
742
  reads.push(targetRead(located));
610
- watched.set(located.target, [...watched.get(located.target) ?? [], { path, ...located }]);
743
+ }
744
+ watched.set(located.target, [
745
+ ...watched.get(located.target) ?? [],
746
+ { path, ...located }
747
+ ]);
611
748
  }
612
- if (!reads.length)
749
+ if (!reads.length) {
613
750
  return () => {};
751
+ }
614
752
  return adapter.subscribe(reads, (target, received) => {
615
- for (const { path, type } of watched.get(target) ?? [])
753
+ for (const { path, type } of watched.get(target) ?? []) {
616
754
  listener(sampleOf(path, type, received));
755
+ }
617
756
  });
618
757
  };
619
758
  const values = async (patterns) => {
620
759
  const connection = adapter.connection();
621
- if (connection.state !== "connected")
760
+ if (connection.state !== "connected") {
622
761
  throw new DebugError(connection.error ?? "The controller is not connected", 503);
762
+ }
623
763
  const located = paths.expand(patterns).map((path) => ({ path, located: locate(path) }));
624
764
  const read = await adapter.read(located.flatMap(({ located }) => ("target" in located) ? [targetRead(located)] : []));
625
765
  return located.map(({ path, located }) => {
626
- if (!("target" in located))
766
+ if (!("target" in located)) {
627
767
  return located;
768
+ }
628
769
  const sample = read.get(located.target);
629
770
  return sample ? sampleOf(path, located.type, sample) : unavailable(path, "Not read");
630
771
  });
631
772
  };
632
- const stopBuild = adapter.subscribe([{ target: manifest.build.target, type: { kind: "scalar", iec: "STRING" } }], (_, sample) => {
633
- if (sample.quality === "good" && typeof sample.value === "string")
773
+ const stopBuild = adapter.subscribe([
774
+ {
775
+ target: manifest.build.target,
776
+ type: { kind: "scalar", iec: "STRING" }
777
+ }
778
+ ], (_, sample) => {
779
+ if (sample.quality === "good" && typeof sample.value === "string") {
634
780
  setBuild(sample.value);
781
+ }
635
782
  }, buildCheckMs);
636
783
  const latest = new Map;
637
784
  const traced = new Map;
638
785
  const failures = new Map;
639
- const requests = new Map;
640
786
  let unread = new Set;
641
787
  const shownJobs = createShown();
642
788
  const shownHealth = createShown();
@@ -649,35 +795,41 @@ var createDebugger = async (options) => {
649
795
  unread: unread.has(job.id),
650
796
  clock: telemetry.clock(),
651
797
  now: Date.now(),
652
- request: ({ job, service, sequence }) => requests.get(`${job}:${service}:${sequence}`)
798
+ request: requests.call
653
799
  });
654
- const deviceOf = (id) => deviceState(manifest.devices.find((device) => device.id === id), manifest.io, (target) => latest.get(target));
800
+ const deviceOf = (device) => deviceState(device, manifest.io, (target) => latest.get(target));
655
801
  const send = (key, message) => {
656
802
  const json = JSON.stringify(message.data);
657
- if (sent.get(key) === json)
803
+ if (sent.get(key) === json) {
658
804
  return;
805
+ }
659
806
  sent.set(key, json);
660
807
  emit(message);
661
808
  };
662
809
  const updateJob = (job) => send(`job:${job.id}`, { event: "job", data: jobOf(job) });
663
- const updateDevice = (id) => send(`device:${id}`, { event: "device", data: deviceOf(id) });
810
+ const updateDevice = (device) => send(`device:${device.id}`, { event: "device", data: deviceOf(device) });
664
811
  const reader = (intervalMs, update) => {
665
812
  let current = { key: "", stop: () => {} };
666
813
  return (reads) => {
667
814
  const key = reads.map(({ read }) => read.target).join(`
668
815
  `);
669
- if (key === current.key)
816
+ if (key === current.key) {
670
817
  return;
818
+ }
671
819
  current.stop();
672
820
  const owners = new Map;
673
- for (const { owner, read } of reads)
821
+ for (const { owner, read } of reads) {
674
822
  owners.set(read.target, [...owners.get(read.target) ?? [], owner]);
823
+ }
675
824
  current = {
676
825
  key,
677
- stop: reads.length ? adapter.subscribe([...new Map(reads.map(({ read }) => [read.target, read])).values()], (target, sample) => {
826
+ stop: reads.length ? adapter.subscribe([
827
+ ...new Map(reads.map(({ read }) => [read.target, read])).values()
828
+ ], (target, sample) => {
678
829
  latest.set(target, sample);
679
- for (const owner of owners.get(target) ?? [])
830
+ for (const owner of owners.get(target) ?? []) {
680
831
  update(owner);
832
+ }
681
833
  schedule();
682
834
  }, intervalMs) : () => {}
683
835
  };
@@ -685,6 +837,15 @@ var createDebugger = async (options) => {
685
837
  };
686
838
  const readJobs = reader(adapter.samplingMs, updateJob);
687
839
  const readDevices = reader(hardwareMs, updateDevice);
840
+ const shownDeviceReads = (device) => {
841
+ if (shownDevices.has(device.id)) {
842
+ return deviceReads(device, manifest.io, paths.typeOf);
843
+ }
844
+ if (shownHealth.has(device.id)) {
845
+ return healthReads(device);
846
+ }
847
+ return [];
848
+ };
688
849
  const plan = () => {
689
850
  const shown = manifest.jobs.filter((job) => shownJobs.has(job.id));
690
851
  const busy = shown.filter((job) => busyOf(job, (target) => latest.get(target), traced.get(job.id)));
@@ -693,12 +854,13 @@ var createDebugger = async (options) => {
693
854
  ...shown.map((job) => ({ owner: job, read: busyRead(job) })),
694
855
  ...busy.slice(0, detailedJobs).flatMap((job) => detailReads(job).map((read) => ({ owner: job, read })))
695
856
  ]);
696
- readDevices(manifest.devices.flatMap((device) => (shownDevices.has(device.id) ? deviceReads(device, manifest.io, paths.typeOf) : shownHealth.has(device.id) ? healthReads(device) : []).map((read) => ({ owner: device.id, read }))));
857
+ readDevices(manifest.devices.flatMap((device) => shownDeviceReads(device).map((read) => ({ owner: device, read }))));
697
858
  };
698
859
  let planned = false;
699
860
  const schedule = () => {
700
- if (planned)
861
+ if (planned) {
701
862
  return;
863
+ }
702
864
  planned = true;
703
865
  queueMicrotask(() => {
704
866
  planned = false;
@@ -713,33 +875,48 @@ var createDebugger = async (options) => {
713
875
  ];
714
876
  schedule();
715
877
  return () => {
716
- for (const stop of stops)
878
+ for (const stop of stops) {
717
879
  stop();
880
+ }
718
881
  schedule();
719
882
  };
720
883
  };
721
- const receive = ({ build, task, sequence, at, record: received }) => {
884
+ const receive = ({
885
+ build,
886
+ task,
887
+ sequence,
888
+ at,
889
+ record: received
890
+ }) => {
722
891
  if (received.kind === "trace") {
723
- if (build !== manifest.build.id)
892
+ if (build !== manifest.build.id) {
724
893
  return;
894
+ }
725
895
  const event = trace.decode(received, task, sequence);
726
- if (event)
896
+ if (event) {
727
897
  record(timelineEntries.trace(at, event));
898
+ }
728
899
  const site = trace.job(received);
729
- if (!site)
900
+ if (!site) {
730
901
  return;
902
+ }
731
903
  const startedAt = site.started ? at : traced.get(site.job.id)?.startedAt;
732
- traced.set(site.job.id, { busy: site.started, ...startedAt && { startedAt } });
904
+ traced.set(site.job.id, {
905
+ busy: site.started,
906
+ ...startedAt && { startedAt }
907
+ });
733
908
  updateJob(site.job);
734
909
  schedule();
735
910
  return;
736
911
  }
737
912
  record(timelineEntries.app(at, task, sequence, received));
738
- if (received.kind !== "exception" || received.handled)
913
+ if (received.kind !== "exception" || received.handled) {
739
914
  return;
915
+ }
740
916
  const job = manifest.jobs.find(({ name, instance }) => name === received.function && instance === received.instance);
741
- if (!job)
917
+ if (!job) {
742
918
  return;
919
+ }
743
920
  failures.set(job.id, {
744
921
  at,
745
922
  code: received.code,
@@ -749,81 +926,69 @@ var createDebugger = async (options) => {
749
926
  updateJob(job);
750
927
  };
751
928
  const pull = async (url, token) => {
752
- const response = await fetch(url, { headers: { authorization: `Bearer ${token}` }, signal });
753
- if (!response.ok)
929
+ const response = await fetch(url, {
930
+ headers: { authorization: `Bearer ${token}` },
931
+ signal
932
+ });
933
+ if (!response.ok) {
754
934
  throw new Error(`The server answered ${response.status}`);
935
+ }
755
936
  return response.json();
756
937
  };
757
- const pullTelemetry = async ({ url, token }) => {
938
+ const pullTelemetry = async ({
939
+ url,
940
+ token
941
+ }) => {
758
942
  const reply = telemetryReply.parse(await pull(new URL(`/tsplc/telemetry?since=${telemetry.since()}`, url), token));
759
943
  const received = telemetry.accept(reply);
760
- for (const item of received)
944
+ for (const item of received) {
761
945
  receive(item);
946
+ }
762
947
  const build = telemetry.build();
763
- if (build)
948
+ if (build) {
764
949
  setBuild(build);
765
- if (reply.batches.length)
950
+ }
951
+ if (reply.batches.length) {
766
952
  emit({ event: "session", data: session() });
953
+ }
767
954
  };
768
- const pullRequests = ({ url, token }) => {
769
- let newest = 0;
770
- const pending = new Map;
771
- return async () => {
772
- const since = pending.size ? Math.min(...pending.keys()) - 1 : newest;
773
- const body = requestsReply.parse(await pull(new URL(`/tsplc/debug/requests?since=${since}`, url), token));
774
- if (body.latest < newest) {
775
- newest = 0;
776
- pending.clear();
777
- return;
955
+ const pullRequests = async ({
956
+ url,
957
+ token
958
+ }) => {
959
+ const reply = requestsReply.parse(await pull(new URL(`/tsplc/debug/requests?since=${requests.since()}`, url), token));
960
+ requests.accept(reply, ({ request, entry, added }) => {
961
+ if (added) {
962
+ timeline.add(entry);
778
963
  }
779
- const oldest = body.requests[0]?.seq;
780
- if (oldest !== undefined) {
781
- for (const seq of pending.keys())
782
- if (seq < oldest)
783
- pending.delete(seq);
964
+ emit({ event: "request", data: request });
965
+ emit({ event: "timeline", data: entry });
966
+ if (request.job === undefined || request.sequence === undefined) {
967
+ return;
784
968
  }
785
- for (const request of body.requests) {
786
- const waiting = pending.get(request.seq);
787
- if (waiting && request.status === "pending")
788
- continue;
789
- if (!waiting && request.seq <= newest)
790
- continue;
791
- const entry = waiting ?? timelineEntries.request(request);
792
- entry.request = request;
793
- newest = Math.max(newest, request.seq);
794
- if (request.status === "pending")
795
- pending.set(request.seq, entry);
796
- else
797
- pending.delete(request.seq);
798
- if (!waiting)
799
- timeline.add(entry);
800
- emit({ event: "request", data: request });
801
- emit({ event: "timeline", data: entry });
802
- if (request.job === undefined || request.sequence === undefined)
803
- continue;
804
- requests.set(`${request.job}:${request.service}:${request.sequence}`, request);
805
- for (const key of [...requests.keys()].slice(0, -requestsRemembered))
806
- requests.delete(key);
807
- const job = manifest.jobs.find(({ id }) => id === request.job);
808
- if (job)
809
- updateJob(job);
969
+ const job = manifest.jobs.find(({ id }) => id === request.job);
970
+ if (job) {
971
+ updateJob(job);
810
972
  }
811
- };
973
+ });
812
974
  };
813
975
  const stopConnection = adapter.onConnection((connection) => {
814
976
  record(timelineEntries.connection(connection.since, connection.state, connection.error));
815
977
  emit({ event: "session", data: session() });
816
978
  if (connection.state !== "connected") {
817
- for (const [target, sample] of latest)
979
+ for (const [target, sample] of latest) {
818
980
  latest.set(target, {
819
981
  ...sample,
820
982
  quality: "unavailable",
821
983
  status: connection.error ?? "The controller is not connected"
822
984
  });
823
- for (const job of manifest.jobs)
985
+ }
986
+ for (const job of manifest.jobs) {
824
987
  updateJob(job);
825
- for (const device of manifest.devices)
826
- updateDevice(device.id);
988
+ }
989
+ for (const device of manifest.devices) {
990
+ updateDevice(device);
991
+ }
827
992
  }
828
993
  });
829
994
  const repeat = async (name, intervalMs, work) => {
@@ -833,11 +998,14 @@ var createDebugger = async (options) => {
833
998
  await work();
834
999
  failure = undefined;
835
1000
  } catch (error) {
836
- if (signal.aborted)
1001
+ if (signal.aborted) {
837
1002
  return;
838
- if (message(error) !== failure)
839
- options.log(`${name} failed: ${message(error)}`);
840
- failure = message(error);
1003
+ }
1004
+ const message = errorMessage(error);
1005
+ if (message !== failure) {
1006
+ options.log(`${name} failed: ${message}`);
1007
+ }
1008
+ failure = message;
841
1009
  }
842
1010
  await delay(intervalMs, undefined, { signal }).catch(() => {
843
1011
  return;
@@ -847,7 +1015,7 @@ var createDebugger = async (options) => {
847
1015
  const { server } = options;
848
1016
  const running = server ? [
849
1017
  repeat("Reading the server's telemetry", pullMs, () => pullTelemetry(server)),
850
- repeat("Reading the server's requests", pullMs, pullRequests(server))
1018
+ repeat("Reading the server's requests", pullMs, () => pullRequests(server))
851
1019
  ] : [];
852
1020
  return {
853
1021
  manifest,
@@ -855,7 +1023,7 @@ var createDebugger = async (options) => {
855
1023
  session,
856
1024
  values,
857
1025
  jobs: () => manifest.jobs.map(jobOf),
858
- hardware: () => manifest.devices.map(({ id }) => deviceOf(id)),
1026
+ hardware: () => manifest.devices.map(deviceOf),
859
1027
  timeline: (since) => timeline.since(since),
860
1028
  watchGroups: () => watchGroups.groups(),
861
1029
  saveWatchGroups: (groups) => watchGroups.save(groups),
@@ -879,189 +1047,193 @@ var createDebugger = async (options) => {
879
1047
  };
880
1048
  // packages/debug/src/http.ts
881
1049
  import { readFile as readFile2 } from "node:fs/promises";
882
- import { z as z4 } from "zod";
1050
+ import { z as z3 } from "zod";
883
1051
 
884
1052
  // packages/debug/src/manifest.ts
885
- import { z as z3 } from "zod";
886
- var sourceRange = z3.object({
887
- file: z3.number().int(),
888
- line: z3.number().int(),
889
- column: z3.number().int(),
890
- endLine: z3.number().int(),
891
- endColumn: z3.number().int()
1053
+ import { z as z2 } from "zod";
1054
+ var sourceRange = z2.object({
1055
+ file: z2.number().int(),
1056
+ line: z2.number().int(),
1057
+ column: z2.number().int(),
1058
+ endLine: z2.number().int(),
1059
+ endColumn: z2.number().int()
892
1060
  });
893
- var json = z3.lazy(() => z3.union([
894
- z3.string(),
895
- z3.number(),
896
- z3.boolean(),
897
- z3.null(),
898
- z3.array(json),
899
- z3.record(z3.string(), json)
1061
+ var json = z2.lazy(() => z2.union([
1062
+ z2.string(),
1063
+ z2.number(),
1064
+ z2.boolean(),
1065
+ z2.null(),
1066
+ z2.array(json),
1067
+ z2.record(z2.string(), json)
900
1068
  ]));
901
- var debugField = z3.object({
902
- name: z3.string(),
903
- target: z3.string(),
904
- type: z3.string(),
1069
+ var debugField = z2.object({
1070
+ name: z2.string(),
1071
+ target: z2.string(),
1072
+ type: z2.string(),
905
1073
  declaration: sourceRange.optional(),
906
- generated: z3.literal(true).optional()
1074
+ generated: z2.literal(true).optional()
907
1075
  });
908
- var debugType = z3.discriminatedUnion("kind", [
909
- z3.object({ kind: z3.literal("scalar"), iec: z3.string() }),
910
- z3.object({
911
- kind: z3.literal("enum"),
912
- name: z3.string(),
913
- members: z3.record(z3.string(), z3.number().int())
1076
+ var debugType = z2.discriminatedUnion("kind", [
1077
+ z2.object({ kind: z2.literal("scalar"), iec: z2.string() }),
1078
+ z2.object({
1079
+ kind: z2.literal("enum"),
1080
+ name: z2.string(),
1081
+ members: z2.record(z2.string(), z2.number().int())
914
1082
  }),
915
- z3.object({
916
- kind: z3.literal("record"),
917
- name: z3.string(),
1083
+ z2.object({
1084
+ kind: z2.literal("record"),
1085
+ name: z2.string(),
918
1086
  declaration: sourceRange.optional(),
919
- fields: z3.array(debugField)
1087
+ fields: z2.array(debugField)
920
1088
  }),
921
- z3.object({
922
- kind: z3.literal("array"),
923
- element: z3.string(),
924
- length: z3.number().int(),
925
- lower: z3.number().int()
1089
+ z2.object({
1090
+ kind: z2.literal("array"),
1091
+ element: z2.string(),
1092
+ length: z2.number().int(),
1093
+ lower: z2.number().int()
926
1094
  })
927
1095
  ]);
928
- var debugRoot = z3.object({
929
- path: z3.string(),
930
- target: z3.string(),
931
- type: z3.string(),
1096
+ var debugRoot = z2.object({
1097
+ path: z2.string(),
1098
+ target: z2.string(),
1099
+ type: z2.string(),
932
1100
  declaration: sourceRange.optional(),
933
- readable: z3.boolean(),
934
- generated: z3.literal(true).optional()
1101
+ readable: z2.boolean(),
1102
+ generated: z2.literal(true).optional()
935
1103
  });
936
- var debugConstant = z3.object({
937
- path: z3.string(),
938
- type: z3.string(),
1104
+ var debugConstant = z2.object({
1105
+ path: z2.string(),
1106
+ type: z2.string(),
939
1107
  value: json,
940
1108
  declaration: sourceRange.optional()
941
1109
  });
942
- var debugIo = z3.object({
943
- path: z3.string(),
944
- target: z3.string(),
945
- direction: z3.enum(["input", "output"]),
946
- type: z3.string(),
947
- device: z3.string().optional(),
948
- channel: z3.string().optional(),
949
- address: z3.string().optional(),
950
- inverted: z3.literal(true).optional(),
951
- shared: z3.literal(true).optional()
1110
+ var debugIo = z2.object({
1111
+ path: z2.string(),
1112
+ target: z2.string(),
1113
+ direction: z2.enum(["input", "output"]),
1114
+ type: z2.string(),
1115
+ device: z2.string().optional(),
1116
+ channel: z2.string().optional(),
1117
+ address: z2.string().optional(),
1118
+ inverted: z2.literal(true).optional(),
1119
+ shared: z2.literal(true).optional()
952
1120
  });
953
- var debugChannel = z3.object({
954
- id: z3.string(),
955
- name: z3.string(),
956
- direction: z3.enum(["input", "output"]),
957
- type: z3.string(),
958
- offsetBits: z3.number().int(),
959
- bits: z3.number().int(),
960
- address: z3.string().optional(),
961
- unit: z3.string().optional(),
962
- description: z3.string().optional()
1121
+ var debugChannel = z2.object({
1122
+ id: z2.string(),
1123
+ name: z2.string(),
1124
+ direction: z2.enum(["input", "output"]),
1125
+ type: z2.string(),
1126
+ offsetBits: z2.number().int(),
1127
+ bits: z2.number().int(),
1128
+ address: z2.string().optional(),
1129
+ unit: z2.string().optional(),
1130
+ description: z2.string().optional()
963
1131
  });
964
- var debugDevice = z3.object({
965
- id: z3.string(),
966
- product: z3.string().optional(),
967
- vendor: z3.string().optional(),
968
- productCode: z3.string().optional(),
969
- network: z3.object({ address: z3.string(), rpiMs: z3.number().optional() }).optional(),
970
- connection: z3.string().optional(),
971
- assemblies: z3.array(z3.object({
972
- direction: z3.enum(["input", "output"]),
973
- bytes: z3.number().int(),
974
- address: z3.string().optional(),
975
- raw: z3.string().optional()
1132
+ var debugDevice = z2.object({
1133
+ id: z2.string(),
1134
+ product: z2.string().optional(),
1135
+ vendor: z2.string().optional(),
1136
+ productCode: z2.string().optional(),
1137
+ network: z2.object({ address: z2.string(), rpiMs: z2.number().optional() }).optional(),
1138
+ connection: z2.string().optional(),
1139
+ assemblies: z2.array(z2.object({
1140
+ direction: z2.enum(["input", "output"]),
1141
+ bytes: z2.number().int(),
1142
+ address: z2.string().optional(),
1143
+ raw: z2.string().optional()
976
1144
  })),
977
- health: z3.object({
978
- state: z3.string().optional(),
979
- states: z3.record(z3.string(), z3.number().int()).optional(),
980
- diagnostic: z3.string().optional(),
981
- message: z3.string().optional()
1145
+ health: z2.object({
1146
+ state: z2.string().optional(),
1147
+ states: z2.record(z2.string(), z2.number().int()).optional(),
1148
+ diagnostic: z2.string().optional(),
1149
+ message: z2.string().optional()
982
1150
  }).optional(),
983
- status: z3.record(z3.string(), z3.string()).optional(),
984
- channels: z3.array(debugChannel)
1151
+ status: z2.record(z2.string(), z2.string()).optional(),
1152
+ channels: z2.array(debugChannel)
985
1153
  });
986
- var debugFunction = z3.object({
987
- id: z3.number().int(),
988
- name: z3.string(),
1154
+ var debugFunction = z2.object({
1155
+ id: z2.number().int(),
1156
+ name: z2.string(),
989
1157
  declaration: sourceRange,
990
- instance: z3.string().optional(),
991
- task: z3.string().optional(),
992
- target: z3.string(),
993
- reads: z3.array(z3.object({ range: sourceRange, path: z3.string() }))
1158
+ instance: z2.string().optional(),
1159
+ task: z2.string().optional(),
1160
+ target: z2.string(),
1161
+ reads: z2.array(z2.object({ range: sourceRange, path: z2.string() }))
994
1162
  });
995
- var debugJob = z3.object({
996
- id: z3.number().int(),
997
- name: z3.string(),
1163
+ var debugJob = z2.object({
1164
+ id: z2.number().int(),
1165
+ name: z2.string(),
998
1166
  declaration: sourceRange,
999
- instance: z3.string().optional(),
1000
- target: z3.string(),
1001
- steps: z3.array(z3.object({
1002
- step: z3.number().int(),
1003
- kind: z3.enum(["await", "return", "throw", "statement"]),
1167
+ instance: z2.string().optional(),
1168
+ target: z2.string(),
1169
+ steps: z2.array(z2.object({
1170
+ step: z2.number().int(),
1171
+ kind: z2.enum(["await", "return", "throw", "statement"]),
1004
1172
  range: sourceRange,
1005
- text: z3.string()
1173
+ text: z2.string()
1006
1174
  })),
1007
- startedAt: z3.string().optional(),
1008
- requests: z3.array(z3.object({
1009
- service: z3.string(),
1010
- target: z3.string(),
1011
- sequence: z3.string(),
1012
- job: z3.number().int().optional()
1175
+ startedAt: z2.string().optional(),
1176
+ requests: z2.array(z2.object({
1177
+ service: z2.string(),
1178
+ target: z2.string(),
1179
+ sequence: z2.string(),
1180
+ job: z2.number().int().optional()
1013
1181
  })),
1014
- generated: z3.literal(true).optional()
1182
+ generated: z2.literal(true).optional()
1015
1183
  });
1016
- var debugEventSite = z3.object({
1017
- id: z3.number().int(),
1018
- kind: z3.enum(["state", "decision", "job"]),
1019
- job: z3.number().int().optional(),
1020
- phase: z3.enum(["started", "finished"]).optional(),
1021
- path: z3.string(),
1022
- function: z3.number().int().optional(),
1023
- type: z3.string(),
1184
+ var debugEventSite = z2.object({
1185
+ id: z2.number().int(),
1186
+ kind: z2.enum(["state", "decision", "job"]),
1187
+ job: z2.number().int().optional(),
1188
+ phase: z2.enum(["started", "finished"]).optional(),
1189
+ path: z2.string(),
1190
+ function: z2.number().int().optional(),
1191
+ type: z2.string(),
1024
1192
  range: sourceRange,
1025
- text: z3.string(),
1026
- task: z3.string(),
1027
- operands: z3.array(z3.object({
1028
- text: z3.string(),
1193
+ text: z2.string(),
1194
+ task: z2.string(),
1195
+ operands: z2.array(z2.object({
1196
+ text: z2.string(),
1029
1197
  range: sourceRange,
1030
- type: z3.string(),
1031
- path: z3.string().optional(),
1032
- stable: z3.boolean()
1198
+ type: z2.string(),
1199
+ path: z2.string().optional(),
1200
+ stable: z2.boolean()
1033
1201
  }))
1034
1202
  });
1035
- var debugWriter = z3.object({
1036
- output: z3.string(),
1037
- target: z3.string(),
1038
- sites: z3.array(z3.object({ id: z3.number().int(), range: sourceRange, function: z3.number().int() }))
1203
+ var debugWriter = z2.object({
1204
+ output: z2.string(),
1205
+ target: z2.string(),
1206
+ sites: z2.array(z2.object({
1207
+ id: z2.number().int(),
1208
+ range: sourceRange,
1209
+ function: z2.number().int()
1210
+ }))
1039
1211
  });
1040
- var debugTask = z3.object({
1041
- name: z3.string(),
1042
- intervalMs: z3.number(),
1043
- priority: z3.number().int()
1212
+ var debugTask = z2.object({
1213
+ name: z2.string(),
1214
+ intervalMs: z2.number(),
1215
+ priority: z2.number().int()
1044
1216
  });
1045
- var debugManifest = z3.object({
1046
- kind: z3.literal("tsplc-debug"),
1047
- version: z3.literal(1),
1048
- build: z3.object({
1049
- id: z3.string(),
1050
- target: z3.string(),
1051
- compiler: z3.string()
1217
+ var debugManifest = z2.object({
1218
+ kind: z2.literal("tsplc-debug"),
1219
+ version: z2.literal(1),
1220
+ build: z2.object({
1221
+ id: z2.string(),
1222
+ target: z2.string(),
1223
+ compiler: z2.string()
1052
1224
  }),
1053
- sources: z3.array(z3.string()),
1054
- types: z3.record(z3.string(), debugType),
1055
- roots: z3.array(debugRoot),
1056
- constants: z3.array(debugConstant),
1057
- io: z3.array(debugIo),
1058
- devices: z3.array(debugDevice),
1059
- functions: z3.array(debugFunction),
1060
- jobs: z3.array(debugJob),
1061
- events: z3.array(debugEventSite),
1062
- writers: z3.array(debugWriter),
1063
- tasks: z3.array(debugTask),
1064
- errors: z3.record(z3.string(), z3.string())
1225
+ sources: z2.array(z2.string()),
1226
+ types: z2.record(z2.string(), debugType),
1227
+ roots: z2.array(debugRoot),
1228
+ constants: z2.array(debugConstant),
1229
+ io: z2.array(debugIo),
1230
+ devices: z2.array(debugDevice),
1231
+ functions: z2.array(debugFunction),
1232
+ jobs: z2.array(debugJob),
1233
+ events: z2.array(debugEventSite),
1234
+ writers: z2.array(debugWriter),
1235
+ tasks: z2.array(debugTask),
1236
+ errors: z2.record(z2.string(), z2.string())
1065
1237
  });
1066
1238
 
1067
1239
  // packages/debug/src/ui/index.ts
@@ -1254,10 +1426,12 @@ var build = async () => {
1254
1426
  resolve(dirname2(createRequire(import.meta.url).resolve("@tsplc/debug")), "../src/ui/main.ts")
1255
1427
  ],
1256
1428
  target: "browser",
1429
+ conditions: ["bun"],
1257
1430
  minify: true
1258
1431
  });
1259
- if (!result.success)
1432
+ if (!result.success) {
1260
1433
  throw new AggregateError(result.logs, "Bundling the debugger UI failed");
1434
+ }
1261
1435
  return { html, script: await result.outputs[0].text() };
1262
1436
  };
1263
1437
  var loadUi = () => ui ??= build();
@@ -1270,8 +1444,9 @@ var list = (value) => (value ?? "").split(",").map((item) => item.trim()).filter
1270
1444
  var loadManifest = async (file) => debugManifest.parse(JSON.parse(await readFile2(file, "utf8")));
1271
1445
  var interest = (search) => {
1272
1446
  const jobs = list(search.get("jobs")).map(Number);
1273
- if (!jobs.every(Number.isInteger))
1447
+ if (!jobs.every(Number.isInteger)) {
1274
1448
  throw new DebugError("jobs must be job ids", 400);
1449
+ }
1275
1450
  return {
1276
1451
  watch: list(search.get("watch")),
1277
1452
  jobs,
@@ -1286,8 +1461,9 @@ var stream = (debug, { watch, ...shown }) => {
1286
1461
  start: (controller) => {
1287
1462
  let open = true;
1288
1463
  const write = (text) => {
1289
- if (open)
1464
+ if (open) {
1290
1465
  controller.enqueue(encoder.encode(text));
1466
+ }
1291
1467
  };
1292
1468
  const send = ({ event, data }) => write(`event: ${event}
1293
1469
  data: ${JSON.stringify(data)}
@@ -1307,12 +1483,18 @@ data: ${JSON.stringify(data)}
1307
1483
  `), heartbeatMs);
1308
1484
  const { intervalMs } = debug.session().reading;
1309
1485
  const refresh = setInterval(() => {
1310
- if (debug.session().connection.state !== "connected")
1486
+ if (debug.session().connection.state !== "connected") {
1311
1487
  return;
1488
+ }
1312
1489
  const now = Date.now();
1313
- for (const { sample, sentAt } of latest.values())
1314
- if (now - sentAt >= intervalMs)
1315
- sendSample({ ...sample, receivedAt: new Date(now).toISOString() });
1490
+ for (const { sample, sentAt } of latest.values()) {
1491
+ if (now - sentAt >= intervalMs) {
1492
+ sendSample({
1493
+ ...sample,
1494
+ receivedAt: new Date(now).toISOString()
1495
+ });
1496
+ }
1497
+ }
1316
1498
  }, intervalMs);
1317
1499
  stop = () => {
1318
1500
  open = false;
@@ -1324,7 +1506,12 @@ data: ${JSON.stringify(data)}
1324
1506
  };
1325
1507
  },
1326
1508
  cancel: () => stop()
1327
- }), { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-store" } });
1509
+ }), {
1510
+ headers: {
1511
+ "Content-Type": "text/event-stream",
1512
+ "Cache-Control": "no-store"
1513
+ }
1514
+ });
1328
1515
  };
1329
1516
  var createDebugHandler = (debug, log) => async (request, route) => {
1330
1517
  const { searchParams } = new URL(request.url);
@@ -1360,12 +1547,15 @@ var createDebugHandler = (debug, log) => async (request, route) => {
1360
1547
  }
1361
1548
  return json2({ error: `No route ${key}` }, 404);
1362
1549
  } catch (error) {
1363
- if (error instanceof DebugError)
1550
+ if (error instanceof DebugError) {
1364
1551
  return json2({ error: error.message }, error.status);
1365
- if (error instanceof RangeError || error instanceof SyntaxError)
1552
+ }
1553
+ if (error instanceof RangeError || error instanceof SyntaxError) {
1366
1554
  return json2({ error: error.message }, 400);
1367
- if (error instanceof z4.ZodError)
1368
- return json2({ error: z4.prettifyError(error) }, 400);
1555
+ }
1556
+ if (error instanceof z3.ZodError) {
1557
+ return json2({ error: z3.prettifyError(error) }, 400);
1558
+ }
1369
1559
  log(`${key} failed: ${error instanceof Error ? error.stack : String(error)}`);
1370
1560
  return json2({ error: "The debugger failed; see its log" }, 500);
1371
1561
  }
@@ -1377,8 +1567,9 @@ var serveDebugger = (debug, options) => {
1377
1567
  port: options.port,
1378
1568
  fetch: (request) => {
1379
1569
  const host = request.headers.get("host")?.replace(/:\d+$/, "");
1380
- if (host !== "127.0.0.1" && host !== "localhost")
1570
+ if (host !== "127.0.0.1" && host !== "localhost") {
1381
1571
  return json2({ error: "Use http://127.0.0.1 or http://localhost" }, 403);
1572
+ }
1382
1573
  return handle(request, new URL(request.url).pathname);
1383
1574
  }
1384
1575
  });
@@ -1386,19 +1577,15 @@ var serveDebugger = (debug, options) => {
1386
1577
  // packages/debug/src/login.ts
1387
1578
  import { setTimeout as delay2 } from "node:timers/promises";
1388
1579
 
1389
- class LoginUnavailable extends Error {
1390
- constructor(message, options) {
1391
- super(message, options);
1392
- this.state = options?.state ?? "unavailable";
1393
- }
1394
- state;
1395
- }
1396
- var noLogin = "Live values need an engineering login: run `tsplc login`.";
1397
- var retryMs = 5000;
1398
- var integerTypes = new Set([
1399
- ...["SINT", "INT", "DINT", "LINT", "USINT", "UINT", "UDINT", "ULINT"],
1400
- ...["BYTE", "WORD", "DWORD", "LWORD"]
1401
- ]);
1580
+ // packages/debug/src/login-value.ts
1581
+ import { elementaryType } from "@tsplc/iec";
1582
+ var booleans = {
1583
+ TRUE: true,
1584
+ FALSE: false,
1585
+ "1": true,
1586
+ "0": false
1587
+ };
1588
+ var radixPrefixes = { 2: "0b", 8: "0o", 16: "0x" };
1402
1589
  var timeUnits = {
1403
1590
  d: 86400000,
1404
1591
  h: 3600000,
@@ -1408,106 +1595,154 @@ var timeUnits = {
1408
1595
  us: 0.001,
1409
1596
  ns: 0.000001
1410
1597
  };
1411
- var typeName = (type) => type.kind === "scalar" ? type.iec : type.kind;
1412
- var message2 = (error) => error instanceof Error ? error.message : String(error);
1598
+ var stringEscapes = {
1599
+ l: `
1600
+ `,
1601
+ n: `
1602
+ `,
1603
+ p: "\f",
1604
+ r: "\r",
1605
+ t: "\t"
1606
+ };
1607
+ var withoutTypePrefix = (text) => text.replace(/^(?!L?T(?:IME)?#)[a-z_]+#(?=.)/i, "");
1608
+ var bigIntLiteral = (text) => {
1609
+ const [, radix, digits] = /^(2|8|16)#([0-9a-f_]+)$/i.exec(text) ?? [];
1610
+ if (radix !== undefined && digits !== undefined) {
1611
+ return `${radixPrefixes[radix]}${digits.replaceAll("_", "")}`;
1612
+ }
1613
+ const decimal = text.replaceAll("_", "");
1614
+ return /^[+-]?\d+$/.test(decimal) ? decimal : undefined;
1615
+ };
1413
1616
  var integer = (text) => {
1414
- const radix = /^(2|8|16)#([0-9a-f_]+)$/i.exec(text);
1415
- const digits = radix ? radix[2].replaceAll("_", "") : text.replaceAll("_", "");
1416
- if (!radix && !/^[+-]?\d+$/.test(digits))
1617
+ const literal = bigIntLiteral(text);
1618
+ if (literal === undefined) {
1417
1619
  return;
1418
- const value = BigInt(radix ? `0${{ 2: "b", 8: "o", 16: "x" }[radix[1]]}${digits}` : digits);
1620
+ }
1621
+ const value = BigInt(literal);
1419
1622
  return Number.isSafeInteger(Number(value)) ? Number(value) : String(value);
1420
1623
  };
1624
+ var real = (text) => {
1625
+ const number = Number(text);
1626
+ return text !== "" && !Number.isNaN(number) ? number : undefined;
1627
+ };
1421
1628
  var duration = (text) => {
1422
- const body = /^L?T(?:IME)?#(-?)(.+)$/i.exec(text);
1423
- if (!body)
1629
+ const [, sign, body] = /^L?T(?:IME)?#(-?)(.+)$/i.exec(text) ?? [];
1630
+ if (body === undefined) {
1424
1631
  return;
1425
- const parts = [...body[2].replaceAll("_", "").matchAll(/(\d+(?:\.\d+)?)(ms|us|ns|d|h|m|s)/gi)];
1426
- if (parts.map(([part]) => part).join("") !== body[2].replaceAll("_", ""))
1632
+ }
1633
+ const units = body.replaceAll("_", "");
1634
+ const parts = [...units.matchAll(/(\d+(?:\.\d+)?)(ms|us|ns|d|h|m|s)/gi)];
1635
+ if (parts.map(([part]) => part).join("") !== units) {
1427
1636
  return;
1637
+ }
1428
1638
  const ms = parts.reduce((total, [, amount, unit]) => total + Number(amount) * timeUnits[unit.toLowerCase()], 0);
1429
- return body[1] ? -ms : ms;
1639
+ return sign ? -ms : ms;
1430
1640
  };
1431
1641
  var quoted = (text) => {
1432
- const body = /^(['"])(.*)\1$/s.exec(text);
1433
- if (!body)
1642
+ const [, , body] = /^(['"])(.*)\1$/s.exec(text) ?? [];
1643
+ return body?.replace(/\$([0-9a-f]{2}|[$'"lnprt])/gi, (_, code) => code.length === 2 ? String.fromCharCode(parseInt(code, 16)) : stringEscapes[code.toLowerCase()] ?? code);
1644
+ };
1645
+ var enumValue = (type, text) => {
1646
+ const number = integer(text);
1647
+ if (typeof number === "number") {
1648
+ return number;
1649
+ }
1650
+ const [, name, value] = /^(?:\w+\.)*(\w+)(?:\s*\((-?\d+)\))?$/.exec(text) ?? [];
1651
+ if (name === undefined) {
1434
1652
  return;
1435
- return body[2].replace(/\$([0-9a-f]{2}|[$'"lnprt])/gi, (_, code) => {
1436
- if (code.length === 2)
1437
- return String.fromCharCode(parseInt(code, 16));
1438
- const named = { l: `
1439
- `, n: `
1440
- `, p: "\f", r: "\r", t: "\t" };
1441
- return named[code.toLowerCase()] ?? code;
1442
- });
1653
+ }
1654
+ return type.members[name] ?? (value === undefined ? name : Number(value));
1655
+ };
1656
+ var arrayValue = (type, text) => {
1657
+ const items = text.replace(/^\[/, "").replace(/\]$/, "").trim();
1658
+ if (!items) {
1659
+ return [];
1660
+ }
1661
+ const values = items.split(/\s*,\s*/).map((item) => scalarValue(type.element, item));
1662
+ return values.length === type.length && values.every((value) => value !== undefined) ? values : undefined;
1663
+ };
1664
+ var scalarValue = (iec, text) => {
1665
+ const untyped = withoutTypePrefix(text);
1666
+ const name = iec.toUpperCase();
1667
+ switch (elementaryType(name)?.family) {
1668
+ case "bool":
1669
+ return booleans[untyped.toUpperCase()];
1670
+ case "integer":
1671
+ case "bits":
1672
+ return integer(untyped);
1673
+ case "real":
1674
+ return real(untyped);
1675
+ case "duration":
1676
+ return duration(text);
1677
+ }
1678
+ if (/^W?STRING\b/.test(name)) {
1679
+ return quoted(text) ?? text;
1680
+ }
1681
+ return text;
1443
1682
  };
1444
1683
  var parseLoginValue = (type, text) => {
1445
1684
  const trimmed = text.trim();
1446
- const untyped = trimmed.replace(/^(?!L?T(?:IME)?#)[a-z_]+#(?=.)/i, "");
1447
1685
  switch (type.kind) {
1448
- case "enum": {
1449
- const number = integer(untyped);
1450
- if (typeof number === "number")
1451
- return number;
1452
- const named = /^(?:\w+\.)*(\w+)(?:\s*\((-?\d+)\))?$/.exec(untyped);
1453
- if (!named)
1454
- return;
1455
- return type.members[named[1]] ?? (named[2] === undefined ? named[1] : Number(named[2]));
1456
- }
1457
- case "array": {
1458
- const items = /^\[?(.*?)\]?$/s.exec(untyped)[1].trim();
1459
- if (!items)
1460
- return [];
1461
- const element = { kind: "scalar", iec: type.element };
1462
- const values = items.split(/\s*,\s*/).map((item) => parseLoginValue(element, item));
1463
- return values.length === type.length && values.every((value) => value !== undefined) ? values : undefined;
1464
- }
1686
+ case "enum":
1687
+ return enumValue(type, withoutTypePrefix(trimmed));
1688
+ case "array":
1689
+ return arrayValue(type, withoutTypePrefix(trimmed));
1465
1690
  case "record":
1466
1691
  return;
1467
1692
  case "scalar":
1468
- break;
1469
- }
1470
- const iec = type.iec.toUpperCase();
1471
- if (iec === "BOOL")
1472
- return { TRUE: true, FALSE: false, 1: true, 0: false }[untyped.toUpperCase()];
1473
- if (integerTypes.has(iec))
1474
- return integer(untyped);
1475
- if (iec === "REAL" || iec === "LREAL") {
1476
- const number = Number(untyped);
1477
- return untyped !== "" && !Number.isNaN(number) ? number : undefined;
1693
+ return scalarValue(type.iec, trimmed);
1478
1694
  }
1479
- if (iec === "TIME" || iec === "LTIME")
1480
- return duration(trimmed);
1481
- if (/^W?STRING\b/.test(iec))
1482
- return quoted(trimmed) ?? trimmed;
1483
- return trimmed;
1484
1695
  };
1696
+
1697
+ // packages/debug/src/login.ts
1698
+ class LoginUnavailable extends Error {
1699
+ constructor(message, options) {
1700
+ super(message, options);
1701
+ this.state = options?.state ?? "unavailable";
1702
+ }
1703
+ state;
1704
+ }
1705
+ var noLogin = "Live values need an engineering login: run `tsplc login`.";
1706
+ var retryMs = 5000;
1707
+ var typeName = (type) => type.kind === "scalar" ? type.iec : type.kind;
1485
1708
  var createLoginAdapter = (options) => {
1486
1709
  const samplingMs = options.samplingMs ?? 1000;
1487
- let connection = { state: "connecting", since: new Date().toISOString() };
1710
+ let connection = {
1711
+ state: "connecting",
1712
+ since: new Date().toISOString()
1713
+ };
1488
1714
  const connectionListeners = new Set;
1489
1715
  const subscriptions = new Set;
1490
1716
  const rejected = new Map;
1491
1717
  const controller = new AbortController;
1492
1718
  const setConnection = (state, error) => {
1493
- if (controller.signal.aborted)
1719
+ if (controller.signal.aborted) {
1494
1720
  return;
1495
- if (connection.state === state && connection.error === error)
1721
+ }
1722
+ if (connection.state === state && connection.error === error) {
1496
1723
  return;
1497
- if (state !== "connected")
1724
+ }
1725
+ if (state !== "connected") {
1498
1726
  rejected.clear();
1499
- connection = { state, since: new Date().toISOString(), ...error && { error } };
1500
- for (const listener of connectionListeners)
1727
+ }
1728
+ connection = {
1729
+ state,
1730
+ since: new Date().toISOString(),
1731
+ ...error && { error }
1732
+ };
1733
+ for (const listener of connectionListeners) {
1501
1734
  listener(connection);
1735
+ }
1502
1736
  };
1503
1737
  const split = async (expressions) => {
1504
1738
  try {
1505
1739
  return await options.read(expressions);
1506
1740
  } catch (error) {
1507
- if (error instanceof LoginUnavailable)
1741
+ if (error instanceof LoginUnavailable) {
1508
1742
  throw error;
1743
+ }
1509
1744
  if (expressions.length === 1) {
1510
- rejected.set(expressions[0], message2(error));
1745
+ rejected.set(expressions[0], errorMessage(error));
1511
1746
  return {};
1512
1747
  }
1513
1748
  const half = Math.ceil(expressions.length / 2);
@@ -1521,56 +1756,75 @@ var createLoginAdapter = (options) => {
1521
1756
  try {
1522
1757
  return await options.read(expressions);
1523
1758
  } catch (error) {
1524
- if (error instanceof LoginUnavailable)
1759
+ if (error instanceof LoginUnavailable) {
1525
1760
  throw error;
1761
+ }
1526
1762
  await options.read([options.probe]);
1527
1763
  return split(expressions);
1528
1764
  }
1529
1765
  };
1530
- const read = async (targets) => {
1531
- const unique = [...new Map(targets.map((target) => [target.target, target])).values()];
1532
- const expressions = unique.map(({ target }) => target).filter((target) => !rejected.has(target));
1533
- const samples = new Map;
1534
- let values;
1766
+ const readTexts = async (expressions) => {
1535
1767
  const overdue = setTimeout(() => setConnection("lost", "The engineering login stopped answering"), 3 * samplingMs);
1536
1768
  try {
1537
- values = expressions.length ? await readText(expressions) : {};
1769
+ const texts = expressions.length ? await readText(expressions) : {};
1538
1770
  setConnection("connected");
1771
+ return { texts };
1539
1772
  } catch (error) {
1540
- setConnection(error instanceof LoginUnavailable ? error.state : "lost", message2(error));
1541
- const receivedAt = new Date().toISOString();
1542
- for (const { target } of unique)
1543
- samples.set(target, {
1544
- value: null,
1545
- quality: "unavailable",
1546
- status: message2(error),
1547
- receivedAt
1548
- });
1549
- return samples;
1773
+ setConnection(error instanceof LoginUnavailable ? error.state : "lost", errorMessage(error));
1774
+ return { error: errorMessage(error) };
1550
1775
  } finally {
1551
1776
  clearTimeout(overdue);
1552
1777
  }
1778
+ };
1779
+ const parsedSample = ({ target, type }, text, receivedAt) => {
1780
+ const value = text === undefined ? undefined : parseLoginValue(type, text);
1781
+ if (value !== undefined) {
1782
+ return { value, quality: "good", status: "Good", receivedAt };
1783
+ }
1784
+ const reason = text === undefined ? "Not returned by the login" : `Not a ${typeName(type)}`;
1785
+ return {
1786
+ value: text ?? null,
1787
+ quality: "bad",
1788
+ status: rejected.get(target) ?? reason,
1789
+ receivedAt
1790
+ };
1791
+ };
1792
+ const read = async (targets) => {
1793
+ const unique = [
1794
+ ...new Map(targets.map((target) => [target.target, target])).values()
1795
+ ];
1796
+ const result = await readTexts(unique.map(({ target }) => target).filter((target) => !rejected.has(target)));
1553
1797
  const receivedAt = new Date().toISOString();
1554
- for (const { target, type } of unique) {
1555
- const text = values[target];
1556
- const refused = rejected.get(target);
1557
- const value = text === undefined ? undefined : parseLoginValue(type, text);
1558
- samples.set(target, value !== undefined ? { value, quality: "good", status: "Good", receivedAt } : {
1559
- value: text ?? null,
1560
- quality: "bad",
1561
- status: refused ?? (text === undefined ? "Not returned by the login" : `Not a ${typeName(type)}`),
1562
- receivedAt
1563
- });
1798
+ if ("error" in result) {
1799
+ return new Map(unique.map(({ target }) => [
1800
+ target,
1801
+ {
1802
+ value: null,
1803
+ quality: "unavailable",
1804
+ status: result.error,
1805
+ receivedAt
1806
+ }
1807
+ ]));
1808
+ }
1809
+ return new Map(unique.map((read) => [
1810
+ read.target,
1811
+ parsedSample(read, result.texts[read.target], receivedAt)
1812
+ ]));
1813
+ };
1814
+ const pauseAfter = (tookMs) => {
1815
+ if (connection.state !== "connected" && connection.state !== "connecting") {
1816
+ return retryMs;
1564
1817
  }
1565
- return samples;
1818
+ return tookMs > samplingMs ? tookMs : samplingMs - tookMs;
1566
1819
  };
1567
1820
  const poll = async () => {
1568
1821
  while (!controller.signal.aborted) {
1569
1822
  const started = Date.now();
1570
1823
  const due = [...subscriptions].filter(({ due }) => due <= started + samplingMs / 2);
1571
1824
  if (due.length) {
1572
- for (const subscription of due)
1825
+ for (const subscription of due) {
1573
1826
  subscription.due = started + subscription.intervalMs;
1827
+ }
1574
1828
  const stopped = Promise.withResolvers();
1575
1829
  const stop = () => stopped.resolve();
1576
1830
  controller.signal.addEventListener("abort", stop, { once: true });
@@ -1578,16 +1832,18 @@ var createLoginAdapter = (options) => {
1578
1832
  read(due.flatMap(({ targets }) => targets)),
1579
1833
  stopped.promise
1580
1834
  ]).finally(() => controller.signal.removeEventListener("abort", stop));
1581
- if (!samples || controller.signal.aborted)
1835
+ if (!samples || controller.signal.aborted) {
1582
1836
  break;
1583
- for (const subscription of due)
1584
- if (subscriptions.has(subscription))
1585
- for (const { target } of subscription.targets)
1837
+ }
1838
+ for (const subscription of due) {
1839
+ if (subscriptions.has(subscription)) {
1840
+ for (const { target } of subscription.targets) {
1586
1841
  subscription.listener(target, samples.get(target));
1842
+ }
1843
+ }
1844
+ }
1587
1845
  }
1588
- const took = Date.now() - started;
1589
- const waiting = connection.state !== "connected" && connection.state !== "connecting";
1590
- await delay2(waiting ? retryMs : took > samplingMs ? took : samplingMs - took, undefined, {
1846
+ await delay2(pauseAfter(Date.now() - started), undefined, {
1591
1847
  signal: controller.signal
1592
1848
  }).catch(() => {
1593
1849
  return;
@@ -1619,20 +1875,19 @@ var createLoginAdapter = (options) => {
1619
1875
  };
1620
1876
  };
1621
1877
  // packages/debug/src/opcua.ts
1622
- import { resolve as resolve2 } from "node:path";
1623
- import { createMonitor } from "@tsplc/opcua";
1878
+ import {
1879
+ createMonitor,
1880
+ monitorConnection
1881
+ } from "@tsplc/opcua";
1624
1882
  var opcUaConnection = (monitor, options) => {
1625
- const password = monitor.passwordEnv && options.env[monitor.passwordEnv];
1626
- if (monitor.username && !password)
1883
+ const password = monitor.passwordEnv ? options.env[monitor.passwordEnv] : undefined;
1884
+ if (monitor.username && !password) {
1627
1885
  throw new Error(monitor.passwordEnv ? `Set ${monitor.passwordEnv} to the OPC UA password of ${monitor.username}.` : "Configure monitor.passwordEnv for the OPC UA user.");
1628
- return {
1629
- endpoint: monitor.endpoint,
1630
- namespaceUri: monitor.namespaceUri,
1631
- certificateDirectory: resolve2(options.root, monitor.certificateDirectory),
1632
- security: monitor.securityPolicy,
1633
- securityMode: monitor.securityMode === "Sign" ? "Sign" : "SignAndEncrypt",
1634
- ...monitor.username && password && { credentials: { username: monitor.username, password } }
1635
- };
1886
+ }
1887
+ return monitorConnection(monitor, {
1888
+ root: options.root,
1889
+ password: password ?? null
1890
+ });
1636
1891
  };
1637
1892
  var createOpcUaAdapter = (options) => {
1638
1893
  const monitor = createMonitor(options);
@@ -1651,10 +1906,7 @@ var createOpcUaAdapter = (options) => {
1651
1906
  samplingMs: monitor.samplingMs,
1652
1907
  connection: monitor.status,
1653
1908
  onConnection: monitor.onStatus,
1654
- read: async (targets) => new Map(Object.entries(await monitor.read(names(targets))).map(([target, reading]) => [
1655
- target,
1656
- sample(reading)
1657
- ])),
1909
+ read: async (targets) => new Map(Object.entries(await monitor.read(names(targets))).map(([target, reading]) => [target, sample(reading)])),
1658
1910
  subscribe: (targets, listener) => monitor.watch(names(targets), (target, reading) => listener(target, sample(reading))),
1659
1911
  close: async () => {
1660
1912
  controller.abort();
@@ -1677,5 +1929,5 @@ export {
1677
1929
  timelineEntries
1678
1930
  };
1679
1931
 
1680
- //# debugId=19B08BFA048A0C5264756E2164756E21
1932
+ //# debugId=D1ADCD561935AA0C64756E2164756E21
1681
1933
  //# sourceMappingURL=index.js.map