@requence/service 0.8.2-edge.1154 → 0.8.2-edge.1155
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +258 -0
- package/build/{chunk-974rc2jy.js → chunk-gpyt51xn.js} +164 -3
- package/build/{chunk-974rc2jy.js.map → chunk-gpyt51xn.js.map} +7 -4
- package/build/cli.js +17 -30
- package/build/cli.js.map +3 -3
- package/build/index.js +537 -93
- package/build/index.js.map +17 -9
- package/build/types/helpers/src/index.d.ts +4 -0
- package/build/types/helpers/src/index.d.ts.map +1 -1
- package/build/types/helpers/src/protocol/NodeTree.d.ts +20 -0
- package/build/types/helpers/src/protocol/NodeTree.d.ts.map +1 -1
- package/build/types/helpers/src/protocol/command.d.ts +74 -13
- package/build/types/helpers/src/protocol/command.d.ts.map +1 -1
- package/build/types/helpers/src/protocol/update.d.ts +128 -10
- package/build/types/helpers/src/protocol/update.d.ts.map +1 -1
- package/build/types/helpers/src/utils/matchSchema.d.ts +14 -0
- package/build/types/helpers/src/utils/matchSchema.d.ts.map +1 -1
- package/build/types/helpers/src/utils/serviceRender.d.ts +73 -0
- package/build/types/helpers/src/utils/serviceRender.d.ts.map +1 -0
- package/build/types/helpers/src/utils/surfaceComponent.d.ts +116 -0
- package/build/types/helpers/src/utils/surfaceComponent.d.ts.map +1 -0
- package/build/types/helpers/src/utils/surfaceHandler.d.ts +171 -0
- package/build/types/helpers/src/utils/surfaceHandler.d.ts.map +1 -0
- package/build/types/helpers/src/utils/uiRebuild.d.ts +48 -0
- package/build/types/helpers/src/utils/uiRebuild.d.ts.map +1 -0
- package/build/types/service/src/boundary.d.ts +14 -1
- package/build/types/service/src/boundary.d.ts.map +1 -1
- package/build/types/service/src/createAmqpConnection.d.ts +55 -0
- package/build/types/service/src/createAmqpConnection.d.ts.map +1 -1
- package/build/types/service/src/errors.d.ts +11 -0
- package/build/types/service/src/errors.d.ts.map +1 -1
- package/build/types/service/src/helpers.d.ts +68 -0
- package/build/types/service/src/helpers.d.ts.map +1 -1
- package/build/types/service/src/index.d.ts +2 -0
- package/build/types/service/src/index.d.ts.map +1 -1
- package/build/types/service/src/surfaceHandler.d.ts +36 -0
- package/build/types/service/src/surfaceHandler.d.ts.map +1 -0
- package/build/types/service/src/surfaceUi.d.ts +141 -0
- package/build/types/service/src/surfaceUi.d.ts.map +1 -0
- package/build/types/service/src/transformer.d.ts +14 -0
- package/build/types/service/src/transformer.d.ts.map +1 -1
- package/build/types/service/src/ui.d.ts +247 -0
- package/build/types/service/src/ui.d.ts.map +1 -0
- package/build/types/service/src/uiWatch.d.ts +68 -0
- package/build/types/service/src/uiWatch.d.ts.map +1 -0
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -234,6 +234,244 @@ await service.act(messageKey, async (api) => {
|
|
|
234
234
|
|
|
235
235
|
`act()` also supports returning a value or an iterable directly from the actor function, which behaves the same as calling `api.send()` for each value.
|
|
236
236
|
|
|
237
|
+
### One instance per defer key — your lock, not ours
|
|
238
|
+
|
|
239
|
+
> **You own the exclusivity.** You own the pending-key storage, so you own the
|
|
240
|
+
> lock. A single connection refuses a key it is already acting on, but that check
|
|
241
|
+
> is per-connection and in memory: **two replicas that both load the pending keys
|
|
242
|
+
> on boot will both `act()` the same key**, both register callbacks, and both
|
|
243
|
+
> render — and whoever rendered last takes the surface over.
|
|
244
|
+
|
|
245
|
+
Requence does not arbitrate this: enforcing it would need cross-replica holder
|
|
246
|
+
state plus a takeover path, so a dead holder could not block a key forever.
|
|
247
|
+
Instead the symptom is made loud — when a render replaces a surface that is still
|
|
248
|
+
live on another connection, the server logs a `surface takeover` error naming both
|
|
249
|
+
connections.
|
|
250
|
+
|
|
251
|
+
Take a lease on the key in your own store before acting (`SELECT … FOR UPDATE SKIP
|
|
252
|
+
LOCKED`, a Redis `SET NX`, one designated attacher), and only act on keys you hold.
|
|
253
|
+
|
|
254
|
+
`act()` is also the live window of a deferred node, so it can draw and redraw the
|
|
255
|
+
node's [surface](#surfaces--a-ui-of-your-own-on-the-node) — see
|
|
256
|
+
[`api.render`](#apirenderui-props-options--rendering-from-act).
|
|
257
|
+
|
|
258
|
+
## Surfaces — a UI of your own on the node
|
|
259
|
+
|
|
260
|
+
A service can ship its own UI component and have Requence render it **on the
|
|
261
|
+
publishing node** in the task canvas, inside a sandboxed iframe. There is no UI
|
|
262
|
+
protocol: the contract is **props**, and a function inside them arrives in the
|
|
263
|
+
component as an invokable function.
|
|
264
|
+
|
|
265
|
+
This section is the reference for surface behaviour in every SDK — the Python SDK
|
|
266
|
+
(`python/README.md`) publishes the same render payload and answers the same
|
|
267
|
+
invocation. The design authority is
|
|
268
|
+
[`knowledge/service-ui-surfaces.md`](../../knowledge/service-ui-surfaces.md).
|
|
269
|
+
|
|
270
|
+
### `createUI(url, defaults?)` — where a component comes from
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
const ui = createUI<{ title: string; at: string; dense: boolean }>(
|
|
274
|
+
// A URL, so it resolves against THIS module — not against whatever directory
|
|
275
|
+
// the service was started from. `{ url, id }` instead overrides the stable id,
|
|
276
|
+
// which defaults to the built file's name.
|
|
277
|
+
new URL('./dist/panel.js', import.meta.url),
|
|
278
|
+
// Optional default props: what does not change from one message to the next.
|
|
279
|
+
{ dense: true },
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
// A render carries only what differs.
|
|
283
|
+
ctx.render(ui, { title: 'Processing', at: new Date().toISOString() })
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**A render is a patch.** What the surface shows is the *fold* of three layers —
|
|
287
|
+
the UI's defaults, everything previously rendered onto that surface, and this
|
|
288
|
+
render — so redrawing one field takes one prop and leaves the rest standing:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
ctx.render(ui, { title: 'Processing', at: now() })
|
|
292
|
+
ctx.render(ui, { at: now() }) // still says "Processing"
|
|
293
|
+
ctx.render(ui, { title: 'Done' }) // and still carries the last `at`
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The merge is **shallow**: a nested object is replaced wholesale, so changing part
|
|
297
|
+
of one means restating that subtree. Nothing is ever removed — an omitted prop
|
|
298
|
+
means "nothing to say about this", which is what makes a render a patch — so a
|
|
299
|
+
prop is taken back by saying something else about it (`{ error: null }`).
|
|
300
|
+
|
|
301
|
+
Defaults are the **weakest** layer: they are what a surface shows until some
|
|
302
|
+
render says otherwise, and a render that overrides one keeps it overridden for
|
|
303
|
+
every later render that stays silent about it. The flip side of all of this is
|
|
304
|
+
that every render's props are optional in TypeScript, so it can no longer catch a
|
|
305
|
+
*first* render that forgot half of them — completing the fold is yours.
|
|
306
|
+
|
|
307
|
+
The bundle is read and hashed once, at registration, and capped at 1 MiB — an
|
|
308
|
+
oversized component fails at startup rather than on its first render in
|
|
309
|
+
production. Requence never bundles for you: build the component with your own
|
|
310
|
+
toolchain into one ES module with a default `mount(element, ctx)` export.
|
|
311
|
+
|
|
312
|
+
**In React, you do not write `mount`.** Leave `react`, `react-dom/client`,
|
|
313
|
+
`react/jsx-runtime` and `@requence/surface-ui` external and the surface host
|
|
314
|
+
resolves them from its shared runtime — which also ships `createSurface`, so the
|
|
315
|
+
component is one default export and the surface state is its own props:
|
|
316
|
+
|
|
317
|
+
```tsx
|
|
318
|
+
import { Button, createSurface, Stack, Text } from '@requence/surface-ui'
|
|
319
|
+
|
|
320
|
+
import type { PropsOf } from '@requence/service'
|
|
321
|
+
import type { buttonUI } from './button.ts'
|
|
322
|
+
|
|
323
|
+
export default createSurface<PropsOf<typeof buttonUI>>(({ props, canInvoke }) => (
|
|
324
|
+
<Stack space={2}>
|
|
325
|
+
<Button disabled={!canInvoke} onClick={() => props.onClick()}>
|
|
326
|
+
{props.label}
|
|
327
|
+
</Button>
|
|
328
|
+
<Text variant="muted">{props.clicks} dispatched</Text>
|
|
329
|
+
</Stack>
|
|
330
|
+
))
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
No root, no `useState`, no `useEffect`, no teardown to forget. Anything nested
|
|
334
|
+
reads the same state with `useSurface<Props>()` instead of having `canInvoke`
|
|
335
|
+
drilled down to it. A bundle that ships its own React writes `mount` itself —
|
|
336
|
+
create a root, subscribe to `ctx.onUpdate`, unmount on teardown.
|
|
337
|
+
|
|
338
|
+
Every component also has a **stable id** — its built file's name, or whatever
|
|
339
|
+
`createUI({ url, id })` says. It survives rebuilds and restarts, which the content
|
|
340
|
+
hash deliberately does not, and it is what a durable handler is addressed by. Two
|
|
341
|
+
handles claiming one id is a startup error, so two `index.js`-shaped components —
|
|
342
|
+
or one file registered twice with different defaults — need explicit ids.
|
|
343
|
+
|
|
344
|
+
### Handler props — where you write the function decides how long it can be pressed
|
|
345
|
+
|
|
346
|
+
A function in a surface's props arrives in the component as an invokable function.
|
|
347
|
+
There are two kinds, and you pick by **where you write it**:
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
const askUI = createUI<{ question: string; onSubmit(answer: string): void }>(
|
|
351
|
+
new URL('./dist/ask.js', import.meta.url),
|
|
352
|
+
{
|
|
353
|
+
// DURABLE. Module scope, so any instance of this version can run it — this
|
|
354
|
+
// one, or the one that replaces it in a redeploy. An invocation waits in the
|
|
355
|
+
// version's handler queue for ~60s, so the handler below is free to defer
|
|
356
|
+
// and exit, and a rolling restart is invisible to the viewer.
|
|
357
|
+
onSubmit(answer) {
|
|
358
|
+
void record(answer)
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
const service = createService('1.0.0', function* (ctx) {
|
|
364
|
+
ctx.render(askUI, {
|
|
365
|
+
question: 'ship it?',
|
|
366
|
+
// CONNECTION-BOUND. Closes over this running handler, so only this process
|
|
367
|
+
// can serve it and it dies when the handler returns — which is also why it
|
|
368
|
+
// needs a generator handler.
|
|
369
|
+
onSubmit: (answer) => ctx.debug.log(answer),
|
|
370
|
+
})
|
|
371
|
+
})
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
A durable handler gets **no injected context**: it reaches `service.act()`, your
|
|
375
|
+
stores, anything else through its own closure, exactly as a module-scope function
|
|
376
|
+
would in any other process of the version. An invocation naming a component or a
|
|
377
|
+
prop path this process does not have is reported on stderr and dropped — the click
|
|
378
|
+
behind it was accepted long before, so there is no request left to fail.
|
|
379
|
+
|
|
380
|
+
Consuming the handler queue is automatic. It is `service-<name>@<version>-handlers`,
|
|
381
|
+
one per version, created by Requence when the version is provisioned and asserted
|
|
382
|
+
again by the SDK on connect.
|
|
383
|
+
|
|
384
|
+
**A durable handler still needs someone home.** The node can wait days; the
|
|
385
|
+
*click* cannot. While no instance of the version is running, every handler prop of
|
|
386
|
+
that version's surfaces reports `reachable: false` and the component withholds its
|
|
387
|
+
control — and an invocation that does get published expires in the queue after
|
|
388
|
+
~60s and is reported back to the viewer beside the surface, in Requence's own
|
|
389
|
+
error box, rather than running whenever somebody next deploys. So
|
|
390
|
+
the queue rides out a redeploy, not a weekend. Scale your version to zero and the
|
|
391
|
+
buttons it drew go quiet until you scale it back up, at which point they come back
|
|
392
|
+
on the open page with no reload.
|
|
393
|
+
|
|
394
|
+
### `api.render(ui, props, options?)` — rendering from `act()`
|
|
395
|
+
|
|
396
|
+
A deferring handler cannot register a *connection-bound* handler at all (they die
|
|
397
|
+
the moment it returns), so a surface whose buttons are written inline has to be
|
|
398
|
+
rendered from the [actor](#deferred-delivery-via-serviceact) — where they belong to
|
|
399
|
+
the `act()` call and live exactly as long as it does. (A handler written in the UI's
|
|
400
|
+
own defaults is durable and needs none of this.)
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
const ui = createUI(new URL('./dist/button.js', import.meta.url))
|
|
404
|
+
|
|
405
|
+
const service = createService('1.0.0', (ctx) => {
|
|
406
|
+
pending.save(ctx.defer('waiting for a click'))
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
await service.open()
|
|
410
|
+
|
|
411
|
+
// On boot AND after each defer — this loop is what survives a restart.
|
|
412
|
+
for (const key of await pending.all()) {
|
|
413
|
+
void attach(key)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async function attach(key: string) {
|
|
417
|
+
const clicks = asyncEventEmitter<{ timestamp: number }>()
|
|
418
|
+
|
|
419
|
+
await service.act(key, async (api) => {
|
|
420
|
+
api.render(ui, {
|
|
421
|
+
onClick: createCallback<{ timestamp: number }>((arg) => clicks.push(arg)),
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
for await (const click of clicks) {
|
|
425
|
+
api.send(click) // completes the deferred node
|
|
426
|
+
break
|
|
427
|
+
}
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
await pending.remove(key)
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Restart the process, reload the keys, `act()` again: the new render — fresh
|
|
435
|
+
connection, fresh callbacks — replaces the stale surface, and the browser needs to
|
|
436
|
+
know nothing about it. `props` is type-checked against `createUI<Props>` exactly as
|
|
437
|
+
it is for `ctx.render`.
|
|
438
|
+
|
|
439
|
+
### Three rules a component author does not get to skip
|
|
440
|
+
|
|
441
|
+
The design took these deliberately, and none of them is enforced for you.
|
|
442
|
+
|
|
443
|
+
**1. A handler's argument comes from the browser, and it is forgeable.** A viewer
|
|
444
|
+
with `task: UPDATE` can invoke any handler reference the stored props hold, with
|
|
445
|
+
anything under the byte cap — the component that was rendered is not what sends it.
|
|
446
|
+
Requence bounds *which* handler (only a reference some render actually published)
|
|
447
|
+
and *how big* the argument is, and deliberately nothing else: no schema declares an
|
|
448
|
+
author's props anywhere on the wire, and a handler is ordinary service code that can
|
|
449
|
+
check its own input. **Validate the argument in the handler**, exactly as you would
|
|
450
|
+
a webhook body — and keep anything the handler must not be *told* in its closure
|
|
451
|
+
rather than in the props. A durable handler reading `arg.deferKey` is fine (a key is
|
|
452
|
+
single-use and belongs to that node); the same handler reading `arg.amount` or
|
|
453
|
+
`arg.userId` is not.
|
|
454
|
+
|
|
455
|
+
**2. An interaction is *accepted*, not awaited.** The click returns the moment the
|
|
456
|
+
command is on the task's shard: a handler prop returns `void`, there is no promise
|
|
457
|
+
and no request left to answer — a durable handler may run minutes later, in a
|
|
458
|
+
process the viewer never had a connection to. **Feedback is a re-render**: state
|
|
459
|
+
what happened in the props (`{ submitted: true }`, `{ error: 'unknown address' }`)
|
|
460
|
+
and let the component read them through `ctx.onUpdate`. Two consequences a component
|
|
461
|
+
must respect — it may not lock itself optimistically (nothing on the server changed,
|
|
462
|
+
so no update arrives to unlock it), and a design that needs a synchronous answer
|
|
463
|
+
needs a different shape.
|
|
464
|
+
|
|
465
|
+
**3. Network egress is open, and the sandbox denies credentials.** A component may
|
|
466
|
+
`fetch` any URL and load images, fonts and media from any host (plus `data:` and
|
|
467
|
+
`blob:`); only `script-src` stays closed, so it can send data out but cannot pull
|
|
468
|
+
code in. Its origin is opaque, so it carries **no cookies** and `Origin: null` — it
|
|
469
|
+
acts on its own behalf, never as the viewer, and cannot reach an authenticated
|
|
470
|
+
endpoint of your deployment. Therefore **never put a credential in props for the
|
|
471
|
+
component to fetch with**: the service fetches and re-renders. The flip side is the
|
|
472
|
+
accepted cost — third-party component code holds the props you sent it and can post
|
|
473
|
+
them anywhere, unaudited.
|
|
474
|
+
|
|
237
475
|
## Dev Overlay
|
|
238
476
|
|
|
239
477
|
When developing locally alongside a production service, use a **dev token** (your personal access token) to register a dev instance. Requence routes your own tasks to the dev instance instead of the production pool:
|
|
@@ -252,6 +490,26 @@ createService(
|
|
|
252
490
|
|
|
253
491
|
The dev token is resolved in the same order as the access token — option, `REQUENCE_SERVICE_DEV_TOKEN` / `REQUENCE_DEV_TOKEN` env var, or `requence.service.devToken` / `requence.devToken` in `package.json`.
|
|
254
492
|
|
|
493
|
+
### Component reload
|
|
494
|
+
|
|
495
|
+
A dev instance watches the built file behind every `createUI`. Rebuild it and the
|
|
496
|
+
frame swaps to the new bundle in place — same props, same handlers, same node,
|
|
497
|
+
no re-render and no new task.
|
|
498
|
+
|
|
499
|
+
It reaches every surface your dev session ever drew, and nothing else: a live
|
|
500
|
+
panel, a form on a node that is still deferred, and a surface on a task that
|
|
501
|
+
finished last week all swap alike, while a production surface of the same
|
|
502
|
+
component is untouched. Restarting your service does not strand the surfaces it
|
|
503
|
+
drew a moment before.
|
|
504
|
+
|
|
505
|
+
One limit worth knowing: what swaps is the code, never the props. A component
|
|
506
|
+
that only shows what the last render sent it will show exactly that, until your
|
|
507
|
+
service renders again.
|
|
508
|
+
|
|
509
|
+
While your dev process is stopped, the surfaces it drew report their handlers as
|
|
510
|
+
unreachable and say so in those terms — *the dev process that drew this surface is
|
|
511
|
+
not running* — not as a missing deployment. Start it again and they come back.
|
|
512
|
+
|
|
255
513
|
## CLI — Generate Types
|
|
256
514
|
|
|
257
515
|
The package ships with a CLI that generates TypeScript types derived from the schemas you defined in the Requence UI (input, configuration, and outputs):
|
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
function __accessProp(key) {
|
|
7
|
+
return this[key];
|
|
8
|
+
}
|
|
9
|
+
var __toESMCache_node;
|
|
10
|
+
var __toESMCache_esm;
|
|
11
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
12
|
+
var canCache = mod != null && typeof mod === "object";
|
|
13
|
+
if (canCache) {
|
|
14
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
15
|
+
var cached = cache.get(mod);
|
|
16
|
+
if (cached)
|
|
17
|
+
return cached;
|
|
18
|
+
}
|
|
19
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
20
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
21
|
+
for (let key of __getOwnPropNames(mod))
|
|
22
|
+
if (!__hasOwnProp.call(to, key))
|
|
23
|
+
__defProp(to, key, {
|
|
24
|
+
get: __accessProp.bind(mod, key),
|
|
25
|
+
enumerable: true
|
|
26
|
+
});
|
|
27
|
+
if (canCache)
|
|
28
|
+
cache.set(mod, to);
|
|
29
|
+
return to;
|
|
30
|
+
};
|
|
31
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
32
|
+
|
|
1
33
|
// ../helpers/src/utils/obfuscate.ts
|
|
2
34
|
var separator = "";
|
|
3
35
|
var base65Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$";
|
|
@@ -50,6 +82,122 @@ function deobfuscate(str) {
|
|
|
50
82
|
return decodedString.split(separator);
|
|
51
83
|
}
|
|
52
84
|
|
|
85
|
+
// ../logger/src/colors.ts
|
|
86
|
+
var STYLES = {
|
|
87
|
+
bold: [1, 22],
|
|
88
|
+
black: [30, 39],
|
|
89
|
+
red: [31, 39],
|
|
90
|
+
green: [32, 39],
|
|
91
|
+
yellow: [33, 39],
|
|
92
|
+
blue: [34, 39],
|
|
93
|
+
magenta: [35, 39],
|
|
94
|
+
cyan: [36, 39],
|
|
95
|
+
white: [37, 39],
|
|
96
|
+
gray: [90, 39],
|
|
97
|
+
bgBlack: [40, 49],
|
|
98
|
+
bgRed: [41, 49],
|
|
99
|
+
bgGreen: [42, 49],
|
|
100
|
+
bgYellow: [43, 49],
|
|
101
|
+
bgBlue: [44, 49],
|
|
102
|
+
bgMagenta: [45, 49],
|
|
103
|
+
bgCyan: [46, 49],
|
|
104
|
+
bgWhite: [47, 49]
|
|
105
|
+
};
|
|
106
|
+
var forceColor = process.env.FORCE_COLOR;
|
|
107
|
+
var colorEnabled = !process.env.NO_COLOR && process.env.TERM !== "dumb" && (forceColor === undefined ? Boolean(process.stdout?.isTTY) : forceColor !== "0" && forceColor !== "false");
|
|
108
|
+
var trueColor = forceColor === "3" || /truecolor|24bit/i.test(process.env.COLORTERM ?? "");
|
|
109
|
+
function sequence(code) {
|
|
110
|
+
return `\x1B[${code}m`;
|
|
111
|
+
}
|
|
112
|
+
function createStyler(codes) {
|
|
113
|
+
const styler = (...args) => {
|
|
114
|
+
let text = args.map(String).join(" ");
|
|
115
|
+
if (!colorEnabled || codes.length === 0) {
|
|
116
|
+
return text;
|
|
117
|
+
}
|
|
118
|
+
let open = "";
|
|
119
|
+
let close = "";
|
|
120
|
+
for (const [start, end] of codes) {
|
|
121
|
+
open += start;
|
|
122
|
+
text = text.includes(end) ? text.replaceAll(end, end + start) : text;
|
|
123
|
+
close = end + close;
|
|
124
|
+
}
|
|
125
|
+
return `${open}${text}${close}`;
|
|
126
|
+
};
|
|
127
|
+
for (const name of Object.keys(STYLES)) {
|
|
128
|
+
Object.defineProperty(styler, name, {
|
|
129
|
+
get: () => {
|
|
130
|
+
const [open, close] = STYLES[name];
|
|
131
|
+
return createStyler([...codes, [sequence(open), sequence(close)]]);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
Object.defineProperty(styler, "hex", {
|
|
136
|
+
value: (value, fallback) => {
|
|
137
|
+
if (!trueColor) {
|
|
138
|
+
const [open, close] = STYLES[fallback];
|
|
139
|
+
return createStyler([...codes, [sequence(open), sequence(close)]]);
|
|
140
|
+
}
|
|
141
|
+
const hex = value.replace("#", "");
|
|
142
|
+
const int = Number.parseInt(hex, 16);
|
|
143
|
+
const r = int >> 16 & 255;
|
|
144
|
+
const g = int >> 8 & 255;
|
|
145
|
+
const b = int & 255;
|
|
146
|
+
return createStyler([
|
|
147
|
+
...codes,
|
|
148
|
+
[`\x1B[38;2;${r};${g};${b}m`, sequence(39)]
|
|
149
|
+
]);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return styler;
|
|
153
|
+
}
|
|
154
|
+
var colors = createStyler([]);
|
|
155
|
+
|
|
156
|
+
// ../logger/src/format.ts
|
|
157
|
+
var instanceName = process.env.INSTANCE_NAME || null;
|
|
158
|
+
function withLevel(level, message) {
|
|
159
|
+
let prefix = `??${level}??`;
|
|
160
|
+
let highlight = (str) => str;
|
|
161
|
+
switch (level) {
|
|
162
|
+
case "info": {
|
|
163
|
+
prefix = colors.bgGreen.black.bold(" INFO ");
|
|
164
|
+
highlight = (str) => colors.green.bold(str);
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
case "warn": {
|
|
168
|
+
prefix = colors.bgYellow.black.bold(" WARN ");
|
|
169
|
+
highlight = (str) => colors.yellow.bold(str);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case "error": {
|
|
173
|
+
prefix = colors.bgRed.black.bold(" ERROR ");
|
|
174
|
+
highlight = (str) => colors.red.bold(str);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
case "debug": {
|
|
178
|
+
prefix = colors.bgMagenta.black.bold(" DEBUG ");
|
|
179
|
+
highlight = (str) => colors.magenta.bold(str);
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return `${prefix} ${String(message).replace(/\|(.*?)\|/g, (_, str) => highlight(str))}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ../logger/src/simple.ts
|
|
187
|
+
function write(level, message, ...rest) {
|
|
188
|
+
let line = `${colors.gray(new Date().toISOString())} ${withLevel(level, message)}`;
|
|
189
|
+
if (instanceName) {
|
|
190
|
+
line = `${colors.bgBlue.black.bold(` ${instanceName} `)} ${line}`;
|
|
191
|
+
}
|
|
192
|
+
console[level](line, ...rest);
|
|
193
|
+
}
|
|
194
|
+
var logger = {
|
|
195
|
+
error: (message, ...rest) => write("error", message, ...rest),
|
|
196
|
+
warn: (message, ...rest) => write("warn", message, ...rest),
|
|
197
|
+
info: (message, ...rest) => write("info", message, ...rest)
|
|
198
|
+
};
|
|
199
|
+
var simple_default = logger;
|
|
200
|
+
|
|
53
201
|
// ../helpers/src/protocol/clientProperties.ts
|
|
54
202
|
var CLIENT_PROPERTY_KEYS = {
|
|
55
203
|
libVersion: "requence_lib_version",
|
|
@@ -174,6 +322,19 @@ async function reachableFetch(input, init) {
|
|
|
174
322
|
return response;
|
|
175
323
|
}
|
|
176
324
|
|
|
325
|
+
class UITooLargeError extends Error {
|
|
326
|
+
path;
|
|
327
|
+
size;
|
|
328
|
+
maxSize;
|
|
329
|
+
constructor(path, size, maxSize) {
|
|
330
|
+
super(`UI component "${path}" is ${size} bytes, which exceeds the maximum ` + `component size of ${maxSize} bytes. Bundle only the component and its ` + `own dependencies — a whole-app bundle travels with every render.`);
|
|
331
|
+
this.name = "UITooLargeError";
|
|
332
|
+
this.path = path;
|
|
333
|
+
this.size = size;
|
|
334
|
+
this.maxSize = maxSize;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
177
338
|
class UnsupportedCapabilityError extends Error {
|
|
178
339
|
capabilities;
|
|
179
340
|
constructor(unsupported) {
|
|
@@ -194,7 +355,7 @@ class PayloadTooLargeError extends Error {
|
|
|
194
355
|
}
|
|
195
356
|
}
|
|
196
357
|
|
|
197
|
-
export { obfuscate, deobfuscate, buildServiceClientProperties, CAPABILITY_HEADER_KEYS, readRequiredCapabilities, unsupportedCapabilities, buildServiceIdentityHeaders, RequenceConnectionError, RequenceUnreachableError, isUnreachableStatus, assertReachable, reachableFetch, UnsupportedCapabilityError, PayloadTooLargeError };
|
|
358
|
+
export { __toESM, __commonJS, obfuscate, deobfuscate, simple_default, buildServiceClientProperties, CAPABILITY_HEADER_KEYS, readRequiredCapabilities, unsupportedCapabilities, buildServiceIdentityHeaders, RequenceConnectionError, RequenceUnreachableError, isUnreachableStatus, assertReachable, reachableFetch, UITooLargeError, UnsupportedCapabilityError, PayloadTooLargeError };
|
|
198
359
|
|
|
199
|
-
//# debugId=
|
|
200
|
-
//# sourceMappingURL=chunk-
|
|
360
|
+
//# debugId=F03D8AC816A7239664756E2164756E21
|
|
361
|
+
//# sourceMappingURL=chunk-gpyt51xn.js.map
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../helpers/src/utils/obfuscate.ts", "../../helpers/src/protocol/clientProperties.ts", "../src/errors.ts"],
|
|
3
|
+
"sources": ["../../helpers/src/utils/obfuscate.ts", "../../logger/src/colors.ts", "../../logger/src/format.ts", "../../logger/src/simple.ts", "../../helpers/src/protocol/clientProperties.ts", "../src/errors.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"const separator = '\\u{f6ee}'\n\nconst base65Chars =\n 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$'\n\nconst base65CharToIndex: Record<string, number> = {}\nfor (let i = 0; i < base65Chars.length; i++) {\n base65CharToIndex[base65Chars[i]] = i\n}\n\nexport function obfuscate(...parts: Array<string>) {\n const str = parts.join(separator)\n let binaryString = ''\n\n for (let i = 0; i < str.length; i++) {\n const charCode = str.charCodeAt(i)\n binaryString += charCode.toString(2).padStart(16, '0')\n }\n\n let encodedString = ''\n const chunkSize = 6\n let paddingCount = 0\n\n for (let i = 0; i < binaryString.length; i += chunkSize) {\n let chunk = binaryString.substring(i, i + chunkSize)\n\n if (chunk.length < chunkSize) {\n paddingCount = chunkSize - chunk.length\n chunk = chunk.padEnd(chunkSize, '0')\n }\n\n const decimalValue = parseInt(chunk, 2)\n encodedString += base65Chars[decimalValue]\n }\n\n return encodedString + paddingCount.toString()\n}\n\nexport function randomString(length: number) {\n const randomValues = new Uint8Array(length)\n crypto.getRandomValues(randomValues)\n\n let randomString = ''\n for (let i = 0; i < length; i++) {\n randomString += base65Chars[randomValues[i] % base65Chars.length]\n }\n\n return randomString\n}\n\nexport function deobfuscate(str: string) {\n const paddingCount = parseInt(str.slice(-1), 10)\n str = str.slice(0, -1)\n\n let binaryString = ''\n\n for (let i = 0; i < str.length; i++) {\n const decimalValue = base65CharToIndex[str[i]]\n const binaryChunk = decimalValue.toString(2).padStart(6, '0')\n binaryString += binaryChunk\n }\n\n if (paddingCount > 0) {\n binaryString = binaryString.slice(0, -paddingCount)\n }\n\n let decodedString = ''\n const charSize = 16\n\n for (let i = 0; i < binaryString.length; i += charSize) {\n const byte = binaryString.substring(i, i + charSize)\n if (byte.length === 16) {\n const charCode = parseInt(byte, 2)\n decodedString += String.fromCharCode(charCode)\n }\n }\n\n return decodedString.split(separator)\n}\n",
|
|
6
|
+
"/**\n * The ANSI styling this package needs, without a dependency.\n *\n * It exists so `./simple` can be imported by a package that ships to npm\n * (`@requence/service`) without dragging a styling library into the published\n * bundle. The emitted escape sequences are byte-identical to the ones chalk\n * produced for every style used here — `colors.spec.ts` is the proof.\n */\n\nconst STYLES = {\n bold: [1, 22],\n black: [30, 39],\n red: [31, 39],\n green: [32, 39],\n yellow: [33, 39],\n blue: [34, 39],\n magenta: [35, 39],\n cyan: [36, 39],\n white: [37, 39],\n gray: [90, 39],\n bgBlack: [40, 49],\n bgRed: [41, 49],\n bgGreen: [42, 49],\n bgYellow: [43, 49],\n bgBlue: [44, 49],\n bgMagenta: [45, 49],\n bgCyan: [46, 49],\n bgWhite: [47, 49],\n} as const satisfies Record<string, readonly [number, number]>\n\ntype StyleName = keyof typeof STYLES\n\ntype Code = readonly [open: string, close: string]\n\nconst forceColor = process.env.FORCE_COLOR\n\n/**\n * Resolved once at load, as a styling library would: the stream a process\n * writes to does not change under it.\n */\nexport const colorEnabled =\n !process.env.NO_COLOR &&\n process.env.TERM !== 'dumb' &&\n (forceColor === undefined\n ? Boolean(process.stdout?.isTTY)\n : forceColor !== '0' && forceColor !== 'false')\n\n/**\n * 24-bit colour is the only thing here that is not universally understood, so\n * it is the only thing gated: everything else is a basic SGR code every\n * terminal has had for decades.\n */\nconst trueColor =\n forceColor === '3' || /truecolor|24bit/i.test(process.env.COLORTERM ?? '')\n\nexport interface Styler {\n (...args: unknown[]): string\n readonly bold: Styler\n readonly black: Styler\n readonly red: Styler\n readonly green: Styler\n readonly yellow: Styler\n readonly blue: Styler\n readonly magenta: Styler\n readonly cyan: Styler\n readonly white: Styler\n readonly gray: Styler\n readonly bgBlack: Styler\n readonly bgRed: Styler\n readonly bgGreen: Styler\n readonly bgYellow: Styler\n readonly bgBlue: Styler\n readonly bgMagenta: Styler\n readonly bgCyan: Styler\n readonly bgWhite: Styler\n /**\n * A 24-bit colour, with the basic colour to fall back on where 24-bit is not\n * available. The fallback is named rather than computed: the palettes here\n * are short and hand-picked, and an approximation nobody chose is worse than\n * the one the author would have chosen.\n */\n hex(value: string, fallback: StyleName): Styler\n}\n\nfunction sequence(code: number) {\n return `\\u001b[${code}m`\n}\n\nfunction createStyler(codes: readonly Code[]): Styler {\n const styler = (...args: unknown[]) => {\n let text = args.map(String).join(' ')\n\n if (!colorEnabled || codes.length === 0) {\n return text\n }\n\n let open = ''\n let close = ''\n for (const [start, end] of codes) {\n open += start\n // A nested style closed this one early. Re-open it right after that\n // close — leaving the close in place, so the nested style still ends\n // where it meant to — and `yellow('a', red('b'), 'c')` is yellow again\n // for the 'c'.\n text = text.includes(end) ? text.replaceAll(end, end + start) : text\n close = end + close\n }\n\n return `${open}${text}${close}`\n }\n\n for (const name of Object.keys(STYLES) as StyleName[]) {\n Object.defineProperty(styler, name, {\n get: () => {\n const [open, close] = STYLES[name]\n return createStyler([...codes, [sequence(open), sequence(close)]])\n },\n })\n }\n\n Object.defineProperty(styler, 'hex', {\n value: (value: string, fallback: StyleName) => {\n if (!trueColor) {\n const [open, close] = STYLES[fallback]\n return createStyler([...codes, [sequence(open), sequence(close)]])\n }\n\n const hex = value.replace('#', '')\n const int = Number.parseInt(hex, 16)\n const r = (int >> 16) & 0xff\n const g = (int >> 8) & 0xff\n const b = int & 0xff\n\n return createStyler([\n ...codes,\n [`\\u001b[38;2;${r};${g};${b}m`, sequence(39)],\n ])\n },\n })\n\n return styler as Styler\n}\n\nexport const colors = createStyler([])\n",
|
|
7
|
+
"import { colors } from './colors.ts'\n\nexport const instanceName = process.env.INSTANCE_NAME || null\n\nexport function withLevel(level: string, message: unknown) {\n let prefix = `??${level}??`\n let highlight = (str: string) => str\n\n switch (level) {\n case 'info': {\n prefix = colors.bgGreen.black.bold(' INFO ')\n highlight = (str) => colors.green.bold(str)\n break\n }\n case 'warn': {\n prefix = colors.bgYellow.black.bold(' WARN ')\n highlight = (str) => colors.yellow.bold(str)\n break\n }\n case 'error': {\n prefix = colors.bgRed.black.bold(' ERROR ')\n highlight = (str) => colors.red.bold(str)\n break\n }\n case 'debug': {\n prefix = colors.bgMagenta.black.bold(' DEBUG ')\n highlight = (str) => colors.magenta.bold(str)\n break\n }\n }\n\n return `${prefix} ${String(message).replace(/\\|(.*?)\\|/g, (_, str) => highlight(str))}`\n}\n",
|
|
8
|
+
"import { colors } from './colors.ts'\nimport { instanceName, withLevel } from './format.ts'\n\ntype Severity = 'error' | 'warn' | 'info'\n\nfunction write(level: Severity, message: unknown, ...rest: unknown[]) {\n let line = `${colors.gray(new Date().toISOString())} ${withLevel(level, message)}`\n\n if (instanceName) {\n line = `${colors.bgBlue.black.bold(` ${instanceName} `)} ${line}`\n }\n\n // Resolved per call, never captured at module load: a severity is also the\n // name of its console method, and a host (or a test) that swaps one expects\n // to intercept what this writes.\n console[level](line, ...rest)\n}\n\n/**\n * A dependency-free logger with the same look as the default export: the same\n * severity prefixes, the same `|highlight|` syntax. Written for packages that\n * ship to npm, where pulling winston into the published bundle costs more than\n * the structured transports are worth.\n *\n * Unlike the default export it has **no levels**. Every call prints, and\n * `LOG_LEVEL` is not read at all. These lines go out from a CLI and from an\n * SDK running inside someone else's process: a line they can lose to an\n * environment variable they did not set for us is worse than no line, because\n * it reads as nothing having happened.\n *\n * That is also why there is no `debug`. A debug level is a promise that\n * something will filter it out later, and nothing here does.\n */\nconst logger = {\n error: (message: unknown, ...rest: unknown[]) =>\n write('error', message, ...rest),\n warn: (message: unknown, ...rest: unknown[]) =>\n write('warn', message, ...rest),\n info: (message: unknown, ...rest: unknown[]) =>\n write('info', message, ...rest),\n}\n\n/**\n * Re-exported here rather than from a second entry point: a consumer that\n * reaches for this logger to avoid the dependency needs the styling for the\n * same reason, and one import path is one thing to keep off the published\n * dependency list.\n */\nexport { type Styler, colorEnabled, colors } from './colors.ts'\n\nexport default logger\n",
|
|
6
9
|
"// AMQP connection client properties: how a service SDK announces itself to the\n// broker at connect time, and how `ui/backend` reads that announcement back.\n//\n// `connection.start-ok` carries a free-form `client-properties` table that the\n// broker keeps for the life of the connection. That makes it the one metadata\n// channel a wire-format change cannot break: it is present BEFORE the first\n// dispatch, it is broker state rather than an event (a `ui/backend` restart just\n// re-reads it), and old SDKs already send amqplib/pika defaults — so a MISSING\n// `requence_*` field means \"too old\", with nothing to negotiate.\n//\n// Read side: `GET /api/connections?columns=name,user,client_properties`. The\n// join key already exists: a service connects with its own AMQP username, and\n// that username IS `service-<internalName>`, so `user` → service and\n// `requence_service_version` → version.\n//\n// THIS FILE IS THE CONTRACT. The Python SDK mirrors it in\n// `python/requence/client_properties.py`, and `python/tests/\n// test_client_properties.py` asserts the key set against this source. Changing\n// a key is a wire change: both SDKs ship it together.\n\n// The pinned, namespaced property set. Flat strings on purpose: a nested field\n// table inside a broker event header is more decode surface than it is worth.\nexport const CLIENT_PROPERTY_KEYS = {\n libVersion: 'requence_lib_version',\n serviceVersion: 'requence_service_version',\n capabilities: 'requence_capabilities',\n // Author-chosen debugging label. Optional, non-unique, unvalidated —\n // NOTHING may key off or branch on it.\n name: 'requence_name',\n // Marks a developer's local dev-overlay connection. Only the marker travels,\n // never the devKey (which is an access-token value).\n devOverlay: 'requence_dev_overlay',\n} as const\n\n// The name is author-controlled text and the whole client-properties table\n// travels inside the connection frame, so an unbounded string is a\n// connection-level failure. Capped in both SDKs and truncated AGAIN on read.\nexport const MAX_SERVICE_NAME_LENGTH = 64\nexport const MAX_CAPABILITIES_LENGTH = 512\n\n// Wire capabilities. `wire:json1` is the JSON body encoding every shipped SDK\n// speaks and is the floor that never goes away; each additional codec joins this\n// list as its own token, and the fleet fold below is what tells a dispatch whether\n// it may use it.\nexport const WIRE_JSON_1 = 'wire:json1'\n// The portable msgpack subset in `@requence/helpers/msgpack` (`packWire`). Claimed\n// only by an SDK that can DECODE it on the dispatch wire — a service echoes the\n// codec it was addressed in, so claiming this is a promise about reading, and the\n// reply direction needs no capability of its own.\n//\n// Both SDKs claim it now; the Python counterpart is `requence/wire_codec.py`. The\n// two lists are still per-language on purpose — the fleet fold intersects across\n// replicas, so a language that lags simply pulls its fleets back to JSON instead\n// of needing a special case.\nexport const WIRE_MSGPACK_1 = 'wire:msgpack1'\nexport const SERVICE_WIRE_CAPABILITIES: ReadonlyArray<string> = [\n WIRE_JSON_1,\n WIRE_MSGPACK_1,\n]\n\n// oxlint-disable-next-line no-control-regex\nconst CONTROL_CHARACTERS = /[\\u0000-\\u001f\\u007f]/g\n\nfunction sanitizeLabel(value: string, maxLength: number) {\n return value.replace(CONTROL_CHARACTERS, '').trim().slice(0, maxLength)\n}\n\nexport interface BuildServiceClientPropertiesParams {\n /** `<language>@<version>`, e.g. `javascript@1.2.3`. */\n libVersion: string\n /** The service version this connection consumes, e.g. `v1`. */\n serviceVersion: string\n /** Optional author-chosen debugging label. */\n name?: string\n devOverlay?: boolean\n capabilities?: ReadonlyArray<string>\n}\n\n// Builds the properties a service SDK passes to its AMQP client on connect (and\n// therefore on every reconnect, since both clients reuse their connection\n// parameters). Absent optional fields are OMITTED, never sent blank.\nexport function buildServiceClientProperties({\n libVersion,\n serviceVersion,\n name,\n devOverlay = false,\n capabilities = SERVICE_WIRE_CAPABILITIES,\n}: BuildServiceClientPropertiesParams): Record<string, string> {\n const properties: Record<string, string> = {\n [CLIENT_PROPERTY_KEYS.libVersion]: libVersion,\n [CLIENT_PROPERTY_KEYS.serviceVersion]: serviceVersion,\n [CLIENT_PROPERTY_KEYS.capabilities]: capabilities\n .join(',')\n .slice(0, MAX_CAPABILITIES_LENGTH),\n }\n\n const label = name ? sanitizeLabel(name, MAX_SERVICE_NAME_LENGTH) : ''\n if (label) {\n properties[CLIENT_PROPERTY_KEYS.name] = label\n }\n\n if (devOverlay) {\n properties[CLIENT_PROPERTY_KEYS.devOverlay] = '1'\n }\n\n return properties\n}\n\n// One attached replica, as read back off its connection. Every field is\n// nullable: an SDK too old to announce yields a blank replica, which is exactly\n// the signal the fold below needs.\nexport interface ServiceReplica {\n libVersion: string | null\n serviceVersion: string | null\n capabilities: string[]\n name: string | null\n devOverlay: boolean\n}\n\nconst BLANK_REPLICA: ServiceReplica = {\n libVersion: null,\n serviceVersion: null,\n capabilities: [],\n name: null,\n devOverlay: false,\n}\n\nfunction readString(\n table: Record<string, unknown>,\n key: string,\n): string | null {\n const value = table[key]\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\n// Parses a broker `client_properties` table. Never throws and never trusts the\n// writer: the name and the capability string are re-capped here, so a\n// misbehaving (or malicious) SDK cannot push an unbounded string into the UI.\nexport function readServiceClientProperties(raw: unknown): ServiceReplica {\n if (typeof raw !== 'object' || raw === null) {\n return { ...BLANK_REPLICA }\n }\n\n const table = raw as Record<string, unknown>\n const rawName = readString(table, CLIENT_PROPERTY_KEYS.name)\n const name = rawName ? sanitizeLabel(rawName, MAX_SERVICE_NAME_LENGTH) : null\n\n return {\n libVersion: readString(table, CLIENT_PROPERTY_KEYS.libVersion),\n serviceVersion: readString(table, CLIENT_PROPERTY_KEYS.serviceVersion),\n capabilities: parseCapabilityList(\n readString(table, CLIENT_PROPERTY_KEYS.capabilities),\n ),\n name: name || null,\n devOverlay: readString(table, CLIENT_PROPERTY_KEYS.devOverlay) === '1',\n }\n}\n\n// Caps the whole string BEFORE splitting, so an unbounded writer cannot push an\n// unbounded list through. A token cut in half by that cap simply matches no\n// known capability, which can only ever UNDER-claim — the safe direction.\nfunction parseCapabilityList(raw: string | null): string[] {\n if (!raw) {\n return []\n }\n return [\n ...new Set(\n raw\n .slice(0, MAX_CAPABILITIES_LENGTH)\n .split(',')\n .map((token) => token.trim())\n .filter(Boolean),\n ),\n ]\n}\n\n// The fleet verdict. `libVersion`/`capabilities` are the MINIMUM across every\n// attached replica, because replicas are mixed during a rolling deploy and a\n// verdict that over-claims breaks the one box that cannot honour it.\nexport interface FleetVerdict {\n libVersion: string | null\n capabilities: string[]\n // True when we hold a row for every replica attached to the queue. False\n // means either nothing is attached, or some attached replica is unaccounted\n // for — in both cases the verdict above is deliberately empty.\n complete: boolean\n}\n\n// Leading `major.minor.patch` of the version half of `<language>@<version>`.\n// Null for anything else (`python@dev`, a garbage string), which sorts LOWEST:\n// an unknown build must never win a minimum fold.\nfunction parseLibVersion(libVersion: string): [number, number, number] | null {\n const match = /@(\\d+)\\.(\\d+)\\.(\\d+)/.exec(libVersion)\n if (!match) {\n return null\n }\n return [Number(match[1]), Number(match[2]), Number(match[3])]\n}\n\n// Both SDKs are released from this monorepo at the same version, so comparing\n// only the numeric half is meaningful even across languages — the question is\n// \"what is the oldest build attached\", not \"which language\".\nfunction isLower(candidate: string, current: string) {\n const parsedCandidate = parseLibVersion(candidate)\n const parsedCurrent = parseLibVersion(current)\n\n if (!parsedCandidate) {\n // Two unknowns: pick deterministically so the verdict never flaps.\n return !parsedCurrent ? candidate < current : true\n }\n if (!parsedCurrent) {\n return false\n }\n\n for (let index = 0; index < 3; index++) {\n if (parsedCandidate[index] !== parsedCurrent[index]) {\n return parsedCandidate[index] < parsedCurrent[index]\n }\n }\n return false\n}\n\n// Folds the announced replicas of ONE service version into a single verdict.\n// `attachedCount` is the queue's consumer count (from the queue count monitor):\n// it is what makes a MISSING announcement visible. Fewer rows than consumers\n// means some attached box never announced itself, and the honest answer is\n// \"unknown\" — a wrong fold is worse than no data.\nexport function foldServiceFleet(\n replicas: ReadonlyArray<ServiceReplica>,\n attachedCount: number,\n): FleetVerdict {\n const complete = replicas.length > 0 && replicas.length >= attachedCount\n if (!complete) {\n return { libVersion: null, capabilities: [], complete }\n }\n\n let libVersion: string | null = null\n for (const replica of replicas) {\n if (!replica.libVersion) {\n // One replica too old to announce ⇒ nothing can be claimed for the fleet.\n return { libVersion: null, capabilities: [], complete }\n }\n if (libVersion === null || isLower(replica.libVersion, libVersion)) {\n libVersion = replica.libVersion\n }\n }\n\n const intersection = new Set(replicas[0].capabilities)\n for (const replica of replicas.slice(1)) {\n const declared = new Set(replica.capabilities)\n for (const capability of intersection) {\n if (!declared.has(capability)) {\n intersection.delete(capability)\n }\n }\n }\n\n return {\n libVersion,\n capabilities: [...intersection].sort(),\n complete,\n }\n}\n\n// THE ONE PLACE A CODEC IS CHOSEN, for the whole system.\n//\n// `ui/backend` calls this at task creation and bakes the answer into the compiled\n// tree, so the operator never decides and holds no presence state: it encodes what\n// the tree tells it to, and every receiver reads the content type off the message.\n//\n// Two ways to answer `json`, and both are the safe direction:\n// - the verdict is incomplete (nothing attached, or an attached replica never\n// announced itself) — an unknown fleet is never given a new codec;\n// - the intersection lacks the token — at least one attached replica cannot\n// decode msgpack, and `foldServiceFleet` has already reduced the fleet to what\n// its weakest member can do.\n//\n// The cost of this being frozen at creation is that a long-running continuous task\n// keeps its codec until it is recreated, so the migration completes only as tasks\n// turn over. That is slow, not unsafe.\nexport function wireCodecForFleet(verdict: FleetVerdict): 'json' | 'msgpack' {\n return verdict.complete && verdict.capabilities.includes(WIRE_MSGPACK_1)\n ? 'msgpack'\n : 'json'\n}\n\n// ─── The dispatch direction: what a message REQUIRES of its consumer ────────\n//\n// The fleet fold above answers \"may this dispatch use a capability?\" BEFORE the\n// message is built. `requires` answers the mirror question at the other end:\n// \"did the replica that actually picked this up have it?\" — because a fold is a\n// verdict about the fleet as it was, and a rollback, a scale-up or a wrong\n// verdict can put an incapable box in front of the message anyway.\n//\n// IT IS A HEADER, NEVER A BODY FIELD. The body is precisely the thing an\n// incapable consumer may be unable to decode, so a requirement stated inside it\n// is unreadable exactly when it matters — the same reasoning that put the codec\n// in `content-type`.\n//\n// An SDK too old to know the header ignores it, as it always has: that is a\n// bare unknown header, not an error. So this is insurance for the NEXT\n// capability, not a fix for a rollback under the current one.\nexport const CAPABILITY_HEADER_KEYS = {\n // dispatch → service: the tokens this message needs. Absent when it needs\n // nothing beyond the floor every SDK speaks.\n requires: 'requires',\n // service → operator: this replica cannot satisfy `requires`. Sent by\n // REPUBLISHING to `<exchange>-retry`, never by nacking — a nack cannot modify\n // the message, these are classic queues (so there is no `x-delivery-count`\n // either), and with no counter anywhere the message would requeue forever.\n mismatch: 'capabilityMismatch',\n // operator → service, on a reroute: how many reroutes are left. Named for\n // reroutes and NOT for retries on purpose — it is its own allowance, never the\n // node's `retry` budget, which the template author set aside for real errors.\n // The operator owns the count; see `MAX_CAPABILITY_REROUTES` in its error\n // consumer.\n reroutes: 'capabilityReroutes',\n} as const\n\n// What a dispatch under `codec` needs of the replica that picks it up. `json` is\n// the floor no SDK has ever lacked, so it requires nothing and the header is\n// omitted entirely.\n//\n// For `wire:msgpack1` this buys nothing on its own — `content-type` already\n// carries that requirement and any SDK new enough to read `requires` has the\n// codec. It is stated anyway so the mechanism has exactly one producer, ready\n// for the first capability that is NOT self-describing.\nexport function requiredCapabilitiesForCodec(\n codec: 'json' | 'msgpack',\n): string[] {\n return codec === 'msgpack' ? [WIRE_MSGPACK_1] : []\n}\n\n// The header table for a dispatch, or `{}` when there is nothing to require —\n// spread into the publish, so an empty requirement sends no header at all\n// rather than a blank one.\nexport function buildRequiresHeader(\n capabilities: ReadonlyArray<string>,\n): Record<string, string> {\n if (capabilities.length === 0) {\n return {}\n }\n return {\n [CAPABILITY_HEADER_KEYS.requires]: capabilities\n .join(',')\n .slice(0, MAX_CAPABILITIES_LENGTH),\n }\n}\n\n// Reads `requires` off a message's header table. Never throws and never trusts\n// the writer, exactly like the client-properties read side: an absent, blank or\n// non-string header means \"requires nothing\", which is how every message\n// published before this existed reads.\nexport function readRequiredCapabilities(headers: unknown): string[] {\n if (typeof headers !== 'object' || headers === null) {\n return []\n }\n return parseCapabilityList(\n readString(\n headers as Record<string, unknown>,\n CAPABILITY_HEADER_KEYS.requires,\n ),\n )\n}\n\n// The tokens this consumer cannot satisfy. Anything unrecognised counts as\n// unsupported — an unknown token is a capability from a newer operator, and\n// guessing it is harmless would defeat the whole point of declaring it.\nexport function unsupportedCapabilities(\n required: ReadonlyArray<string>,\n supported: ReadonlyArray<string> = SERVICE_WIRE_CAPABILITIES,\n): string[] {\n const known = new Set(supported)\n return required.filter((token) => !known.has(token))\n}\n\n// The one diagnostic string, so both SDKs report a mismatch identically and the\n// operator can surface it verbatim. Named and deliberate — the point of the\n// whole mechanism is that the failure is this message and never whatever\n// incidental exception the undecodable payload would have produced.\nexport function unsupportedCapabilityMessage(\n unsupported: ReadonlyArray<string>,\n): string {\n return `unsupported wire capability: ${unsupported.join(', ')}`\n}\n\n// ─── The second announcement: per-run identity on the begin message ─────────\n//\n// The same three values as above, sent again — deliberately, because they answer\n// a DIFFERENT question at a different time:\n//\n// - Client properties (above) say \"can this fleet decode msgpack?\". Live broker\n// state, folded to the MINIMUM across replicas, and the only answer available\n// BEFORE dispatch — which is where it must be, since a codec break fails\n// inside the service's own decoder and no reply could warn us.\n// - These headers say \"which build ran THIS node?\". The service stamps them on\n// its `begin` publish, the operator freezes them into the `NODE_START` update,\n// and that row is written once and never re-derived. Exact, not folded: at\n// dispatch time nobody knows which replica will pick the message up, but once\n// it has, only that replica can say who it is.\n//\n// The two can never disagree — both come from the same constants in the same\n// process, through this one file — and neither is ever read to answer the\n// other's question.\n//\n// Sent on `begin` ONLY, not on every reply: one stamp per node execution.\n\n// Bare, un-namespaced names, matching the message headers that already ride\n// alongside them (`taskId`, `index`, `connectionId`, `version`, `epoch`).\n// `libVersion` keeps its historical name — it is the same value it always was.\nexport const IDENTITY_HEADER_KEYS = {\n libVersion: 'libVersion',\n name: 'serviceName',\n capabilities: 'capabilities',\n} as const\n\n// What ran one node execution. `libVersion` is DISPLAY-ONLY: it may read\n// `javascript@legacy` (see below), so nothing may parse or branch on it — the\n// pre-dispatch capability answer is the fleet fold, never this.\nexport interface ServiceIdentity {\n libVersion: string | null\n name: string | null\n capabilities: string[]\n}\n\nexport interface BuildServiceIdentityHeadersParams {\n libVersion: string\n name?: string\n capabilities?: ReadonlyArray<string>\n}\n\n// Built by the service, from its own build-time constants. Nothing is looked up,\n// so nothing can be stale or race a lookup.\nexport function buildServiceIdentityHeaders({\n libVersion,\n name,\n capabilities = SERVICE_WIRE_CAPABILITIES,\n}: BuildServiceIdentityHeadersParams): Record<string, string> {\n const headers: Record<string, string> = {\n [IDENTITY_HEADER_KEYS.libVersion]: libVersion,\n [IDENTITY_HEADER_KEYS.capabilities]: capabilities\n .join(',')\n .slice(0, MAX_CAPABILITIES_LENGTH),\n }\n\n const label = name ? sanitizeLabel(name, MAX_SERVICE_NAME_LENGTH) : ''\n if (label) {\n headers[IDENTITY_HEADER_KEYS.name] = label\n }\n\n return headers\n}\n\n// `<language>` half of `<language>@<version>`, for the legacy label below. The\n// `@` is required: without it the string makes no language claim at all, and\n// inventing one from a bare token would put a made-up word in the UI.\nfunction libVersionLanguage(libVersion: string): string | null {\n const match = /^([a-z]+)@/i.exec(libVersion.trim())\n return match ? match[1].toLowerCase() : null\n}\n\n// Parses the identity a service stamped on its begin message. Returns null when\n// the message carried none at all — a reference/or node's NODE_START, or the\n// operator's own loopback.\n//\n// THE LEGACY CASE: an SDK from before this existed still sends `libVersion`, but\n// never sent a name or capabilities. Since every current build declares at least\n// `wire:json1`, a MISSING capabilities header is a reliable \"predates this\" —\n// and the version header still names the language, so it reports as\n// `javascript@legacy` / `python@legacy`. That says \"reported before we asked for\n// more\", which is a different thing from \"unknown\".\nexport function readServiceIdentity(raw: unknown): ServiceIdentity | null {\n if (typeof raw !== 'object' || raw === null) {\n return null\n }\n\n const table = raw as Record<string, unknown>\n const libVersion = readString(table, IDENTITY_HEADER_KEYS.libVersion)\n const rawCapabilities = readString(table, IDENTITY_HEADER_KEYS.capabilities)\n\n if (!rawCapabilities) {\n if (!libVersion) {\n return null\n }\n const language = libVersionLanguage(libVersion)\n return {\n libVersion: language ? `${language}@legacy` : 'legacy',\n name: null,\n capabilities: [],\n }\n }\n\n // Never trust the writer, exactly as on the client-properties read side.\n const rawName = readString(table, IDENTITY_HEADER_KEYS.name)\n const name = rawName ? sanitizeLabel(rawName, MAX_SERVICE_NAME_LENGTH) : ''\n\n return {\n libVersion,\n name: name || null,\n capabilities: parseCapabilityList(rawCapabilities),\n }\n}\n\n/**\n * WHY a service node got the codec it got.\n *\n * `wireCodecForFleet` answers `json` for several distinct situations that matter\n * very differently in an incident: a service that is simply offline looks nothing\n * like a management API that is down, which in turn looks nothing like a genuine\n * mixed fleet. Recording only the codec makes \"why is this json?\" unanswerable\n * without shell access to the box, so the reason travels beside it.\n *\n * A compact token rather than prose: it is baked into every service node of every\n * task, and the wording belongs to the UI, which can then change without touching\n * the tree or the operator.\n */\n// A literal tuple, so the protocol schemas can build a `z.enum` straight from it\n// and the vocabulary exists in exactly one place.\nexport const WIRE_CODEC_REASON_VALUES = [\n /** msgpack: every attached replica claimed the capability. */\n 'fleet-capable',\n /** The broker's management API is unreachable, so no fleet can be judged. */\n 'presence-unavailable',\n /** Nothing is attached to this service version's queue at all. */\n 'no-replicas',\n /**\n * Something IS consuming the queue that never announced itself. An SDK too old\n * to announce is also too old to decode, so this folds to json — and it is the\n * one reason that looks like a bug but is the design working.\n */\n 'replicas-unaccounted',\n /** A complete fleet, but at least one replica cannot decode msgpack. */\n 'missing-capability',\n] as const\n\nexport type WireCodecReason = (typeof WIRE_CODEC_REASON_VALUES)[number]\n\n/** Named accessors, so call sites read as intent rather than as string literals. */\nexport const WIRE_CODEC_REASONS = {\n fleetCapable: 'fleet-capable',\n presenceUnavailable: 'presence-unavailable',\n noReplicas: 'no-replicas',\n replicasUnaccounted: 'replicas-unaccounted',\n missingCapability: 'missing-capability',\n} as const satisfies Record<string, WireCodecReason>\n\n/**\n * Explains a `json` verdict from the facts the fleet monitor holds.\n *\n * Split from `wireCodecForFleet` rather than folded into it because the codec\n * decision needs only the verdict, while the explanation needs the counts behind\n * it — and the codec must stay trivially checkable.\n */\nexport function wireCodecReason({\n codec,\n presenceAvailable,\n replicaCount,\n consumerCount,\n}: {\n codec: 'json' | 'msgpack'\n presenceAvailable: boolean\n replicaCount: number\n consumerCount: number\n}): WireCodecReason {\n if (codec === 'msgpack') {\n return WIRE_CODEC_REASONS.fleetCapable\n }\n if (!presenceAvailable) {\n return WIRE_CODEC_REASONS.presenceUnavailable\n }\n // Checked before `no-replicas`, so \"nothing announced but something IS\n // consuming\" reports the consumer rather than claiming the service is offline.\n if (replicaCount < consumerCount) {\n return WIRE_CODEC_REASONS.replicasUnaccounted\n }\n if (replicaCount === 0) {\n return WIRE_CODEC_REASONS.noReplicas\n }\n return WIRE_CODEC_REASONS.missingCapability\n}\n",
|
|
7
|
-
"import { unsupportedCapabilityMessage } from '@requence/helpers/protocol'\n\nexport class RequenceConnectionError extends Error {}\n\n// Gateway statuses an ingress/load balancer emits when no healthy backend is\n// available (maintenance window, deploy, cutover). The backend endpoints never\n// return these themselves, so treating them as \"unreachable\" is unambiguous.\nconst UNREACHABLE_STATUS = new Set([502, 503, 504])\n\n/**\n * Signals that the requence system could not be reached — e.g. during a\n * maintenance window or deploy, when the backend is unreachable (connection\n * refused / timeout / DNS failure) or an ingress returns a gateway error\n * (502/503/504). Callers should treat this as transient and retry later.\n */\nexport class RequenceUnreachableError extends Error {\n constructor(\n public readonly status?: number,\n options?: { cause?: unknown },\n ) {\n super(\n 'The requence system is currently not reachable. Please retry later.',\n options,\n )\n this.name = 'RequenceUnreachableError'\n }\n}\n\nexport function isUnreachableStatus(status: number): boolean {\n return UNREACHABLE_STATUS.has(status)\n}\n\n/**\n * Throws a {@link RequenceUnreachableError} when the response carries a gateway\n * status indicating the system is unreachable. Call right after a fetch, before\n * inspecting `response.ok`.\n */\nexport function assertReachable(response: Response): void {\n if (isUnreachableStatus(response.status)) {\n throw new RequenceUnreachableError(response.status)\n }\n}\n\n/**\n * Wraps `fetch` so a transport-level failure (connection refused, DNS failure,\n * timeout) surfaces as a {@link RequenceUnreachableError} instead of a raw\n * TypeError, and gateway statuses (502/503/504) do the same. The returned\n * response is guaranteed not to be a gateway-error response.\n */\nexport async function reachableFetch(\n input: Parameters<typeof fetch>[0],\n init?: Parameters<typeof fetch>[1],\n): Promise<Response> {\n let response: Response\n try {\n response = await fetch(input, init)\n } catch (error) {\n throw new RequenceUnreachableError(undefined, { cause: error })\n }\n assertReachable(response)\n return response\n}\n\n/**\n * The dispatch declared a capability (`requires` header) this build cannot\n * satisfy, so it was refused before its body was ever decoded.\n *\n * A NAMED error on purpose: the point of the `requires` header is that a\n * mismatch reports the missing capability, instead of whatever incidental\n * exception the undecodable payload would otherwise have produced — a bare\n * `SyntaxError` from `JSON.parse` says nothing about why this box was the wrong\n * one. The message travels to the operator, which surfaces it on the node.\n */\nexport class UnsupportedCapabilityError extends Error {\n readonly capabilities: string[]\n\n constructor(unsupported: ReadonlyArray<string>) {\n super(unsupportedCapabilityMessage(unsupported))\n this.name = 'UnsupportedCapabilityError'\n this.capabilities = [...unsupported]\n }\n}\n\nexport class PayloadTooLargeError extends Error {\n readonly size: number\n readonly maxSize: number\n\n constructor(size: number, maxSize: number) {\n super(\n `Message payload is ${size} bytes, which exceeds the maximum message ` +\n `size of ${maxSize} bytes. Reduce the returned value, or offload large ` +\n `data (files/streams) so it travels by reference instead of inline.`,\n )\n this.name = 'PayloadTooLargeError'\n this.size = size\n this.maxSize = maxSize\n }\n}\n"
|
|
10
|
+
"import { unsupportedCapabilityMessage } from '@requence/helpers/protocol'\n\nexport class RequenceConnectionError extends Error {}\n\n// Gateway statuses an ingress/load balancer emits when no healthy backend is\n// available (maintenance window, deploy, cutover). The backend endpoints never\n// return these themselves, so treating them as \"unreachable\" is unambiguous.\nconst UNREACHABLE_STATUS = new Set([502, 503, 504])\n\n/**\n * Signals that the requence system could not be reached — e.g. during a\n * maintenance window or deploy, when the backend is unreachable (connection\n * refused / timeout / DNS failure) or an ingress returns a gateway error\n * (502/503/504). Callers should treat this as transient and retry later.\n */\nexport class RequenceUnreachableError extends Error {\n constructor(\n public readonly status?: number,\n options?: { cause?: unknown },\n ) {\n super(\n 'The requence system is currently not reachable. Please retry later.',\n options,\n )\n this.name = 'RequenceUnreachableError'\n }\n}\n\nexport function isUnreachableStatus(status: number): boolean {\n return UNREACHABLE_STATUS.has(status)\n}\n\n/**\n * Throws a {@link RequenceUnreachableError} when the response carries a gateway\n * status indicating the system is unreachable. Call right after a fetch, before\n * inspecting `response.ok`.\n */\nexport function assertReachable(response: Response): void {\n if (isUnreachableStatus(response.status)) {\n throw new RequenceUnreachableError(response.status)\n }\n}\n\n/**\n * Wraps `fetch` so a transport-level failure (connection refused, DNS failure,\n * timeout) surfaces as a {@link RequenceUnreachableError} instead of a raw\n * TypeError, and gateway statuses (502/503/504) do the same. The returned\n * response is guaranteed not to be a gateway-error response.\n */\nexport async function reachableFetch(\n input: Parameters<typeof fetch>[0],\n init?: Parameters<typeof fetch>[1],\n): Promise<Response> {\n let response: Response\n try {\n response = await fetch(input, init)\n } catch (error) {\n throw new RequenceUnreachableError(undefined, { cause: error })\n }\n assertReachable(response)\n return response\n}\n\n/**\n * A UI component bundle exceeded the hard cap. Thrown by `createUI` at\n * registration: the source travels inline in every first render of its hash, so\n * an oversized bundle is a deploy-time mistake, not a runtime condition.\n */\nexport class UITooLargeError extends Error {\n readonly path: string\n readonly size: number\n readonly maxSize: number\n\n constructor(path: string, size: number, maxSize: number) {\n super(\n `UI component \"${path}\" is ${size} bytes, which exceeds the maximum ` +\n `component size of ${maxSize} bytes. Bundle only the component and its ` +\n `own dependencies — a whole-app bundle travels with every render.`,\n )\n this.name = 'UITooLargeError'\n this.path = path\n this.size = size\n this.maxSize = maxSize\n }\n}\n\n/**\n * The dispatch declared a capability (`requires` header) this build cannot\n * satisfy, so it was refused before its body was ever decoded.\n *\n * A NAMED error on purpose: the point of the `requires` header is that a\n * mismatch reports the missing capability, instead of whatever incidental\n * exception the undecodable payload would otherwise have produced — a bare\n * `SyntaxError` from `JSON.parse` says nothing about why this box was the wrong\n * one. The message travels to the operator, which surfaces it on the node.\n */\nexport class UnsupportedCapabilityError extends Error {\n readonly capabilities: string[]\n\n constructor(unsupported: ReadonlyArray<string>) {\n super(unsupportedCapabilityMessage(unsupported))\n this.name = 'UnsupportedCapabilityError'\n this.capabilities = [...unsupported]\n }\n}\n\nexport class PayloadTooLargeError extends Error {\n readonly size: number\n readonly maxSize: number\n\n constructor(size: number, maxSize: number) {\n super(\n `Message payload is ${size} bytes, which exceeds the maximum message ` +\n `size of ${maxSize} bytes. Reduce the returned value, or offload large ` +\n `data (files/streams) so it travels by reference instead of inline.`,\n )\n this.name = 'PayloadTooLargeError'\n this.size = size\n this.maxSize = maxSize\n }\n}\n"
|
|
8
11
|
],
|
|
9
|
-
"mappings": "
|
|
10
|
-
"debugId": "
|
|
12
|
+
"mappings": "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",
|
|
13
|
+
"debugId": "F03D8AC816A7239664756E2164756E21",
|
|
11
14
|
"names": []
|
|
12
15
|
}
|