@stim-cli/core 1.20.1 → 1.21.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/index.d.mts CHANGED
@@ -1,13 +1,51 @@
1
1
  import { Server } from "node:net";
2
2
  //#region dir-lock.d.ts
3
3
  interface DirLockOptions {
4
- /** @deprecated Occupied locks are never expired. */
5
- staleMs?: number;
6
4
  waitMs?: number;
7
5
  pollMs?: number;
8
6
  ensureParent?: () => void;
9
7
  }
10
8
  declare function withDirLock<T>(lockPath: string, fn: () => T, { waitMs, pollMs, ensureParent }?: DirLockOptions): T;
9
+ /**
10
+ * `withDirLock` that waits for a contended lock on a timer instead of blocking the thread. `fn` still runs
11
+ * synchronously: nothing awaits between taking the lock, running `fn` and releasing the lock, so the held
12
+ * transaction and reentrant nesting behave exactly as with `withDirLock`.
13
+ * An optional signal cancels acquisition; it cannot interrupt the synchronous transaction.
14
+ */
15
+ declare function withDirLockAsync<T>(lockPath: string, fn: () => T, { waitMs, pollMs, ensureParent, signal }?: DirLockOptions & {
16
+ signal?: AbortSignal;
17
+ }): Promise<T>;
18
+ //#endregion
19
+ //#region context.d.ts
20
+ /** Where an operation writes progress that the CLI prints and an API client receives through `onProgress`. */
21
+ type StimOutput = (stream: "stdout" | "stderr", text: string) => void;
22
+ /**
23
+ * Everything one Stim operation takes from its caller instead of from `process`. The CLI builds one per command
24
+ * from `process`; `createStim` builds one per operation from the client's options. Engine code receives it as a
25
+ * parameter.
26
+ */
27
+ interface StimContext {
28
+ home: string;
29
+ buildCache: string | null;
30
+ projectRoot: string;
31
+ env: NodeJS.ProcessEnv;
32
+ cwd: string;
33
+ runId: string;
34
+ signal: AbortSignal;
35
+ output: StimOutput;
36
+ }
37
+ /**
38
+ * Runs `fn` with `context` reachable through `ambientContext()`, including after an `await` inside it. A temporary
39
+ * bridge for call sites that are not passed a context yet: `configDir()`, `runId()` and the exec wrapper's base
40
+ * environment.
41
+ */
42
+ declare function withAmbientContext<T>(context: StimContext, fn: () => T): T;
43
+ /** The context set by `withAmbientContext`, for call sites that are not passed one yet. */
44
+ declare function ambientContext(): StimContext | undefined;
45
+ /** Writes `text` to the ambient context's output, or to the process stream outside one. */
46
+ declare function ambientWrite(stream: "stdout" | "stderr", text: string): void;
47
+ /** The environment of the ambient context, or `process.env` outside one. */
48
+ declare function ambientEnv(): NodeJS.ProcessEnv;
11
49
  //#endregion
12
50
  //#region macos-resources.d.ts
13
51
  /** Whether a resource destination is a contained, non-empty path under Contents/Resources. */
@@ -17,6 +55,22 @@ declare function validMacosResourceDestination(value: unknown): value is string;
17
55
  /** Quote a path for a shell command Stim prints for a human to copy and run. */
18
56
  declare function quotedPath(path: string): string;
19
57
  //#endregion
58
+ //#region systemd-unit.d.ts
59
+ /**
60
+ * One double-quoted word as systemd.syntax(7) reads it: C escapes inside the quotes, `%` doubled because unit files
61
+ * expand specifiers, and in `ExecStart=` `$` doubled because systemd expands variables in command lines.
62
+ */
63
+ declare function quoteUnitWord(value: string, exec: boolean): string;
64
+ /** The words of a unit value `quoteUnitWord` wrote, or null when the value is not in that form. */
65
+ declare function unitWords(line: string, exec: boolean): string[] | null;
66
+ interface UnitLine {
67
+ section: string;
68
+ key: string;
69
+ value: string;
70
+ }
71
+ /** The `key=value` lines of a unit file with the section each is in, skipping comments and blank lines. */
72
+ declare function unitLines(text: string): UnitLine[];
73
+ //#endregion
20
74
  //#region artifact-store.d.ts
