@fjall/util 12.1.1 → 12.3.0
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/.build-source-hash +7 -4
- package/dist/.minified +1 -1
- package/dist/diagnostics/controlSequences.d.ts +8 -0
- package/dist/diagnostics/controlSequences.js +1 -0
- package/dist/diagnostics/index.d.ts +151 -0
- package/dist/diagnostics/index.js +2 -0
- package/dist/errors/absence.d.ts +21 -0
- package/dist/errors/absence.js +1 -0
- package/package.json +9 -1
package/dist/.build-source-hash
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"package.json",
|
|
10
10
|
"tsconfig.json"
|
|
11
11
|
],
|
|
12
|
-
"hash": "
|
|
12
|
+
"hash": "6bdd5fed222c37914a9331fbb1fa99dce21a102ed9159de858b1b27ae44fc62b",
|
|
13
13
|
"files": {
|
|
14
14
|
"src/async/concurrency.ts": "3c7ebe0222a720f9",
|
|
15
15
|
"src/async/singleton.ts": "7bcc21b0fb920561",
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"src/deploy/approvalTokenOutput.ts": "4be7b52825d88a37",
|
|
36
36
|
"src/devSubstrate/entrypoint.ts": "0526b88258174aa6",
|
|
37
37
|
"src/devSubstrate/params.ts": "43bf902b57eb9edd",
|
|
38
|
+
"src/diagnostics/controlSequences.ts": "cb1cc3f29f041371",
|
|
39
|
+
"src/diagnostics/index.ts": "249d04cb4dd7db35",
|
|
38
40
|
"src/docker/DockerCli.build.ts": "93a74294437cc002",
|
|
39
41
|
"src/docker/DockerCli.daemon.ts": "11b9c3352c957d7c",
|
|
40
42
|
"src/docker/DockerCli.registry.ts": "f8dfcf1d7e413014",
|
|
@@ -59,6 +61,7 @@
|
|
|
59
61
|
"src/docker/result.ts": "2ff0d59a3add8b06",
|
|
60
62
|
"src/environments.ts": "b3d2f2493554cd0f",
|
|
61
63
|
"src/errorUtils.ts": "c601f2905c74b8d0",
|
|
64
|
+
"src/errors/absence.ts": "390fea350ffbb4e5",
|
|
62
65
|
"src/failure/deploymentFailure.ts": "5824db1d97d01838",
|
|
63
66
|
"src/failure/extractErrorWindow.ts": "ca41f90946fa939c",
|
|
64
67
|
"src/fsHelpers.ts": "0869d9739d523572",
|
|
@@ -107,9 +110,9 @@
|
|
|
107
110
|
"src/securityHelpers.ts": "3f78606079c8f45e",
|
|
108
111
|
"src/targets.ts": "126280f0a5762e21",
|
|
109
112
|
"../scripts/minify-dist.mjs": "6b2e4b0df8aec601",
|
|
110
|
-
"package.json": "
|
|
113
|
+
"package.json": "da4f5b3991378803",
|
|
111
114
|
"tsconfig.json": "db759831ad95f396"
|
|
112
115
|
},
|
|
113
|
-
"outputHash": "
|
|
114
|
-
"outputFileCount":
|
|
116
|
+
"outputHash": "5d7079e0f38bcce1da6411693f11df940a142f138d4bfa6481553998cfaa1aab",
|
|
117
|
+
"outputFileCount": 203
|
|
115
118
|
}
|
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
101 files minified at 2026-08-16T01:19:29.355Z
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip terminal control sequences from a warning message before it is
|
|
3
|
+
* masked: ESC-introduced ANSI sequences, C1-CSI-introduced sequences, and
|
|
4
|
+
* every remaining C0/DEL/C1 control character except \t, \n and \r.
|
|
5
|
+
* Strip BEFORE mask (F-R12): a control character inside a credential-shaped
|
|
6
|
+
* value splits the mask regex anchors, so masking must see stripped text.
|
|
7
|
+
*/
|
|
8
|
+
export declare function stripControlSequences(value: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var C=Object.defineProperty;var u=(e,E)=>C(e,"name",{value:E,configurable:!0});const c=/\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\(B/g,n=/\u009b[0-?]*[ -/]*[@-~]/g,r=/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/g;function x(e){return e.replace(c,"").replace(n,"").replace(r,"")}u(x,"stripControlSequences");export{x as stripControlSequences};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diagnostics spine — the one guaranteed lane for severity=warning
|
|
3
|
+
* diagnostics. Producers call {@link emitWarning}; the surface that owns the
|
|
4
|
+
* terminal end of the lane binds a {@link DiagnosticsSink} via
|
|
5
|
+
* {@link configureDiagnosticsSink}; screen-scoped consumers tap
|
|
6
|
+
* {@link subscribeWarnings} and replay via {@link consumeWarnings}; and
|
|
7
|
+
* {@link drainUnconsumed} is the end-of-session backstop that keeps a
|
|
8
|
+
* warning no surface ever consumed visible rather than silent.
|
|
9
|
+
*
|
|
10
|
+
* `consumed` means exactly "this event reached a user-visible surface":
|
|
11
|
+
* a rendering sink emitted it ({@link DiagnosticsSink.rendersToUser}), a
|
|
12
|
+
* scope-matched live subscriber received it, or a consuming read replayed
|
|
13
|
+
* it onto a screen. Nothing else sets the flag, so
|
|
14
|
+
* {@link drainUnconsumed} is a true complement — everything it returns
|
|
15
|
+
* was never shown anywhere, and everything shown is never reprinted.
|
|
16
|
+
*
|
|
17
|
+
* Masking is owned structurally: emitWarning strips control sequences THEN
|
|
18
|
+
* masks (F-R12 mechanised — no call-site ordering to get wrong), over the
|
|
19
|
+
* message AND every string value inside plain-object/array metadata. The
|
|
20
|
+
* buffer and every subscriber receive the same post-mask event object; the
|
|
21
|
+
* subscription taps after the mask, never before. Sinks declaring
|
|
22
|
+
* masksInternally receive the post-strip PRE-mask message and raw metadata
|
|
23
|
+
* because their own render path masks the whole tree (the agent
|
|
24
|
+
* renderEvent maskTree, the MCP emitter) — one mask per path, decided by
|
|
25
|
+
* the sink's declaration. Producers whose payloads are masked at
|
|
26
|
+
* construction by documented contract (e.g. StepOutput.warnings)
|
|
27
|
+
* double-mask idempotently — a benign overlap, named here so nobody
|
|
28
|
+
* deletes the construction masks: they also protect persistence surfaces
|
|
29
|
+
* this spine never sees.
|
|
30
|
+
*
|
|
31
|
+
* Scope binding is explicit (per-package scope constants), never ambient.
|
|
32
|
+
* AsyncLocalStorage was considered and rejected: ALS would give
|
|
33
|
+
* per-operation isolation without scope strings, but context propagation
|
|
34
|
+
* through Ink's React scheduler and AWS SDK callback chains is not
|
|
35
|
+
* async-hooks-reliable — scope binding would be silently wrong exactly
|
|
36
|
+
* where it matters. Explicit scope constants and subscriptions are
|
|
37
|
+
* inspectable.
|
|
38
|
+
*
|
|
39
|
+
* The confirm-style warn-and-wait lane (ProgressCallbacks.onWarning(message,
|
|
40
|
+
* proceedCallback)) is a consent flow, not a diagnostic — it must never be
|
|
41
|
+
* flattened onto this fire-and-forget spine.
|
|
42
|
+
*/
|
|
43
|
+
export { stripControlSequences } from "./controlSequences.js";
|
|
44
|
+
export interface WarningEvent {
|
|
45
|
+
/**
|
|
46
|
+
* Warning text, stored and delivered post-strip+mask. Only a sink
|
|
47
|
+
* declaring masksInternally sees the post-strip pre-mask spelling.
|
|
48
|
+
*/
|
|
49
|
+
message: string;
|
|
50
|
+
/**
|
|
51
|
+
* Per-package scope constant (diagnosticScopes.ts in each consumer) —
|
|
52
|
+
* free-form scope literals are forbidden in production code.
|
|
53
|
+
*/
|
|
54
|
+
scope?: string;
|
|
55
|
+
code?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Agent TOON operation spelling; the bound sink defaults it to the
|
|
58
|
+
* bound command path when absent. Producer-owned constant (a command
|
|
59
|
+
* path), never remote text — it bypasses the strip+mask, as does
|
|
60
|
+
* `code`.
|
|
61
|
+
*/
|
|
62
|
+
operation?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Spread into the agent TOON value by the agent sink. String values in
|
|
65
|
+
* plain objects/arrays are stripped+masked for the buffered/subscriber
|
|
66
|
+
* copy; non-plain objects pass through unprocessed and must therefore
|
|
67
|
+
* never carry sensitive text.
|
|
68
|
+
*/
|
|
69
|
+
metadata?: Record<string, unknown>;
|
|
70
|
+
}
|
|
71
|
+
export interface DiagnosticsSink {
|
|
72
|
+
emit(event: WarningEvent): void;
|
|
73
|
+
/**
|
|
74
|
+
* true — the sink's render path masks (agent renderEvent maskTree, MCP
|
|
75
|
+
* emitter) and receives the post-strip PRE-mask message and raw
|
|
76
|
+
* metadata so credentials are masked exactly once. false — the sink
|
|
77
|
+
* renders verbatim and receives the post-mask event.
|
|
78
|
+
*/
|
|
79
|
+
masksInternally: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* true — emit() puts the event on a user-visible surface (stderr, the
|
|
82
|
+
* TOON stream, MCP frames), so emission marks it consumed and the
|
|
83
|
+
* end-of-session drain never reprints it. false — emit() renders
|
|
84
|
+
* nothing (the tui/mcp buffer-only defaults); the event stays
|
|
85
|
+
* unconsumed until a scope-matched subscriber, a consuming read, or
|
|
86
|
+
* the drain shows it.
|
|
87
|
+
*/
|
|
88
|
+
rendersToUser: boolean;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Emit a warning onto the spine. Strips control sequences, masks, buffers
|
|
92
|
+
* the post-mask event, delivers it to the bound sink synchronously and to
|
|
93
|
+
* scope-matched subscribers on a microtask. The event is marked consumed
|
|
94
|
+
* only when a user-visible surface actually shows it: a rendering sink at
|
|
95
|
+
* emit, or a still-subscribed scope-matched listener at dispatch.
|
|
96
|
+
*/
|
|
97
|
+
export declare function emitWarning(message: string, opts?: Omit<WarningEvent, "message">): void;
|
|
98
|
+
/**
|
|
99
|
+
* Bind the sink that owns warning rendering. Returns a restore function
|
|
100
|
+
* that reinstates the previously bound sink — guarded, so a stale restore
|
|
101
|
+
* (a surface torn down after a later rebind) never clobbers the newer
|
|
102
|
+
* binding.
|
|
103
|
+
*/
|
|
104
|
+
export declare function configureDiagnosticsSink(sink: DiagnosticsSink): () => void;
|
|
105
|
+
export interface SubscribeWarningsOptions {
|
|
106
|
+
/**
|
|
107
|
+
* Deliver only events carrying exactly this scope. The declaration
|
|
108
|
+
* lives in the spine so delivery to a non-matching listener can never
|
|
109
|
+
* mark an event consumed — a foreign-scope event stays unconsumed for
|
|
110
|
+
* its own consumer or the drain. Omitted = every event, any scope.
|
|
111
|
+
*/
|
|
112
|
+
scope?: string;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Subscribe to post-mask warning events (screen-scoped capture). Dispatch
|
|
116
|
+
* is microtask-deferred and scope-filtered by the spine. Returns
|
|
117
|
+
* unsubscribe.
|
|
118
|
+
*/
|
|
119
|
+
export declare function subscribeWarnings(listener: (event: WarningEvent) => void, options?: SubscribeWarningsOptions): () => void;
|
|
120
|
+
/**
|
|
121
|
+
* Inspect the buffered warnings for one scope WITHOUT consuming them —
|
|
122
|
+
* tests and diagnostics only. A screen replaying onto a surface must use
|
|
123
|
+
* {@link consumeWarnings}, or the drain will reprint what it showed.
|
|
124
|
+
*/
|
|
125
|
+
export declare function readWarnings(scope: string): WarningEvent[];
|
|
126
|
+
/**
|
|
127
|
+
* Read the buffered warnings for one scope onto a user-visible surface
|
|
128
|
+
* (replay-on-mount), marking them consumed. Events stay buffered so a
|
|
129
|
+
* remount replays the same set — {@link clearWarnings} at operation start
|
|
130
|
+
* deletes the shown ones.
|
|
131
|
+
*/
|
|
132
|
+
export declare function consumeWarnings(scope: string): WarningEvent[];
|
|
133
|
+
/**
|
|
134
|
+
* Delete one scope's CONSUMED buffered warnings (call when an operation
|
|
135
|
+
* starts, so back-nav or re-run never re-shows a previous run's
|
|
136
|
+
* warnings). Never-shown events survive — they belong to the next replay
|
|
137
|
+
* (where showing them consumes them) or the end-of-session drain, so a
|
|
138
|
+
* clear can never destroy a warning nobody saw. Scoped only — the full
|
|
139
|
+
* clear is resetDiagnostics, and that is for tests.
|
|
140
|
+
*/
|
|
141
|
+
export declare function clearWarnings(scope: string): void;
|
|
142
|
+
/**
|
|
143
|
+
* Return every buffered event no rendering sink, scope-matched
|
|
144
|
+
* subscriber, or consuming read ever showed, in emission order, marking
|
|
145
|
+
* them consumed — the end-of-session flush that keeps buffer-only modes
|
|
146
|
+
* honest. In rendering-sink modes every emission is consumed at emit, so
|
|
147
|
+
* a drain here is a no-op rather than a double-print.
|
|
148
|
+
*/
|
|
149
|
+
export declare function drainUnconsumed(): WarningEvent[];
|
|
150
|
+
/** Tests only: drop all buffered events and subscribers, restore the default sink. */
|
|
151
|
+
export declare function resetDiagnostics(): void;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var b=Object.defineProperty;var r=(e,t)=>b(e,"name",{value:t,configurable:!0});import{maskSensitiveOutput as g}from"../securityHelpers.js";import{stripControlSequences as y}from"./controlSequences.js";import{stripControlSequences as _}from"./controlSequences.js";const h=50,k=Symbol("fjall.diagnostics.unscoped"),o=new Map,f=new Set;let S=0;const x={masksInternally:!1,rendersToUser:!0,emit(e){process.stderr.write(`[warn] ${e.message}
|
|
2
|
+
`)}};let i=x;function m(e){if(typeof e=="string")return g(y(e));if(Array.isArray(e))return e.map(m);if(e!==null&&typeof e=="object"){const t=Object.getPrototypeOf(e);if(t===Object.prototype||t===null)return Object.fromEntries(Object.entries(e).map(([n,s])=>[n,m(s)]))}return e}r(m,"maskMetadataValue");function E(e,t){const n=y(e),s={...t,...t?.metadata!==void 0&&{metadata:m(t.metadata)},message:g(n)},a={event:s,consumed:!1,seq:S++},l=s.scope??k;let c=o.get(l);c||(c=[],o.set(l,c)),c.push(a),c.length>h&&c.shift(),i.masksInternally?i.emit({...t,message:n}):i.emit(s),i.rendersToUser&&(a.consumed=!0);const d=[...f].filter(u=>u.scope===void 0||u.scope===s.scope);d.length>0&&queueMicrotask(()=>{const u=d.filter(p=>f.has(p));if(u.length!==0){a.consumed=!0;for(const{listener:p}of u)p(s)}})}r(E,"emitWarning");function C(e){const t=i;return i=e,()=>{i===e&&(i=t)}}r(C,"configureDiagnosticsSink");function U(e,t={}){const n={listener:e,scope:t.scope};return f.add(n),()=>{f.delete(n)}}r(U,"subscribeWarnings");function W(e){const t=o.get(e);return t?t.map(n=>n.event):[]}r(W,"readWarnings");function w(e){const t=o.get(e);return t?t.map(n=>(n.consumed=!0,n.event)):[]}r(w,"consumeWarnings");function P(e){const t=o.get(e);if(!t)return;const n=t.filter(s=>!s.consumed);n.length===0?o.delete(e):o.set(e,n)}r(P,"clearWarnings");function v(){const e=[];for(const t of o.values())for(const n of t)n.consumed||(n.consumed=!0,e.push(n));return e.sort((t,n)=>t.seq-n.seq).map(t=>t.event)}r(v,"drainUnconsumed");function A(){o.clear(),f.clear(),i=x,S=0}r(A,"resetDiagnostics");export{P as clearWarnings,C as configureDiagnosticsSink,w as consumeWarnings,v as drainUnconsumed,E as emitWarning,W as readWarnings,A as resetDiagnostics,_ as stripControlSequences,U as subscribeWarnings};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared absence vocabulary. Verified absence (the thing looked for is
|
|
3
|
+
* known not to exist) is a different outcome from failure-to-determine;
|
|
4
|
+
* consumers that branch on absence probe structurally with
|
|
5
|
+
* {@link isAbsence} instead of sniffing per-class spellings or message
|
|
6
|
+
* text.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Discriminant literal for verified absence. Error classes spell absence
|
|
10
|
+
* as errorType "not_found"; wire codes derive from it via the
|
|
11
|
+
* PREFIX_${errorType.toUpperCase()} minting convention.
|
|
12
|
+
*/
|
|
13
|
+
export declare const NOT_FOUND = "not_found";
|
|
14
|
+
/**
|
|
15
|
+
* Structural absence probe: errorType "not_found" (typed service errors)
|
|
16
|
+
* or statusCode 404 (API-boundary errors). Structural rather than
|
|
17
|
+
* instanceof — class names are presentation, not contract (dist
|
|
18
|
+
* minification has mangled them before), and the probe must hold across
|
|
19
|
+
* the cli/deploy-core package boundary.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isAbsence(err: unknown): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var o=Object.defineProperty;var n=(t,e)=>o(t,"name",{value:e,configurable:!0});const s="not_found";function u(t){if(typeof t!="object"||t===null)return!1;const e=t;return e.errorType===s||e.statusCode===404}n(u,"isAbsence");export{s as NOT_FOUND,u as isAbsence};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/util",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.3.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/fjall-tech/fjall.git",
|
|
@@ -102,6 +102,14 @@
|
|
|
102
102
|
"./secrets": {
|
|
103
103
|
"types": "./dist/secrets.d.ts",
|
|
104
104
|
"default": "./dist/secrets.js"
|
|
105
|
+
},
|
|
106
|
+
"./diagnostics": {
|
|
107
|
+
"types": "./dist/diagnostics/index.d.ts",
|
|
108
|
+
"default": "./dist/diagnostics/index.js"
|
|
109
|
+
},
|
|
110
|
+
"./errors/absence": {
|
|
111
|
+
"types": "./dist/errors/absence.d.ts",
|
|
112
|
+
"default": "./dist/errors/absence.js"
|
|
105
113
|
}
|
|
106
114
|
},
|
|
107
115
|
"files": [
|