stitchkit 0.81.0 → 0.83.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/CHANGELOG.md +6613 -0
- package/README.md +4 -0
- package/dist/agent-runtime/events.d.ts +9 -2
- package/dist/agent-runtime/events.d.ts.map +1 -1
- package/dist/agent-runtime/observability.d.ts +9 -2
- package/dist/agent-runtime/observability.d.ts.map +1 -1
- package/dist/agent-runtime-harness.js +2 -2
- package/dist/agent-runtime.js +4 -4
- package/dist/application/events.d.ts +9 -2
- package/dist/application/events.d.ts.map +1 -1
- package/dist/application/latest-sink.d.ts +12 -2
- package/dist/application/latest-sink.d.ts.map +1 -1
- package/dist/application-opentelemetry.js +2 -1
- package/dist/application.js +4 -4
- package/dist/{index-6s7n2v50.js → index-64ew0vnj.js} +15 -10
- package/dist/{index-a59da114.js → index-7z1h3kbr.js} +61 -15
- package/dist/{index-1dd82z59.js → index-pq7ak4k8.js} +3 -3
- package/dist/internal/observability-sink.d.ts +49 -2
- package/dist/internal/observability-sink.d.ts.map +1 -1
- package/dist/internal/upgrade-plan.d.ts +18 -0
- package/dist/internal/upgrade-plan.d.ts.map +1 -0
- package/dist/observability/audit.d.ts +20 -4
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/index.d.ts +1 -1
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +30 -15
- package/dist/observability/status.d.ts +1 -0
- package/dist/observability/status.d.ts.map +1 -1
- package/dist/release/socket.d.ts +14 -1
- package/dist/release/socket.d.ts.map +1 -1
- package/dist/tracking/contract.d.ts +11 -0
- package/dist/tracking/contract.d.ts.map +1 -1
- package/dist/tracking-server.d.ts +1 -0
- package/dist/tracking-server.d.ts.map +1 -1
- package/dist/tracking.js +1 -0
- package/dist/upgrade-cli.d.ts +27 -0
- package/dist/upgrade-cli.d.ts.map +1 -0
- package/dist/upgrade-cli.js +177 -0
- package/llms-full.txt +185 -17
- package/package.json +6 -2
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
ObservabilitySinkStatusSchema,
|
|
4
4
|
ObservabilityStatusSchema,
|
|
5
5
|
aggregateObservabilityStatus,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
assertDrainBound,
|
|
7
|
+
createBoundedSinkManager,
|
|
8
|
+
withinBound
|
|
9
|
+
} from "../index-7z1h3kbr.js";
|
|
8
10
|
import {
|
|
9
11
|
measureSize,
|
|
10
12
|
redact,
|
|
@@ -115,7 +117,9 @@ function createObservability(config) {
|
|
|
115
117
|
const requestManager = config.request ? createSinkManager(config.request) : undefined;
|
|
116
118
|
const toolManager = config.tools ? createSinkManager(config.tools) : undefined;
|
|
117
119
|
let closed = false;
|
|
118
|
-
let
|
|
120
|
+
let drain;
|
|
121
|
+
let drainStartedAt = 0;
|
|
122
|
+
let unbounded;
|
|
119
123
|
const request = config.request ? {
|
|
120
124
|
includePayload: config.request.includePayload ?? false,
|
|
121
125
|
complete: ({ context, statusCode, durationMs, payload, outcome }) => {
|
|
@@ -232,23 +236,34 @@ function createObservability(config) {
|
|
|
232
236
|
return {
|
|
233
237
|
...request && { request },
|
|
234
238
|
toolCall: toolCall ?? {},
|
|
235
|
-
|
|
236
|
-
|
|
239
|
+
flush(bound) {
|
|
240
|
+
assertDrainBound(bound);
|
|
241
|
+
return withinBound(Promise.all([requestManager?.flush(), toolManager?.flush()]), bound);
|
|
237
242
|
},
|
|
238
243
|
getStatus,
|
|
239
|
-
close() {
|
|
240
|
-
|
|
244
|
+
close(bound) {
|
|
245
|
+
assertDrainBound(bound);
|
|
246
|
+
if (!drain) {
|
|
241
247
|
closed = true;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
248
|
+
drainStartedAt = performance.now();
|
|
249
|
+
drain = Promise.all([requestManager?.close(), toolManager?.close()]).then(() => {
|
|
250
|
+
return;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
const startedAt = drainStartedAt;
|
|
254
|
+
const report = () => {
|
|
255
|
+
const status = getStatus();
|
|
256
|
+
return ObservabilityDrainReportSchema.parse({
|
|
257
|
+
...status,
|
|
258
|
+
durationMs: performance.now() - startedAt,
|
|
259
|
+
drained: status.total.pending + status.total.preparing === 0
|
|
249
260
|
});
|
|
261
|
+
};
|
|
262
|
+
if (!bound?.signal && bound?.timeoutMs === undefined) {
|
|
263
|
+
unbounded ??= withinBound(drain, undefined).then(report);
|
|
264
|
+
return unbounded;
|
|
250
265
|
}
|
|
251
|
-
return
|
|
266
|
+
return withinBound(drain, bound).then(report);
|
|
252
267
|
}
|
|
253
268
|
};
|
|
254
269
|
}
|
|
@@ -98,6 +98,7 @@ export declare const ObservabilityDrainReportSchema: z.ZodReadonly<z.ZodObject<{
|
|
|
98
98
|
closed: z.ZodBoolean;
|
|
99
99
|
}, z.core.$strip>>;
|
|
100
100
|
durationMs: z.ZodNumber;
|
|
101
|
+
drained: z.ZodBoolean;
|
|
101
102
|
}, z.core.$strip>>;
|
|
102
103
|
export type ObservabilityDrainReport = z.infer<typeof ObservabilityDrainReportSchema>;
|
|
103
104
|
/** Build and freeze the aggregate without exposing mutable counters. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/observability/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,yEAAyE;AACzE,eAAO,MAAM,6BAA6B;;;;;;;;;;;;kBAc7B,CAAC;AAEd,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AASpF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAA6C,CAAC;AAEpF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,sFAAsF;AACtF,eAAO,MAAM,8BAA8B
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/observability/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,yEAAyE;AACzE,eAAO,MAAM,6BAA6B;;;;;;;;;;;;kBAc7B,CAAC;AAEd,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AASpF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAA6C,CAAC;AAEpF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,sFAAsF;AACtF,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAoB9B,CAAC;AAEd,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAiBtF,wEAAwE;AACxE,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,SAAS,uBAAuB,EAAE,EAC5C,MAAM,EAAE,OAAO,GACd,uBAAuB,CAiBzB"}
|
package/dist/release/socket.d.ts
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import type { ReleaseMarker } from './marker.js';
|
|
2
2
|
import type { ReleaseWatcher } from './watcher.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* The little of a Socket.IO server this binding uses — structural, no peer
|
|
5
|
+
* import.
|
|
6
|
+
*
|
|
7
|
+
* `emit` is declared over `string` because the event name is configurable. A
|
|
8
|
+
* **typed** `Server<ClientToServer, ServerToClient>` narrows its own `emit` to
|
|
9
|
+
* the names in its map, so it satisfies this only once that map declares the
|
|
10
|
+
* event — `release: (payload: { buildId: string | null }) => void`, or whatever
|
|
11
|
+
* `options.event` renames it to. Until then the compiler reports a structural
|
|
12
|
+
* mismatch between two large socket.io types, which reads like a defect in this
|
|
13
|
+
* binding and is a missing line in the application's event map. Adding it is
|
|
14
|
+
* step 3 of the guide's adoption list for exactly this reason: the same
|
|
15
|
+
* declaration is what lets the application's own `on` know the event.
|
|
16
|
+
*/
|
|
4
17
|
export interface ReleaseSocketServer {
|
|
5
18
|
on(event: 'connection', handler: (socket: ReleaseSocketEmitter) => void): unknown;
|
|
6
19
|
emit(event: string, payload: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../src/release/socket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD
|
|
1
|
+
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../src/release/socket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,mBAAmB;IAClC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC;IAClF,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC;CACnE;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC;CACnE;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC;IACnF,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC;CACtF;AAED,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,mBAAmB,EACvB,MAAM,EAAE,aAAa,EACrB,OAAO,GAAE,oBAAoB,GAAG;IAAE,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAAO,GACrE,MAAM,IAAI,CAcZ;AAED,qEAAqE;AACrE,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,oBAAyB,GACjC,MAAM,IAAI,CAQZ"}
|
|
@@ -27,6 +27,7 @@ export type TrackingContractEndpoints<TSchemas extends TrackingSchemas<z.ZodObje
|
|
|
27
27
|
track: {
|
|
28
28
|
method: 'POST';
|
|
29
29
|
path: '/events';
|
|
30
|
+
expose: readonly ['HTTP'];
|
|
30
31
|
desc: string;
|
|
31
32
|
input: TSchemas['request'];
|
|
32
33
|
output: TSchemas['response'];
|
|
@@ -40,6 +41,16 @@ export type TrackingContractEndpoints<TSchemas extends TrackingSchemas<z.ZodObje
|
|
|
40
41
|
* `sendUnloadBeacon` with a string body — the only body a document that is
|
|
41
42
|
* being unloaded can deliver to another origin (ADR 0165) — and the server
|
|
42
43
|
* therefore needs an explicit `cors.origin` allow-list for it to arrive.
|
|
44
|
+
*
|
|
45
|
+
* **Both endpoints are HTTP-only, and that is not a formality.** An endpoint
|
|
46
|
+
* with no `expose` is a tool on MCP and AGENT by default, and this contract is
|
|
47
|
+
* built here rather than by the application's own contract factory — so a
|
|
48
|
+
* project that sets `toolExposure: 'explicit'` for everything it authors was
|
|
49
|
+
* still handed a `track` tool it never asked for. An agent has nothing to gain
|
|
50
|
+
* from a browser-event ingest and one thing to lose by having it: writes into
|
|
51
|
+
* the application's visitor data, under its own name, indistinguishable
|
|
52
|
+
* afterwards from a real visitor's. `bootstrap` said `['HTTP']` from the start;
|
|
53
|
+
* `track` not saying it was an omission, not a decision.
|
|
43
54
|
*/
|
|
44
55
|
export declare function createTrackingContract<TType extends string, TScope extends string>(config: TrackingContractConfig<TType, undefined, TScope>): ContractDef<TrackingContractEndpoints<TrackingSchemas<z.ZodObject<TrackingEventShape<TType>>>>, TScope>;
|
|
45
56
|
export declare function createTrackingContract<TType extends string, TExtras extends z.ZodRawShape, TScope extends string>(config: TrackingContractConfig<TType, TExtras, TScope>): ContractDef<TrackingContractEndpoints<TrackingSchemas<z.ZodObject<TrackingEventShape<TType> & TExtras>>>, TScope>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/tracking/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,KAAK,WAAW,EAAkB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC3B,MAAM,WAAW,CAAC;AAEnB,MAAM,WAAW,sBAAsB,CACrC,KAAK,SAAS,MAAM,EACpB,OAAO,SAAS,CAAC,CAAC,WAAW,GAAG,SAAS,EACzC,MAAM,SAAS,MAAM,CACrB,SAAQ,qBAAqB,CAAC,KAAK,EAAE,OAAO,CAAC;IAC7C,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,yFAAyF;AACzF,MAAM,MAAM,yBAAyB,CAAC,QAAQ,SAAS,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI;IACrF,SAAS,EAAE;QACT,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,QAAQ,CAAC;QACf,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;KAC/B,CAAC;IACF,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,SAAS,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC3B,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC7B,cAAc,EAAE,IAAI,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/tracking/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,KAAK,WAAW,EAAkB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC3B,MAAM,WAAW,CAAC;AAEnB,MAAM,WAAW,sBAAsB,CACrC,KAAK,SAAS,MAAM,EACpB,OAAO,SAAS,CAAC,CAAC,WAAW,GAAG,SAAS,EACzC,MAAM,SAAS,MAAM,CACrB,SAAQ,qBAAqB,CAAC,KAAK,EAAE,OAAO,CAAC;IAC7C,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,yFAAyF;AACzF,MAAM,MAAM,yBAAyB,CAAC,QAAQ,SAAS,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI;IACrF,SAAS,EAAE;QACT,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,QAAQ,CAAC;QACf,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;KAC/B,CAAC;IACF,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,SAAS,CAAC;QAChB,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC3B,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC7B,cAAc,EAAE,IAAI,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,SAAS,MAAM,EAChF,MAAM,EAAE,sBAAsB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,GACvD,WAAW,CACZ,yBAAyB,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAClF,MAAM,CACP,CAAC;AACF,wBAAgB,sBAAsB,CACpC,KAAK,SAAS,MAAM,EACpB,OAAO,SAAS,CAAC,CAAC,WAAW,EAC7B,MAAM,SAAS,MAAM,EAErB,MAAM,EAAE,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,GACrD,WAAW,CACZ,yBAAyB,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAC5F,MAAM,CACP,CAAC"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* back; `issueVisitLease` runs the visit algorithm over a store interface the
|
|
6
6
|
* application implements. No database, no schema, no domain. → ADR 0166.
|
|
7
7
|
*/
|
|
8
|
+
export type { TrackingDisposition, VisitBootstrapResponse, VisitEntryContext, } from './tracking/schemas.js';
|
|
8
9
|
export { type ActiveIntervalOptions, type ActiveTimeInterval, activeIntervalOf, } from './tracking/server/active-interval.js';
|
|
9
10
|
export { DEFAULT_BOT_USER_AGENT_PATTERN, isBotUserAgent } from './tracking/server/bot.js';
|
|
10
11
|
export { type DispositionEvent, type DispositionInput, type DispositionResult, dispositionTrackingBatch, type KnownVisit, } from './tracking/server/disposition.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracking-server.d.ts","sourceRoot":"","sources":["../src/tracking-server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"tracking-server.d.ts","sourceRoot":"","sources":["../src/tracking-server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,YAAY,EACV,mBAAmB,EACnB,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,gBAAgB,GACjB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,8BAA8B,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,wBAAwB,EACxB,KAAK,UAAU,GAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,KAAK,aAAa,EAClB,KAAK,gBAAgB,GACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,+BAA+B,CAAC"}
|
package/dist/tracking.js
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export interface UpgradeCliResult {
|
|
3
|
+
/** What to write to stdout. */
|
|
4
|
+
readonly output: string;
|
|
5
|
+
/** Process exit code — 0 when the plan (or "nothing to do") was produced. */
|
|
6
|
+
readonly code: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The whole command as a function of its arguments and the filesystem, so the
|
|
10
|
+
* gate can exercise the paths that matter — a project with no stitchkit
|
|
11
|
+
* installed, a range that runs backwards, a changelog the package did not ship.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runUpgradeCli(argv: readonly string[]): UpgradeCliResult;
|
|
14
|
+
/**
|
|
15
|
+
* Was this file executed as a program, rather than imported?
|
|
16
|
+
*
|
|
17
|
+
* Through the real install path that is a **symlink** question, not a string
|
|
18
|
+
* one: npm and bun link `node_modules/.bin/stitchkit` at the built file, Node
|
|
19
|
+
* keeps the link in `argv[1]` and resolves `import.meta.url` to its target. A
|
|
20
|
+
* direct comparison of the two is therefore false for every consumer, and the
|
|
21
|
+
* binary becomes a silent no-op — exit 0, no output, nothing to notice — while
|
|
22
|
+
* running the same file in place works perfectly. Both arguments are passed in
|
|
23
|
+
* so this is decidable without launching a process: under Bun `argv[1]` is
|
|
24
|
+
* already resolved, so a spawned symlink proves nothing about Node.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isDirectInvocation(invoked: string | undefined, self: string): boolean;
|
|
27
|
+
//# sourceMappingURL=upgrade-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade-cli.d.ts","sourceRoot":"","sources":["../src/upgrade-cli.ts"],"names":[],"mappings":";AA4DA,MAAM,WAAW,gBAAgB;IAC/B,+BAA+B;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,gBAAgB,CAqCvE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CASrF"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/upgrade-cli.ts
|
|
4
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
5
|
+
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
+
|
|
8
|
+
// src/internal/upgrade-plan.ts
|
|
9
|
+
function semverParts(version) {
|
|
10
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
|
|
11
|
+
if (!match)
|
|
12
|
+
throw new Error(`Expected an exact semver, received "${version}"`);
|
|
13
|
+
const [, major, minor, patch] = match;
|
|
14
|
+
if (major === undefined || minor === undefined || patch === undefined) {
|
|
15
|
+
throw new Error(`Expected an exact semver, received "${version}"`);
|
|
16
|
+
}
|
|
17
|
+
return [Number(major), Number(minor), Number(patch)];
|
|
18
|
+
}
|
|
19
|
+
function compareSemver(left, right) {
|
|
20
|
+
const a = semverParts(left);
|
|
21
|
+
const b = semverParts(right);
|
|
22
|
+
for (const index of [0, 1, 2]) {
|
|
23
|
+
const difference = a[index] - b[index];
|
|
24
|
+
if (difference !== 0)
|
|
25
|
+
return difference;
|
|
26
|
+
}
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
function planUpgrade(changelog, from, to) {
|
|
30
|
+
semverParts(from);
|
|
31
|
+
semverParts(to);
|
|
32
|
+
if (compareSemver(from, to) >= 0) {
|
|
33
|
+
throw new Error(`Upgrade range must increase: ${from} → ${to}`);
|
|
34
|
+
}
|
|
35
|
+
const releases = [...changelog.matchAll(/^## \[(\d+\.\d+\.\d+)\].*$/gm)];
|
|
36
|
+
const changes = [];
|
|
37
|
+
for (const [index, release] of releases.entries()) {
|
|
38
|
+
const version = release[1];
|
|
39
|
+
if (version === undefined)
|
|
40
|
+
continue;
|
|
41
|
+
if (compareSemver(version, from) <= 0 || compareSemver(version, to) > 0)
|
|
42
|
+
continue;
|
|
43
|
+
const bodyStart = (release.index ?? 0) + release[0].length;
|
|
44
|
+
const bodyEnd = releases[index + 1]?.index ?? changelog.length;
|
|
45
|
+
const body = changelog.slice(bodyStart, bodyEnd);
|
|
46
|
+
const breakingHeader = /^### ⚠️ Breaking changes\s*$/m.exec(body);
|
|
47
|
+
if (!breakingHeader)
|
|
48
|
+
continue;
|
|
49
|
+
const breakingStart = (breakingHeader.index ?? 0) + breakingHeader[0].length;
|
|
50
|
+
const afterHeader = body.slice(breakingStart);
|
|
51
|
+
const nextSection = /^### /m.exec(afterHeader);
|
|
52
|
+
const markdown = afterHeader.slice(0, nextSection?.index ?? afterHeader.length).trim();
|
|
53
|
+
const who = /^\*\*Who must act:\*\*\s*([\s\S]*?)(?=\n\s*\n|\n[-*] )/m.exec(markdown);
|
|
54
|
+
changes.push({
|
|
55
|
+
version,
|
|
56
|
+
whoMustAct: who?.[1]?.replace(/\s+/g, " ").trim() ?? "Not declared in this legacy changelog section.",
|
|
57
|
+
markdown
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return changes.sort((left, right) => compareSemver(left.version, right.version));
|
|
61
|
+
}
|
|
62
|
+
function renderUpgradePlan(changes, from, to) {
|
|
63
|
+
const header = `# Stitchkit upgrade ${from} → ${to}`;
|
|
64
|
+
if (changes.length === 0)
|
|
65
|
+
return `${header}
|
|
66
|
+
|
|
67
|
+
No breaking sections in this range.
|
|
68
|
+
`;
|
|
69
|
+
return `${header}
|
|
70
|
+
|
|
71
|
+
${changes.map((change) => `## ${change.version}
|
|
72
|
+
|
|
73
|
+
**Who must act:** ${change.whoMustAct}
|
|
74
|
+
|
|
75
|
+
${change.markdown.replace(/^\*\*Who must act:\*\*[\s\S]*?(?=\n\s*\n|\n[-*] )/m, "").trim()}`).join(`
|
|
76
|
+
|
|
77
|
+
`)}
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/upgrade-cli.ts
|
|
82
|
+
var USAGE = `stitchkit upgrade — print every breaking change between the installed version and this one
|
|
83
|
+
|
|
84
|
+
bunx stitchkit@latest upgrade in a project that depends on stitchkit
|
|
85
|
+
npx stitchkit@latest upgrade
|
|
86
|
+
|
|
87
|
+
Options
|
|
88
|
+
--from <version> installed version (default: the stitchkit in ./node_modules)
|
|
89
|
+
--to <version> target version (default: the version of this package)
|
|
90
|
+
--cwd <dir> project to read the installed version from (default: .)
|
|
91
|
+
--changelog <path> changelog to plan from (default: the one in this package)
|
|
92
|
+
`;
|
|
93
|
+
function option(argv, name) {
|
|
94
|
+
const index = argv.indexOf(`--${name}`);
|
|
95
|
+
if (index < 0)
|
|
96
|
+
return;
|
|
97
|
+
const value = argv[index + 1];
|
|
98
|
+
if (value === undefined || value.startsWith("--")) {
|
|
99
|
+
throw new Error(`--${name} needs a value`);
|
|
100
|
+
}
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
function packageRoot() {
|
|
104
|
+
return resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
105
|
+
}
|
|
106
|
+
function versionOf(packageJsonPath) {
|
|
107
|
+
let text;
|
|
108
|
+
try {
|
|
109
|
+
text = readFileSync(packageJsonPath, "utf8");
|
|
110
|
+
} catch {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const parsed = JSON.parse(text);
|
|
114
|
+
if (typeof parsed !== "object" || parsed === null || !("version" in parsed))
|
|
115
|
+
return;
|
|
116
|
+
const { version } = parsed;
|
|
117
|
+
return typeof version === "string" ? version : undefined;
|
|
118
|
+
}
|
|
119
|
+
function runUpgradeCli(argv) {
|
|
120
|
+
if (argv.includes("--help") || argv.includes("-h") || argv.length === 0) {
|
|
121
|
+
return { output: USAGE, code: 0 };
|
|
122
|
+
}
|
|
123
|
+
const [command] = argv;
|
|
124
|
+
if (command !== "upgrade") {
|
|
125
|
+
return { output: `Unknown command "${command}".
|
|
126
|
+
|
|
127
|
+
${USAGE}`, code: 1 };
|
|
128
|
+
}
|
|
129
|
+
const root = packageRoot();
|
|
130
|
+
const to = option(argv, "to") ?? versionOf(join(root, "package.json"));
|
|
131
|
+
if (to === undefined) {
|
|
132
|
+
throw new Error(`Cannot read the version of the stitchkit at ${root}`);
|
|
133
|
+
}
|
|
134
|
+
const cwd = resolve(option(argv, "cwd") ?? process.cwd());
|
|
135
|
+
const from = option(argv, "from") ?? versionOf(join(cwd, "node_modules", "stitchkit", "package.json"));
|
|
136
|
+
if (from === undefined) {
|
|
137
|
+
throw new Error(`No stitchkit found in ${join(cwd, "node_modules")} — run this in the project that depends on it, or pass --from <installed version>`);
|
|
138
|
+
}
|
|
139
|
+
if (from === to) {
|
|
140
|
+
return { output: `Already on ${to}. Nothing to upgrade.
|
|
141
|
+
`, code: 0 };
|
|
142
|
+
}
|
|
143
|
+
const changelogPath = option(argv, "changelog") ?? join(root, "CHANGELOG.md");
|
|
144
|
+
let changelog;
|
|
145
|
+
try {
|
|
146
|
+
changelog = readFileSync(changelogPath, "utf8");
|
|
147
|
+
} catch {
|
|
148
|
+
throw new Error(`Cannot read the changelog at ${changelogPath}`);
|
|
149
|
+
}
|
|
150
|
+
return { output: renderUpgradePlan(planUpgrade(changelog, from, to), from, to), code: 0 };
|
|
151
|
+
}
|
|
152
|
+
function isDirectInvocation(invoked, self) {
|
|
153
|
+
if (invoked === undefined)
|
|
154
|
+
return false;
|
|
155
|
+
if (invoked === self || pathToFileURL(invoked).href === pathToFileURL(self).href)
|
|
156
|
+
return true;
|
|
157
|
+
try {
|
|
158
|
+
return realpathSync(invoked) === realpathSync(self);
|
|
159
|
+
} catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (isDirectInvocation(process.argv[1], fileURLToPath(import.meta.url))) {
|
|
164
|
+
try {
|
|
165
|
+
const { output, code } = runUpgradeCli(process.argv.slice(2));
|
|
166
|
+
process.stdout.write(output);
|
|
167
|
+
process.exit(code);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
170
|
+
`);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export {
|
|
175
|
+
runUpgradeCli,
|
|
176
|
+
isDirectInvocation
|
|
177
|
+
};
|