21
75
  declare function artifactIn(dir: string): string | null;
22
76
  declare function resolveArtifact(dir: string): string | null;
@@ -28,19 +82,29 @@ interface StoreArtifactOptions {
28
82
  }
29
83
  declare function storeArtifact(dest: string, buildPath: string, { runFile, overwrite, writeMetadata, onRenameError }: StoreArtifactOptions): string | null;
30
84
  //#endregion
85
+ //#region artifact-store-async.d.ts
86
+ interface AsyncStoreArtifactOptions extends Omit<StoreArtifactOptions, "runFile"> {
87
+ /** Resolves after the copy and its owned child work have ended. */
88
+ runFile: (file: string, args: string[]) => Promise<unknown>;
89
+ signal?: AbortSignal;
90
+ }
91
+ /** Awaits copying before atomic publication; cancellation before publication preserves the previous entry. */
92
+ declare function storeArtifactAsync(dest: string, buildPath: string, { runFile, overwrite, writeMetadata, onRenameError, signal }: AsyncStoreArtifactOptions): Promise<string | null>;
93
+ //#endregion
31
94
  //#region compiled-helper.d.ts
32
95
  /**
33
96
  * Returns `<dir>/<name>-<hash>`, compiling it with `compile` first when it is missing. The hash covers `version` and
34
97
  * each input's name and bytes, so a changed source or compiler builds a new helper beside the old one. `compile`
35
98
  * writes to a private temporary path that is renamed into place, so concurrent builders never expose a partial file.
36
- * Pruning is best effort and keeps this helper, the newest previous build, helpers modified in the last ten minutes and running helpers. It runs after a build and on the first call per process.
99
+ * Pruning is best effort and keeps this helper, the newest previous build, helpers modified in the last ten minutes and running helpers. It runs after a build and on the first call per process unless `prune` is false.
37
100
  */
38
- declare function compiledHelper({ dir, name, inputs, version, compile }: {
101
+ declare function compiledHelper({ dir, name, inputs, version, compile, prune }: {
39
102
  dir: string;
40
103
  name: string;
41
104
  inputs: readonly string[];
42
105
  version: string;
43
106
  compile: (output: string) => Promise<void>;
107
+ prune?: boolean;
44
108
  }): Promise<string>;
45
109
  //#endregion
46
110
  //#region metro-bridge.d.ts
@@ -122,6 +186,7 @@ interface CacheManifest {
122
186
  declare function cacheManifestLockPath(file: string): string;
123
187
  declare function readCacheManifest(file: string): CacheManifest;
124
188
  declare function updateCacheManifest(file: string, mutate: (caches: Array<Record<string, unknown>>) => Array<Record<string, unknown>>): CacheManifest;
189
+ declare function cacheDirRefusal(dir: string): string | null;
125
190
  declare function registerCache({ dir, name, prune, note, entriesDepth, layout, replaces }: RegisterOptions): void;
126
191
  //#endregion
127
- export { BuildRunOptions, CacheManifest, CacheRegistrationMatch, type DirLockOptions, LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, type MetroBridge, RegisterOptions, STORE_ROOT_TAG, type StoreArtifactOptions, artifactIn, buildCacheKey, buildCacheRoot, cacheManifestLockPath, cacheNameSegment, cachePathSetting, compiledHelper, configDir, createMetroBridge, createMetroGateway, ensurePrivateDir, hostSimulatorArch, metroCacheRoot, quotedPath, readCacheManifest, registerCache, resolveArtifact, rotateLog, rotatedLogPath, sharedStoreRoot, storeArtifact, tagSharedStore, updateCacheManifest, validMacosResourceDestination, withDirLock, workspaceId, workspaceLogDir, workspaceName, workspaceSlug, workspaceStateDir };
192
+ export { type AsyncStoreArtifactOptions, BuildRunOptions, CacheManifest, CacheRegistrationMatch, type DirLockOptions, LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, type MetroBridge, RegisterOptions, STORE_ROOT_TAG, type StimContext, type StimOutput, type StoreArtifactOptions, type UnitLine, ambientContext, ambientEnv, ambientWrite, artifactIn, buildCacheKey, buildCacheRoot, cacheDirRefusal, cacheManifestLockPath, cacheNameSegment, cachePathSetting, compiledHelper, configDir, createMetroBridge, createMetroGateway, ensurePrivateDir, hostSimulatorArch, metroCacheRoot, quoteUnitWord, quotedPath, readCacheManifest, registerCache, resolveArtifact, rotateLog, rotatedLogPath, sharedStoreRoot, storeArtifact, storeArtifactAsync, tagSharedStore, unitLines, unitWords, updateCacheManifest, validMacosResourceDestination, withAmbientContext, withDirLock, withDirLockAsync, workspaceId, workspaceLogDir, workspaceName, workspaceSlug, workspaceStateDir };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { S as quotedPath } from "./ownership-claim-O31Y5iIb.mjs";
2
- import { A as storeArtifact, C as workspaceSlug, D as compiledHelper, E as createMetroGateway, L as withDirLock, O as artifactIn, S as workspaceName, T as createMetroBridge, _ as sharedStoreRoot, a as buildCacheRoot, b as workspaceId, c as cachePathSetting, d as hostSimulatorArch, f as metroCacheRoot, g as rotatedLogPath, h as rotateLog, i as buildCacheKey, j as validMacosResourceDestination, k as resolveArtifact, l as configDir, m as registerCache, n as METRO_NAMED_CACHE_LAYOUT, o as cacheManifestLockPath, p as readCacheManifest, r as STORE_ROOT_TAG, s as cacheNameSegment, t as LOG_ROTATE_BYTES, u as ensurePrivateDir, v as tagSharedStore, w as workspaceStateDir, x as workspaceLogDir, y as updateCacheManifest } from "./core-CzCQ9yWY.mjs";
3
- export { LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, STORE_ROOT_TAG, artifactIn, buildCacheKey, buildCacheRoot, cacheManifestLockPath, cacheNameSegment, cachePathSetting, compiledHelper, configDir, createMetroBridge, createMetroGateway, ensurePrivateDir, hostSimulatorArch, metroCacheRoot, quotedPath, readCacheManifest, registerCache, resolveArtifact, rotateLog, rotatedLogPath, sharedStoreRoot, storeArtifact, tagSharedStore, updateCacheManifest, validMacosResourceDestination, withDirLock, workspaceId, workspaceLogDir, workspaceName, workspaceSlug, workspaceStateDir };
1
+ import { C as quotedPath } from "./ownership-claim-DHUs_Dir.mjs";
2
+ import { A as artifactIn, B as withAmbientContext, C as workspaceName, D as createMetroGateway, E as createMetroBridge, F as unitWords, I as validMacosResourceDestination, K as withDirLock, L as ambientContext, M as storeArtifact, N as quoteUnitWord, O as compiledHelper, P as unitLines, R as ambientEnv, S as workspaceLogDir, T as workspaceStateDir, _ as rotatedLogPath, a as buildCacheRoot, b as updateCacheManifest, c as cacheNameSegment, d as ensurePrivateDir, f as hostSimulatorArch, g as rotateLog, h as registerCache, i as buildCacheKey, j as resolveArtifact, k as storeArtifactAsync, l as cachePathSetting, m as readCacheManifest, n as METRO_NAMED_CACHE_LAYOUT, o as cacheDirRefusal, p as metroCacheRoot, q as withDirLockAsync, r as STORE_ROOT_TAG, s as cacheManifestLockPath, t as LOG_ROTATE_BYTES, u as configDir, v as sharedStoreRoot, w as workspaceSlug, x as workspaceId, y as tagSharedStore, z as ambientWrite } from "./core-DisO3hs1.mjs";
3
+ export { LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, STORE_ROOT_TAG, ambientContext, ambientEnv, ambientWrite, artifactIn, buildCacheKey, buildCacheRoot, cacheDirRefusal, cacheManifestLockPath, cacheNameSegment, cachePathSetting, compiledHelper, configDir, createMetroBridge, createMetroGateway, ensurePrivateDir, hostSimulatorArch, metroCacheRoot, quoteUnitWord, quotedPath, readCacheManifest, registerCache, resolveArtifact, rotateLog, rotatedLogPath, sharedStoreRoot, storeArtifact, storeArtifactAsync, tagSharedStore, unitLines, unitWords, updateCacheManifest, validMacosResourceDestination, withAmbientContext, withDirLock, withDirLockAsync, workspaceId, workspaceLogDir, workspaceName, workspaceSlug, workspaceStateDir };
@@ -0,0 +1,289 @@
1
+ //#region state/status-derived.ts
2
+ const PLATFORMS = ["ios", "android"];
3
+ function isRunning(platform, device) {
4
+ if (platform === "ios") return device.state === "Booted";
5
+ return device.state === "detected" && !device.physical;
6
+ }
7
+ function appPresence(env, platform, device) {
8
+ if (!isRunning(platform, device) || device.app?.state === "running") return null;
9
+ const everBuilt = env.builds?.[platform]?.some((entry) => entry.result === "succeeded") ?? true;
10
+ if (env.lastBuilds?.[platform]?.status === "failed" && !everBuilt) return "none";
11
+ return device.app?.state === "stopped" ? "closed" : null;
12
+ }
13
+ function deviceSlots(env) {
14
+ return [{
15
+ slot: "default",
16
+ ios: env.ios,
17
+ android: env.android
18
+ }, ...env.slots ?? []];
19
+ }
20
+ function latestBuild(env) {
21
+ let latest = null;
22
+ for (const platform of PLATFORMS) {
23
+ const build = env.lastBuilds?.[platform];
24
+ if (build && (latest === null || Date.parse(build.startedAt) > Date.parse(latest.startedAt))) latest = build;
25
+ }
26
+ return latest;
27
+ }
28
+ function stage(kind, since, platform = null) {
29
+ return {
30
+ kind,
31
+ since: since ?? null,
32
+ platform,
33
+ closedApps: []
34
+ };
35
+ }
36
+ function workspaceStage(env) {
37
+ if (env.macos?.build.state === "running") return stage("building", env.macos.build.startedAt, "macos");
38
+ if (env.macos?.build.state === "failed") return stage("build-failed", env.macos.build.finishedAt ?? env.macos.build.startedAt, "macos");
39
+ if (env.build?.state === "running") return stage("building", env.build.startedAt, env.build.platform);
40
+ const macosApp = env.macos?.state === "running" || env.macos?.state === "orphaned" ? env.macos : null;
41
+ const live = env.live || macosApp !== null;
42
+ if (!live && env.phase === "warming") return stage("warming", env.phaseSince);
43
+ if (!live && env.phase === "ready") return stage("ready", env.phaseSince);
44
+ const latest = latestBuild(env);
45
+ if (latest?.status === "failed") return stage("build-failed", latest.finishedAt ?? latest.startedAt, latest.platform);
46
+ if (live || (env.remoteDevices?.length ?? 0) > 0) {
47
+ const closedApps = deviceSlots(env).flatMap(({ slot, ...devices }) => PLATFORMS.flatMap((platform) => {
48
+ const device = devices[platform];
49
+ return device && appPresence(env, platform, device) === "closed" ? [{
50
+ platform,
51
+ slot
52
+ }] : [];
53
+ }));
54
+ return {
55
+ ...stage("running", env.supervisor?.startedAt ?? macosApp?.build.finishedAt ?? macosApp?.build.startedAt),
56
+ closedApps
57
+ };
58
+ }
59
+ const lastStop = env.metro?.lastStop;
60
+ return stage("stopped", lastStop && "at" in lastStop ? lastStop.at : null);
61
+ }
62
+ function checksState(checks) {
63
+ if (!checks) return null;
64
+ if (checks.failing > 0) return "failing";
65
+ if (checks.pending > 0) return "pending";
66
+ return checks.passing > 0 ? "passing" : null;
67
+ }
68
+ function gitChip(worktree) {
69
+ const git = worktree.git;
70
+ if (!git) return null;
71
+ const pull = worktree.pullRequest;
72
+ const parts = [];
73
+ const ahead = git.ahead ?? 0;
74
+ const behind = git.behind ?? 0;
75
+ if (ahead || behind) parts.push({
76
+ kind: "arrows",
77
+ ahead,
78
+ behind
79
+ });
80
+ const uncommitted = git.changed + git.untracked;
81
+ if (uncommitted) parts.push({
82
+ kind: "changed",
83
+ count: uncommitted
84
+ });
85
+ if (git.mergedInto !== null) {
86
+ if (pull?.state !== "merged") parts.push({
87
+ kind: "merged",
88
+ into: git.mergedInto
89
+ });
90
+ } else if (git.upstream === null) parts.push({ kind: "no-upstream" });
91
+ return {
92
+ parts,
93
+ ci: pull ? checksState(pull.checks) : null
94
+ };
95
+ }
96
+ function buildCause(build) {
97
+ if (("result" in build ? build.result : build.status) !== "failed") return null;
98
+ const at = build.diagnostics?.find((d) => d.file && d.line !== null && d.line !== void 0);
99
+ if (at) return {
100
+ key: `${at.file}:${at.line}`,
101
+ file: at.file,
102
+ line: at.line
103
+ };
104
+ return {
105
+ key: build.errorCode ?? "failed",
106
+ file: null,
107
+ line: null
108
+ };
109
+ }
110
+ function withCauses(builds) {
111
+ if (!builds) return builds;
112
+ return Object.fromEntries(Object.entries(builds).map(([platform, list]) => [platform, list?.map((build) => withCause(build))]));
113
+ }
114
+ function withCause(build) {
115
+ const cause = buildCause(build);
116
+ return cause ? {
117
+ ...build,
118
+ cause
119
+ } : build;
120
+ }
121
+ function withGitChip(worktree) {
122
+ const chip = gitChip(worktree);
123
+ return chip ? {
124
+ ...worktree,
125
+ gitChip: chip
126
+ } : worktree;
127
+ }
128
+ /** The environment with the conclusions clients draw from it: `stage`, `appPresence`, `gitChip` and `cause`. */
129
+ function withDerivedFacts(env) {
130
+ const device = (platform, record) => record ? {
131
+ ...record,
132
+ appPresence: appPresence(env, platform, record)
133
+ } : record;
134
+ const derived = {
135
+ ...env,
136
+ stage: workspaceStage(env)
137
+ };
138
+ if (env.ios) derived.ios = device("ios", env.ios);
139
+ if (env.android) derived.android = device("android", env.android);
140
+ if (env.slots) derived.slots = env.slots.map((slot) => ({
141
+ ...slot,
142
+ ios: device("ios", slot.ios),
143
+ android: device("android", slot.android)
144
+ }));
145
+ if (env.worktree) derived.worktree = withGitChip(env.worktree);
146
+ if (env.lastBuilds) derived.lastBuilds = Object.fromEntries(Object.entries(env.lastBuilds).map(([platform, build]) => [platform, withCause(build)]));
147
+ if (env.builds) derived.builds = withCauses(env.builds);
148
+ return derived;
149
+ }
150
+ //#endregion
151
+ //#region state/status-views.ts
152
+ /** How many of the newest archives the workspace list names. */
153
+ const SUMMARY_ARCHIVES = 10;
154
+ function summaryBuild({ fingerprint: _fingerprint, missReason: _missReason, diagnostics, ...build }) {
155
+ const located = diagnostics?.find((diagnostic) => diagnostic.file && diagnostic.line !== null);
156
+ const kept = build;
157
+ if (located) kept.diagnostics = [located];
158
+ return kept;
159
+ }
160
+ function summaryHistory(entries) {
161
+ const streak = entries.findIndex((entry) => entry.result !== "failed");
162
+ return entries.slice(0, Math.max(1, streak === -1 ? entries.length : streak)).map(({ result, slot, phases, configuration: _configuration, cacheKey: _cacheKey, compileSteps: _steps, ...build }) => Object.assign(summaryBuild(build), {
163
+ result,
164
+ slot,
165
+ phases
166
+ }));
167
+ }
168
+ function summaryWorktree({ pullRequest, ...worktree }) {
169
+ if (!pullRequest) return pullRequest === void 0 ? worktree : {
170
+ ...worktree,
171
+ pullRequest
172
+ };
173
+ const { reviewDecision: _reviewDecision, checkedAt: _checkedAt, ...kept } = pullRequest;
174
+ return {
175
+ ...worktree,
176
+ pullRequest: kept
177
+ };
178
+ }
179
+ /** The summary of one environment of a status payload. */
180
+ function workspaceSummary(environment) {
181
+ const { logs, builds, lastBuilds, macos, agentDevice, worktree, ...rest } = environment;
182
+ const summary = rest;
183
+ if (worktree !== void 0) summary.worktree = worktree && summaryWorktree(worktree);
184
+ if (logs !== void 0) summary.logs = logs && { errorsSinceMarker: logs.errorsSinceMarker };
185
+ if (lastBuilds) {
186
+ summary.lastBuilds = {};
187
+ if (lastBuilds.ios) summary.lastBuilds.ios = summaryBuild(lastBuilds.ios);
188
+ if (lastBuilds.android) summary.lastBuilds.android = summaryBuild(lastBuilds.android);
189
+ }
190
+ if (builds) {
191
+ summary.builds = {};
192
+ for (const platform of [
193
+ "ios",
194
+ "android",
195
+ "macos"
196
+ ]) {
197
+ const entries = builds[platform];
198
+ if (entries) summary.builds[platform] = summaryHistory(entries);
199
+ }
200
+ }
201
+ if (macos !== void 0) summary.macos = macos && (({ arguments: _arguments, ...app }) => app)(macos);
202
+ if (environment.tutorial && agentDevice) summary.agentDevice = agentDevice;
203
+ return summary;
204
+ }
205
+ function worktreeRow({ path, branch, repository, git, gitChip, pullRequest }) {
206
+ const row = { path };
207
+ if (branch !== void 0) row.branch = branch;
208
+ if (repository !== void 0) row.repository = repository;
209
+ if (git !== void 0) row.git = git;
210
+ if (gitChip !== void 0) row.gitChip = gitChip;
211
+ if (pullRequest !== void 0) row.pullRequest = pullRequest && (({ reviewDecision: _review, checkedAt: _checked, ...kept }) => kept)(pullRequest);
212
+ return row;
213
+ }
214
+ /** The live views of a status payload as `stim status --json` prints it. */
215
+ function statusViews(payload) {
216
+ const archived = payload.archived ?? [];
217
+ return {
218
+ workspaces: payload.environments.map(workspaceSummary),
219
+ worktrees: payload.unprovisionedWorktrees.map(worktreeRow),
220
+ archived: {
221
+ count: payload.archivedUsage?.count ?? archived.length,
222
+ bytes: payload.archivedUsage?.bytes ?? 0,
223
+ newest: archived.toSorted((a, b) => b.removedAt.localeCompare(a.removedAt)).slice(0, 10).map(({ id, projectRoot, project, workspace, removedAt }) => ({
224
+ id,
225
+ projectRoot,
226
+ project,
227
+ workspace,
228
+ removedAt
229
+ }))
230
+ },
231
+ machine: {
232
+ capacity: payload.capacity,
233
+ machine: payload.machine ?? null,
234
+ deviceLeases: payload.deviceLeases,
235
+ simctlAvailable: payload.simctlAvailable,
236
+ maintenance: payload.maintenance ? (({ recent: _recent, sizes: _sizes, ...rest }) => rest)(payload.maintenance) : null
237
+ },
238
+ details: new Map(payload.environments.map((environment) => [environment.path, environment]))
239
+ };
240
+ }
241
+ //#endregion
242
+ //#region state/keyed-delta.ts
243
+ /**
244
+ * Tracks the JSON of the entities last sent on one subscription, so the next emit carries only what changed. An
245
+ * entity counts as changed when its JSON differs, so the order of its fields matters and producers build entities
246
+ * with a stable field order.
247
+ */
248
+ var KeyedEntities = class {
249
+ sent = /* @__PURE__ */ new Map();
250
+ key;
251
+ constructor(key) {
252
+ this.key = key;
253
+ }
254
+ /** Records `entities` as sent in full, for a snapshot. */
255
+ reset(entities) {
256
+ this.sent = new Map(entities.map((entity) => [this.key(entity), JSON.stringify(entity)]));
257
+ }
258
+ /** The delta from what was last sent to `entities`, recorded as sent; null when nothing changed. */
259
+ next(entities) {
260
+ const sent = /* @__PURE__ */ new Map();
261
+ const upsert = [];
262
+ for (const entity of entities) {
263
+ const key = this.key(entity);
264
+ const json = JSON.stringify(entity);
265
+ sent.set(key, json);
266
+ if (this.sent.get(key) !== json) upsert.push(entity);
267
+ }
268
+ const remove = [...this.sent.keys()].filter((key) => !sent.has(key));
269
+ this.sent = sent;
270
+ return upsert.length || remove.length ? {
271
+ upsert,
272
+ remove
273
+ } : null;
274
+ }
275
+ };
276
+ /** Applies `delta` to `entities` keyed by `key`, keeping the order of `entities` and appending new keys. */
277
+ function applyKeyedDelta(entities, delta, key) {
278
+ const removed = new Set(delta.remove);
279
+ const replaced = new Map(delta.upsert.map((entity) => [key(entity), entity]));
280
+ return [...entities.flatMap((entity) => {
281
+ const id = key(entity);
282
+ if (removed.has(id)) return [];
283
+ const replacement = replaced.get(id);
284
+ replaced.delete(id);
285
+ return [replacement ?? entity];
286
+ }), ...replaced.values()];
287
+ }
288
+ //#endregion
289
+ export { workspaceSummary as a, statusViews as i, applyKeyedDelta as n, withDerivedFacts as o, SUMMARY_ARCHIVES as r, withGitChip as s, KeyedEntities as t };
@@ -0,0 +1,100 @@
1
+ //#region noise.d.ts
2
+ declare const NOISE_XX = "Noise_XX_25519_ChaChaPoly_SHA256";
3
+ declare const NOISE_XX_PSK2 = "Noise_XXpsk2_25519_ChaChaPoly_SHA256";
4
+ type NoiseProtocolName = typeof NOISE_XX | typeof NOISE_XX_PSK2;
5
+ /** Largest handshake message either side writes or reads, payload included. */
6
+ declare const MAX_HANDSHAKE_MESSAGE_BYTES = 1024;
7
+ /** Thrown for any handshake or transport failure; the handshake or transport is unusable afterwards. */
8
+ declare class NoiseError extends Error {
9
+ override name: string;
10
+ }
11
+ /** A raw 32-byte X25519 key pair. */
12
+ interface NoiseKeyPair {
13
+ readonly publicKey: Buffer;
14
+ readonly privateKey: Buffer;
15
+ }
16
+ declare function generateNoiseKeyPair(): NoiseKeyPair;
17
+ declare function noiseKeyPairFromPrivateKey(privateKey: Uint8Array): NoiseKeyPair;
18
+ /** X25519 of a raw 32-byte private key and a peer's raw 32-byte public key; it throws for a low-order key. */
19
+ type NoiseDh = (privateKey: Buffer, publicKey: Buffer) => Uint8Array;
20
+ /** Noise's CipherState: a key and a 64-bit counter used as the nonce. */
21
+ declare class CipherState {
22
+ #private;
23
+ constructor(key?: Uint8Array);
24
+ hasKey(): boolean;
25
+ setNonce(n: bigint): void;
26
+ encryptWithAd(ad: Uint8Array, plaintext: Uint8Array): Buffer;
27
+ decryptWithAd(ad: Uint8Array, ciphertext: Uint8Array): Buffer;
28
+ }
29
+ interface HandshakeOptions {
30
+ protocol: NoiseProtocolName;
31
+ initiator: boolean;
32
+ prologue: Uint8Array;
33
+ staticKeyPair: NoiseKeyPair;
34
+ /** 32 bytes, required for {@link NOISE_XX_PSK2} and refused for {@link NOISE_XX}. */
35
+ psk?: Uint8Array;
36
+ /** Fixed ephemeral key, only for reproducing published test vectors. */
37
+ ephemeralKeyPair?: NoiseKeyPair;
38
+ /**
39
+ * X25519 in place of node:crypto's, for a runtime whose node:crypto cannot derive with an imported raw public key,
40
+ * such as Cloudflare workerd.
41
+ */
42
+ dh?: NoiseDh;
43
+ }
44
+ /**
45
+ * Noise's HandshakeState for {@link NOISE_XX} and {@link NOISE_XX_PSK2}. The initiator writes messages 1 and 3. Any
46
+ * error from {@link writeMessage} or {@link readMessage}, including an out-of-turn call, a call after the last
47
+ * message, or an invalid or oversized message, ends the handshake: every later call throws {@link NoiseError}.
48
+ */
49
+ declare class HandshakeState {
50
+ #private;
51
+ constructor(options: HandshakeOptions);
52
+ get isComplete(): boolean;
53
+ /** The peer's static public key, once the message that carries it has been read and authenticated. */
54
+ get remoteStaticPublicKey(): Buffer | undefined;
55
+ get handshakeHash(): Buffer;
56
+ writeMessage(payload?: Uint8Array): Buffer;
57
+ readMessage(message: Uint8Array): Buffer;
58
+ /** Noise's Split(), returning this side's sending and receiving cipher states. It can be called once. */
59
+ split(): {
60
+ send: CipherState;
61
+ receive: CipherState;
62
+ };
63
+ }
64
+ interface NoiseTransportOptions {
65
+ send: CipherState;
66
+ receive: CipherState;
67
+ /** Largest message, in bytes (UTF-8 bytes for text), this side may send. */
68
+ maxSendBytes: number;
69
+ /** Largest message, in bytes (UTF-8 bytes for text), this side accepts. */
70
+ maxReceiveBytes: number;
71
+ }
72
+ /** A message carried by one WebSocket message: text or binary, as the sender passed it. */
73
+ type NoiseMessage = {
74
+ type: "text";
75
+ data: string;
76
+ } | {
77
+ type: "binary";
78
+ data: Buffer;
79
+ };
80
+ /**
81
+ * Stim's transport framing after the handshake: each WebSocket message is one ChaCha20-Poly1305 message with empty
82
+ * associated data under the next counter value. The first plaintext byte is the message type, 0x01 text or 0x02
83
+ * binary. Counters are not sent, so a reordered, replayed, dropped or altered message fails to decrypt. Any decryption
84
+ * failure closes the transport: every later call throws {@link NoiseError}. Nonce 2^64-1 is reserved and never used:
85
+ * after a direction's last usable message, at counter 2^64-2, the next call in that direction closes the transport.
86
+ */
87
+ declare class NoiseTransport {
88
+ #private;
89
+ constructor(options: NoiseTransportOptions);
90
+ get closed(): boolean;
91
+ /**
92
+ * Encrypts a string as a text message or bytes as a binary message. Data over `maxSendBytes` (UTF-8 bytes for text)
93
+ * throws a `RangeError`, consumes no counter, and leaves the transport open: it is the caller's own error and nothing
94
+ * was sent.
95
+ */
96
+ encrypt(data: string | Uint8Array): Buffer;
97
+ decrypt(message: Uint8Array): NoiseMessage;
98
+ }
99
+ //#endregion
100
+ export { NOISE_XX as a, NoiseError as c, NoiseProtocolName as d, NoiseTransport as f, noiseKeyPairFromPrivateKey as h, MAX_HANDSHAKE_MESSAGE_BYTES as i, NoiseKeyPair as l, generateNoiseKeyPair as m, HandshakeOptions as n, NOISE_XX_PSK2 as o, NoiseTransportOptions as p, HandshakeState as r, NoiseDh as s, CipherState as t, NoiseMessage as u };