@melite/estate-drafts 0.1.4-alpha → 0.1.5-pre-alpha
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 +56 -0
- package/lib/bench.d.ts +48 -1
- package/lib/bench.js +87 -12
- package/lib/index.d.ts +4 -1
- package/lib/index.js +5 -1
- package/lib/remote.d.ts +29 -10
- package/lib/remote.js +347 -156
- package/lib/spec.d.ts +28 -1
- package/lib/spec.js +32 -2
- package/lib/store.d.ts +39 -5
- package/lib/store.js +130 -42
- package/lib/trail.d.ts +16 -0
- package/lib/trail.js +91 -0
- package/lib/typert.host.js +24 -21
- package/lib/typert.remote-client.js +15 -15
- package/package.json +7 -3
package/lib/spec.d.ts
CHANGED
|
@@ -15,6 +15,23 @@ declare const DraftRecordSchema: z.ZodObject<{
|
|
|
15
15
|
}, z.core.$loose>;
|
|
16
16
|
}, z.core.$strip>;
|
|
17
17
|
export type DraftRecord = z.infer<typeof DraftRecordSchema>;
|
|
18
|
+
/** One character record (audit 004/C2): the reusable identity
|
|
19
|
+
* bundle. `references` is the ORDERED reference-image list — path
|
|
20
|
+
* strings of exactly the class the submit walk stages (data URLs,
|
|
21
|
+
* http(s), estate asset URLs, runs-relative and cwd-relative
|
|
22
|
+
* paths; `uploadReferenced` walks the whole graph and stages any
|
|
23
|
+
* of them, audit 004 §3), order preserved — sheet views are
|
|
24
|
+
* position-coded. `description` is the LOCKED identity prompt (a
|
|
25
|
+
* character-sheet ten-section block, audit 004/C7's skill); the
|
|
26
|
+
* run-time seam (how it reaches a prompt) is C3's decision, not
|
|
27
|
+
* this table's. */
|
|
28
|
+
declare const CharacterRecordSchema: z.ZodObject<{
|
|
29
|
+
name: z.ZodString;
|
|
30
|
+
references: z.ZodArray<z.ZodString>;
|
|
31
|
+
description: z.ZodString;
|
|
32
|
+
updated_at: z.ZodNumber;
|
|
33
|
+
}, z.core.$strip>;
|
|
34
|
+
export type CharacterRecord = z.infer<typeof CharacterRecordSchema>;
|
|
18
35
|
/** The draft-id law (editor_runs.818-822 heritage): 1..96 chars,
|
|
19
36
|
* [A-Za-z0-9_-] ASCII only, path-safe. THE SSOT — the queue's
|
|
20
37
|
* submit gate and the drafts remote both test this one address
|
|
@@ -28,10 +45,14 @@ export declare const DRAFT_ID_PATTERN: RegExp;
|
|
|
28
45
|
export declare const DRAFT_ID_MAX_LEN = 96;
|
|
29
46
|
/** draft_id_ok (the doors' hand-loop drop-in): the law, one voice. */
|
|
30
47
|
export declare function draftIdOk(draftId: string): boolean;
|
|
31
|
-
/** The drafts domain (unit dir `drafts/`, version
|
|
48
|
+
/** The drafts domain (unit dir `drafts/`, version 2 — the
|
|
49
|
+
* characters table join; version-1 record documents stay
|
|
50
|
+
* readable through compatibleVersions, the per-record
|
|
51
|
+
* contract's accepted-stamp set). */
|
|
32
52
|
export declare const draftsDomainSpec: {
|
|
33
53
|
name: string;
|
|
34
54
|
version: number;
|
|
55
|
+
compatibleVersions: number[];
|
|
35
56
|
layout: "per-record";
|
|
36
57
|
invalidRecords: "backup-and-skip";
|
|
37
58
|
global: {
|
|
@@ -54,6 +75,12 @@ export declare const draftsDomainSpec: {
|
|
|
54
75
|
}[];
|
|
55
76
|
};
|
|
56
77
|
}>;
|
|
78
|
+
characters: import("@deepseek-ai/dsh-storage-domain").DomainTableSpec<string, {
|
|
79
|
+
name: string;
|
|
80
|
+
references: string[];
|
|
81
|
+
description: string;
|
|
82
|
+
updated_at: number;
|
|
83
|
+
}>;
|
|
57
84
|
};
|
|
58
85
|
};
|
|
59
86
|
export type DraftsDomainSpec = typeof draftsDomainSpec;
|
package/lib/spec.js
CHANGED
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
// skipped by the facility (`invalidRecords: 'backup-and-skip'`).
|
|
8
8
|
// The global slot is the one-time legacy-import marker: 0 until the
|
|
9
9
|
// legacy drafts tree has been imported, the import epoch (ms) after.
|
|
10
|
+
//
|
|
11
|
+
|
|
12
|
+
// character bundle (ordered reference paths + a locked description),
|
|
13
|
+
// same id grammar as drafts (one address class, one law — the
|
|
14
|
+
// per-record layout's SAFE_KEY_RE is exactly DRAFT_ID_RE's set), the
|
|
15
|
+
// storage root itself is the tenant boundary. Version 2 with
|
|
16
|
+
// compatibleVersions [1]: existing v1-stamped record documents keep
|
|
17
|
+
// reading green (the per-record contract accepts the declared set),
|
|
18
|
+
// new writes stamp 2.
|
|
10
19
|
import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
|
|
11
20
|
import { z } from "zod";
|
|
12
21
|
/** One draft doc — the composer's shape: six contract keys plus
|
|
@@ -28,6 +37,22 @@ const DraftRecordSchema = z.object({
|
|
|
28
37
|
updated_at: z.number().int(),
|
|
29
38
|
doc: DraftDocSchema,
|
|
30
39
|
});
|
|
40
|
+
/** One character record (audit 004/C2): the reusable identity
|
|
41
|
+
* bundle. `references` is the ORDERED reference-image list — path
|
|
42
|
+
* strings of exactly the class the submit walk stages (data URLs,
|
|
43
|
+
* http(s), estate asset URLs, runs-relative and cwd-relative
|
|
44
|
+
* paths; `uploadReferenced` walks the whole graph and stages any
|
|
45
|
+
* of them, audit 004 §3), order preserved — sheet views are
|
|
46
|
+
* position-coded. `description` is the LOCKED identity prompt (a
|
|
47
|
+
* character-sheet ten-section block, audit 004/C7's skill); the
|
|
48
|
+
* run-time seam (how it reaches a prompt) is C3's decision, not
|
|
49
|
+
* this table's. */
|
|
50
|
+
const CharacterRecordSchema = z.object({
|
|
51
|
+
name: z.string().min(1),
|
|
52
|
+
references: z.array(z.string()),
|
|
53
|
+
description: z.string(),
|
|
54
|
+
updated_at: z.number().int(),
|
|
55
|
+
});
|
|
31
56
|
/** The draft-id law (editor_runs.818-822 heritage): 1..96 chars,
|
|
32
57
|
* [A-Za-z0-9_-] ASCII only, path-safe. THE SSOT — the queue's
|
|
33
58
|
* submit gate and the drafts remote both test this one address
|
|
@@ -43,15 +68,20 @@ export const DRAFT_ID_MAX_LEN = 96;
|
|
|
43
68
|
export function draftIdOk(draftId) {
|
|
44
69
|
return DRAFT_ID_RE.test(draftId);
|
|
45
70
|
}
|
|
46
|
-
/** The drafts domain (unit dir `drafts/`, version
|
|
71
|
+
/** The drafts domain (unit dir `drafts/`, version 2 — the
|
|
72
|
+
* characters table join; version-1 record documents stay
|
|
73
|
+
* readable through compatibleVersions, the per-record
|
|
74
|
+
* contract's accepted-stamp set). */
|
|
47
75
|
export const draftsDomainSpec = defineDomain({
|
|
48
76
|
name: "drafts",
|
|
49
|
-
version:
|
|
77
|
+
version: 2,
|
|
78
|
+
compatibleVersions: [1],
|
|
50
79
|
layout: "per-record",
|
|
51
80
|
invalidRecords: "backup-and-skip",
|
|
52
81
|
global: { schema: z.number().int(), initial: 0 },
|
|
53
82
|
tables: {
|
|
54
83
|
docs: domainTable(DraftRecordSchema),
|
|
84
|
+
characters: domainTable(CharacterRecordSchema),
|
|
55
85
|
},
|
|
56
86
|
});
|
|
57
87
|
//# sourceMappingURL=spec.js.map
|
package/lib/store.d.ts
CHANGED
|
@@ -1,12 +1,46 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { Service, type Context } from "@deepseek-ai/cordis";
|
|
2
2
|
import type { Domain } from "@deepseek-ai/dsh-storage-domain";
|
|
3
3
|
import type { DraftsDomainSpec } from "./spec.js";
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
/** The drafts plugin's config (J12: the retired
|
|
5
|
+
* MELITE_STORAGE_ROOT / MELITE_STATE_ROOT env seams' config
|
|
6
|
+
* twin — the unit-file root the json backend hangs under,
|
|
7
|
+
* named at the composition site, never read from the env). */
|
|
8
|
+
export interface DraftsConfig {
|
|
9
|
+
/** The storage unit-file root (`<stateRoot>/storage` at the
|
|
10
|
+
* composition site); default `data/runs/storage`. */
|
|
11
|
+
storageRoot?: string;
|
|
12
|
+
}
|
|
13
|
+
/** The json backend's unit-file root. */
|
|
14
|
+
export declare function resolveStorageRoot(config?: Readonly<DraftsConfig>): string;
|
|
15
|
+
/** The fiber-scoped seat owning the drafts domain handles (id
|
|
16
|
+
* `meliteDraftsDomain` — the estate-seat prefix law, cf.
|
|
17
|
+
* skillRegistry.ts:20-28): one instance per mounting fiber, so the
|
|
18
|
+
* cached opens die with the fiber instead of living at module
|
|
19
|
+
* scope. Fields are TS-`private`, never `#private` — cordis
|
|
20
|
+
* `ctx.get` returns a proxy and private class fields die through
|
|
21
|
+
* it ("Cannot read private member ... whose class did not declare
|
|
22
|
+
* it"; the flows seat, flowsData.ts, is the measured precedent). */
|
|
23
|
+
export declare class DraftsDomainSeatService extends Service {
|
|
24
|
+
static readonly inject: {
|
|
25
|
+
readonly optional: readonly [];
|
|
26
|
+
};
|
|
27
|
+
/** Per-facility open handles, keyed by the facility object (one
|
|
28
|
+
* slot per provider; GC-bounded, fiber-lifetime). */
|
|
29
|
+
private cached;
|
|
30
|
+
constructor(ctx: Context, config?: Readonly<DraftsConfig>);
|
|
31
|
+
/** The composition site's named storage root (J12: config —
|
|
32
|
+
* the retired env seam's twin). */
|
|
33
|
+
readonly config: Readonly<DraftsConfig>;
|
|
34
|
+
|
|
8
35
|
|
|
9
36
|
|
|
10
37
|
|
|
38
|
+
open(): Promise<Domain<DraftsDomainSpec>>;
|
|
39
|
+
}
|
|
40
|
+
/** Open (or reuse) the drafts domain through the mounted seat. The
|
|
41
|
+
* apply mounts `DraftsDomainSeatService` before the remote
|
|
42
|
+
* services; tests mount it on their own Context.
|
|
43
|
+
* @param ctx - the Context whose fiber carries the seat.
|
|
44
|
+
* @returns the ready domain handle. */
|
|
11
45
|
export declare function openDraftsDomain(ctx: Context): Promise<Domain<DraftsDomainSpec>>;
|
|
12
46
|
//# sourceMappingURL=store.d.ts.map
|
package/lib/store.js
CHANGED
|
@@ -1,27 +1,34 @@
|
|
|
1
|
+
// store.ts — open the drafts domain through the host's facility.
|
|
2
|
+
//
|
|
3
|
+
// The facility seat (`ctx.storageDomain`) is provided by the HOST's
|
|
4
|
+
// storage rows (json backend + domain facility); this package never
|
|
5
|
+
// bundles them. The open is lazy and process-scoped (one open per
|
|
6
|
+
// storage root — the domain write chain serializes concurrent puts,
|
|
7
|
+
// so sharing the handle is correct), cached on first use.
|
|
8
|
+
//
|
|
9
|
+
// First open runs the one-time legacy import (the pre-domain
|
|
10
|
+
// STATE_ROOT/drafts tree → the domain), guarded by the global slot:
|
|
11
|
+
// runs only while it serves the initial 0, then stamps the epoch —
|
|
12
|
+
// a wiped unit never re-imports frozen ghosts. The legacy files stay
|
|
13
|
+
// untouched on disk — evidence, never truth.
|
|
14
|
+
import { Service } from "@deepseek-ai/cordis";
|
|
1
15
|
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
16
|
import { join } from "node:path";
|
|
17
|
+
import { isPurelyUnstatedRequiredVoice } from "@melite/contracts/laneRelations";
|
|
3
18
|
import { draftsDomainSpec } from "./spec.js";
|
|
4
|
-
/**
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
const STATE_ROOT_ENV = "MELITE_STATE_ROOT";
|
|
8
|
-
/** The json backend's unit-file root: `<stateRoot>/storage`. */
|
|
9
|
-
export function resolveStorageRoot() {
|
|
10
|
-
const override = process.env[STORAGE_ROOT_ENV] ?? "";
|
|
11
|
-
if (override !== "")
|
|
12
|
-
return override;
|
|
13
|
-
const state = process.env[STATE_ROOT_ENV] ?? "data/runs";
|
|
14
|
-
return join(state, "storage");
|
|
19
|
+
/** The json backend's unit-file root. */
|
|
20
|
+
export function resolveStorageRoot(config = {}) {
|
|
21
|
+
return config.storageRoot ?? join("data", "runs", "storage");
|
|
15
22
|
}
|
|
16
23
|
/** The one-time legacy-drafts import (the pre-domain drafts tree →
|
|
17
24
|
* the drafts domain). Unparseable/non-object legacy files skip;
|
|
18
25
|
* schema-failing rows skip individually; the rest import. */
|
|
19
|
-
async function importLegacyDrafts(drafts) {
|
|
26
|
+
async function importLegacyDrafts(drafts, storageRoot) {
|
|
20
27
|
const marker = drafts.global;
|
|
21
28
|
if (marker.get() !== 0)
|
|
22
29
|
return;
|
|
23
30
|
const docs = drafts.table("docs");
|
|
24
|
-
const legacyDir = join(
|
|
31
|
+
const legacyDir = join(storageRoot, "..", "drafts");
|
|
25
32
|
let names;
|
|
26
33
|
try {
|
|
27
34
|
names = readdirSync(legacyDir);
|
|
@@ -66,41 +73,122 @@ async function importLegacyDrafts(drafts) {
|
|
|
66
73
|
}
|
|
67
74
|
await marker.set(Date.now());
|
|
68
75
|
}
|
|
69
|
-
let cached = new WeakMap();
|
|
70
76
|
|
|
71
77
|
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
async function healReplayedRefusals(drafts) {
|
|
89
|
+
const docs = drafts.table("docs");
|
|
90
|
+
for (const [did, rec] of docs.entries()) {
|
|
91
|
+
// persisted rows are a durable boundary: a record without a doc
|
|
92
|
+
// object (or without stages) carries no bench row to heal
|
|
93
|
+
const doc = typeof rec === "object" && rec !== null ? rec.doc : undefined;
|
|
94
|
+
if (typeof doc !== "object" || doc === null)
|
|
95
|
+
continue;
|
|
96
|
+
const stages = Array.isArray(doc.stages) ? doc.stages : [];
|
|
97
|
+
let changed = false;
|
|
98
|
+
for (const stage of stages) {
|
|
99
|
+
if (typeof stage !== "object" || stage === null)
|
|
100
|
+
continue;
|
|
101
|
+
const row = stage;
|
|
102
|
+
const runStatus = row["runStatus"];
|
|
103
|
+
if (runStatus !== "failed" && runStatus !== "error")
|
|
104
|
+
continue;
|
|
105
|
+
const result = row["result"];
|
|
106
|
+
const error = typeof result === "object" && result !== null
|
|
107
|
+
? result["error"]
|
|
108
|
+
: undefined;
|
|
109
|
+
// PURELY the class (the mixed-carry guard): a composite
|
|
110
|
+
// refusal can join the stem with a stated-wrong half — that
|
|
111
|
+
// row keeps its failed face, only whole-stem texts heal
|
|
112
|
+
if (typeof error !== "string" || !isPurelyUnstatedRequiredVoice(error)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
row["runStatus"] = "";
|
|
116
|
+
row["result"] = {};
|
|
117
|
+
changed = true;
|
|
118
|
+
}
|
|
119
|
+
if (changed)
|
|
120
|
+
await docs.put(did, rec);
|
|
82
121
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
122
|
+
}
|
|
123
|
+
/** The fiber-scoped seat owning the drafts domain handles (id
|
|
124
|
+
* `meliteDraftsDomain` — the estate-seat prefix law, cf.
|
|
125
|
+
* skillRegistry.ts:20-28): one instance per mounting fiber, so the
|
|
126
|
+
* cached opens die with the fiber instead of living at module
|
|
127
|
+
* scope. Fields are TS-`private`, never `#private` — cordis
|
|
128
|
+
* `ctx.get` returns a proxy and private class fields die through
|
|
129
|
+
* it ("Cannot read private member ... whose class did not declare
|
|
130
|
+
* it"; the flows seat, flowsData.ts, is the measured precedent). */
|
|
131
|
+
export class DraftsDomainSeatService extends Service {
|
|
132
|
+
static inject = { optional: [] };
|
|
133
|
+
/** Per-facility open handles, keyed by the facility object (one
|
|
134
|
+
* slot per provider; GC-bounded, fiber-lifetime). */
|
|
135
|
+
cached = new WeakMap();
|
|
136
|
+
constructor(ctx, config = {}) {
|
|
137
|
+
// the estate-seat id: dsh core may own the bare name
|
|
138
|
+
super(ctx, "meliteDraftsDomain");
|
|
139
|
+
this.config = config;
|
|
89
140
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
141
|
+
/** The composition site's named storage root (J12: config —
|
|
142
|
+
* the retired env seam's twin). */
|
|
143
|
+
config;
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
async open() {
|
|
149
|
+
const root = resolveStorageRoot(this.config);
|
|
150
|
+
// OPTIONAL-dependency probe per the tutorial's documented pattern
|
|
151
|
+
// (cordis-tutorial/03-services.md: "skip inject and probe at the
|
|
152
|
+
// use site" — property reads demand inject on loader-mounted
|
|
153
|
+
// fibers; ctx.get returns the active provider or undefined).
|
|
154
|
+
const facility = this.ctx.get("storageDomain");
|
|
155
|
+
if (facility === undefined || facility === null) {
|
|
156
|
+
throw new Error("[estate-drafts] storage domain facility not mounted (ctx.storageDomain)");
|
|
157
|
+
}
|
|
158
|
+
const hit = this.cached.get(facility);
|
|
159
|
+
// (J12) the config root is immutable per boot, so a cached hit is
|
|
160
|
+
// always this root — the drifting-mid-process root the retired
|
|
161
|
+
|
|
162
|
+
if (hit !== undefined)
|
|
163
|
+
return hit.ready;
|
|
164
|
+
const ready = (async () => {
|
|
165
|
+
const drafts = await facility.open(draftsDomainSpec);
|
|
166
|
+
await importLegacyDrafts(drafts, root);
|
|
167
|
+
await healReplayedRefusals(drafts);
|
|
168
|
+
return drafts;
|
|
169
|
+
})();
|
|
170
|
+
this.cached.set(facility, { root, ready });
|
|
171
|
+
try {
|
|
172
|
+
return await ready;
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
// Unconditional: one slot per facility exists at a time (a
|
|
176
|
+
// foreign root refuses above), so this rejection owns the slot.
|
|
177
|
+
this.cached.delete(facility);
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
98
180
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
181
|
+
}
|
|
182
|
+
/** Open (or reuse) the drafts domain through the mounted seat. The
|
|
183
|
+
* apply mounts `DraftsDomainSeatService` before the remote
|
|
184
|
+
* services; tests mount it on their own Context.
|
|
185
|
+
* @param ctx - the Context whose fiber carries the seat.
|
|
186
|
+
* @returns the ready domain handle. */
|
|
187
|
+
export async function openDraftsDomain(ctx) {
|
|
188
|
+
const seat = ctx.get("meliteDraftsDomain");
|
|
189
|
+
if (seat === undefined || seat === null) {
|
|
190
|
+
throw new Error("[estate-drafts] drafts domain seat not mounted (new DraftsDomainSeatService(ctx))");
|
|
104
191
|
}
|
|
192
|
+
return seat.open();
|
|
105
193
|
}
|
|
106
194
|
//# sourceMappingURL=store.js.map
|
package/lib/trail.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function canonicalOutputUrl(url: string): string;
|
|
2
|
+
/** A result's OUTPUT urls, one spelling: the artifacts name→url map
|
|
3
|
+
* first, the flat assetUrls list second, the lone url third (the
|
|
4
|
+
* same harvest order the client's preview reads). */
|
|
5
|
+
export declare function resultOutputUrls(result: unknown): string[];
|
|
6
|
+
/** The draft row's terminal words (see remote.ts's trail laws). */
|
|
7
|
+
export declare function isTerminalTrailWord(v: unknown): boolean;
|
|
8
|
+
/** Same run identity: two non-empty equal run_id strings. */
|
|
9
|
+
export declare function sameTrailRunIdentity(a: unknown, b: unknown): boolean;
|
|
10
|
+
/** THE ONE-ROW-PER-RUN TRAIL (bench-trail W2's law, applied to any
|
|
11
|
+
* carried history): identity is the run_id first — the media route
|
|
12
|
+
* and the gallery proxy spell the same file differently — with the
|
|
13
|
+
* canonical-url set as the no-id fallback. Order preserved, newest
|
|
14
|
+
* first in, newest first out. */
|
|
15
|
+
export declare function trailDedupe(history: unknown[]): unknown[];
|
|
16
|
+
//# sourceMappingURL=trail.d.ts.map
|
package/lib/trail.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
|
|
2
|
+
// trail's dedup/canonical laws live here so EVERY carrier obeys them
|
|
3
|
+
// — the patch door's archive (remote.ts), the load/undo memory carry
|
|
4
|
+
// (bench.ts), and any future writer. remote.ts imports bench.ts, so
|
|
5
|
+
// bench-facing carriers can never import remote.ts back; this module
|
|
6
|
+
// is the cycle-free home both sides share.
|
|
7
|
+
import { RUN_MEDIA_PREFIX } from "@melite/contracts/benchStage";
|
|
8
|
+
/** One url, canonical: the run-relative path. The media route and
|
|
9
|
+
* the gallery proxy spell the SAME file two ways
|
|
10
|
+
* (/melite-media/runs/<rest>, /infer-gallery/artifact?path=<enc>) —
|
|
11
|
+
* every dedup compares canonical forms, never wire spellings. The
|
|
12
|
+
* run-media prefix rides the contract's constant — the closure
|
|
13
|
+
* gate's no-second-copy law. */
|
|
14
|
+
const MEDIA_RUNS = RUN_MEDIA_PREFIX + "/";
|
|
15
|
+
export function canonicalOutputUrl(url) {
|
|
16
|
+
if (url.startsWith(MEDIA_RUNS))
|
|
17
|
+
return url.slice(MEDIA_RUNS.length);
|
|
18
|
+
if (url.startsWith("/infer-gallery/artifact?path=")) {
|
|
19
|
+
try {
|
|
20
|
+
return decodeURIComponent(url.slice("/infer-gallery/artifact?path=".length));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// a malformed escape is not a spelling of another file — keep it
|
|
24
|
+
return url;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return url;
|
|
28
|
+
}
|
|
29
|
+
/** A result's OUTPUT urls, one spelling: the artifacts name→url map
|
|
30
|
+
* first, the flat assetUrls list second, the lone url third (the
|
|
31
|
+
* same harvest order the client's preview reads). */
|
|
32
|
+
export function resultOutputUrls(result) {
|
|
33
|
+
if (typeof result !== "object" || result === null || Array.isArray(result))
|
|
34
|
+
return [];
|
|
35
|
+
const row = result;
|
|
36
|
+
const urls = [];
|
|
37
|
+
if (row["artifacts"] !== null && typeof row["artifacts"] === "object" && !Array.isArray(row["artifacts"])) {
|
|
38
|
+
for (const v of Object.values(row["artifacts"])) {
|
|
39
|
+
if (typeof v === "string" && v !== "")
|
|
40
|
+
urls.push(v);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (urls.length === 0 && Array.isArray(row["assetUrls"])) {
|
|
44
|
+
for (const v of row["assetUrls"]) {
|
|
45
|
+
if (typeof v === "string" && v !== "")
|
|
46
|
+
urls.push(v);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (urls.length === 0 && typeof row["url"] === "string" && row["url"] !== "")
|
|
50
|
+
urls.push(row["url"]);
|
|
51
|
+
return urls;
|
|
52
|
+
}
|
|
53
|
+
const TRAIL_TERMINAL = new Set(["done", "completed", "failed", "stopped"]);
|
|
54
|
+
/** The draft row's terminal words (see remote.ts's trail laws). */
|
|
55
|
+
export function isTerminalTrailWord(v) {
|
|
56
|
+
return typeof v === "string" && TRAIL_TERMINAL.has(v);
|
|
57
|
+
}
|
|
58
|
+
/** Same run identity: two non-empty equal run_id strings. */
|
|
59
|
+
export function sameTrailRunIdentity(a, b) {
|
|
60
|
+
return typeof a === "string" && a !== "" && a === b;
|
|
61
|
+
}
|
|
62
|
+
/** THE ONE-ROW-PER-RUN TRAIL (bench-trail W2's law, applied to any
|
|
63
|
+
* carried history): identity is the run_id first — the media route
|
|
64
|
+
* and the gallery proxy spell the same file differently — with the
|
|
65
|
+
* canonical-url set as the no-id fallback. Order preserved, newest
|
|
66
|
+
* first in, newest first out. */
|
|
67
|
+
export function trailDedupe(history) {
|
|
68
|
+
const out = [];
|
|
69
|
+
const runIds = new Set();
|
|
70
|
+
const sets = [];
|
|
71
|
+
for (const entry of history) {
|
|
72
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
73
|
+
out.push(entry);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const row = entry;
|
|
77
|
+
const rid = row["run_id"];
|
|
78
|
+
if (typeof rid === "string" && rid !== "") {
|
|
79
|
+
if (runIds.has(rid))
|
|
80
|
+
continue;
|
|
81
|
+
runIds.add(rid);
|
|
82
|
+
}
|
|
83
|
+
const canon = new Set(resultOutputUrls(row).map(canonicalOutputUrl));
|
|
84
|
+
if (sets.some((s) => s.size === canon.size && [...s].every((u) => canon.has(u))))
|
|
85
|
+
continue;
|
|
86
|
+
sets.push(canon);
|
|
87
|
+
out.push(entry);
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=trail.js.map
|
package/lib/typert.host.js
CHANGED
|
@@ -249,7 +249,7 @@ export const TYPERT = {
|
|
|
249
249
|
typeSymbol: '@melite/estate-drafts/types#BenchStageRow',
|
|
250
250
|
create: _melite_estate_drafts_bench_add_stage_result$schema,
|
|
251
251
|
},
|
|
252
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
252
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1449,"column":9},
|
|
253
253
|
},
|
|
254
254
|
{
|
|
255
255
|
id: '@melite/estate-drafts#bench/clear',
|
|
@@ -286,7 +286,7 @@ export const TYPERT = {
|
|
|
286
286
|
typeSymbol: '@melite/estate-drafts/types#BenchClearRow',
|
|
287
287
|
create: _melite_estate_drafts_bench_clear_result$schema,
|
|
288
288
|
},
|
|
289
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
289
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1472,"column":9},
|
|
290
290
|
},
|
|
291
291
|
{
|
|
292
292
|
id: '@melite/estate-drafts#bench/ensure',
|
|
@@ -313,7 +313,7 @@ export const TYPERT = {
|
|
|
313
313
|
typeSymbol: '@melite/estate-drafts/types#BenchEnsureRow',
|
|
314
314
|
create: _melite_estate_drafts_bench_ensure_result$schema,
|
|
315
315
|
},
|
|
316
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
316
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1380,"column":9},
|
|
317
317
|
},
|
|
318
318
|
{
|
|
319
319
|
id: '@melite/estate-drafts#bench/get',
|
|
@@ -340,7 +340,7 @@ export const TYPERT = {
|
|
|
340
340
|
typeSymbol: '@melite/estate-drafts/types#BenchGetRow',
|
|
341
341
|
create: _melite_estate_drafts_bench_get_result$schema,
|
|
342
342
|
},
|
|
343
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
343
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1275,"column":9},
|
|
344
344
|
},
|
|
345
345
|
{
|
|
346
346
|
id: '@melite/estate-drafts#bench/group',
|
|
@@ -377,7 +377,7 @@ export const TYPERT = {
|
|
|
377
377
|
typeSymbol: '@melite/estate-drafts/types#BenchStageRow',
|
|
378
378
|
create: _melite_estate_drafts_bench_group_result$schema,
|
|
379
379
|
},
|
|
380
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
380
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1510,"column":9},
|
|
381
381
|
},
|
|
382
382
|
{
|
|
383
383
|
id: '@melite/estate-drafts#bench/patch',
|
|
@@ -414,7 +414,7 @@ export const TYPERT = {
|
|
|
414
414
|
typeSymbol: '@melite/estate-drafts/types#BenchPatchRow',
|
|
415
415
|
create: _melite_estate_drafts_bench_patch_result$schema,
|
|
416
416
|
},
|
|
417
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
417
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1439,"column":9},
|
|
418
418
|
},
|
|
419
419
|
{
|
|
420
420
|
id: '@melite/estate-drafts#bench/remove-stage',
|
|
@@ -451,7 +451,7 @@ export const TYPERT = {
|
|
|
451
451
|
typeSymbol: '@melite/estate-drafts/types#BenchStageRow',
|
|
452
452
|
create: _melite_estate_drafts_bench_remove_stage_result$schema,
|
|
453
453
|
},
|
|
454
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
454
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1459,"column":9},
|
|
455
455
|
},
|
|
456
456
|
{
|
|
457
457
|
id: '@melite/estate-drafts#bench/reorder',
|
|
@@ -488,7 +488,7 @@ export const TYPERT = {
|
|
|
488
488
|
typeSymbol: '@melite/estate-drafts/types#BenchReorderRow',
|
|
489
489
|
create: _melite_estate_drafts_bench_reorder_result$schema,
|
|
490
490
|
},
|
|
491
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
491
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1493,"column":9},
|
|
492
492
|
},
|
|
493
493
|
{
|
|
494
494
|
id: '@melite/estate-drafts#bench/rewire',
|
|
@@ -525,7 +525,7 @@ export const TYPERT = {
|
|
|
525
525
|
typeSymbol: '@melite/estate-drafts/types#BenchRewireRow',
|
|
526
526
|
create: _melite_estate_drafts_bench_rewire_result$schema,
|
|
527
527
|
},
|
|
528
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
528
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1482,"column":9},
|
|
529
529
|
},
|
|
530
530
|
{
|
|
531
531
|
id: '@melite/estate-drafts#bench/subscribe',
|
|
@@ -552,7 +552,7 @@ export const TYPERT = {
|
|
|
552
552
|
typeSymbol: '@melite/estate-drafts/types#BenchGetRow',
|
|
553
553
|
create: _melite_estate_drafts_bench_subscribe_result$schema,
|
|
554
554
|
},
|
|
555
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
555
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1293,"column":10},
|
|
556
556
|
},
|
|
557
557
|
{
|
|
558
558
|
id: '@melite/estate-drafts#bench/undo',
|
|
@@ -589,7 +589,7 @@ export const TYPERT = {
|
|
|
589
589
|
typeSymbol: '@melite/estate-drafts/types#BenchUndoRow',
|
|
590
590
|
create: _melite_estate_drafts_bench_undo_result$schema,
|
|
591
591
|
},
|
|
592
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
592
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1542,"column":9},
|
|
593
593
|
},
|
|
594
594
|
{
|
|
595
595
|
id: '@melite/estate-drafts#bench/ungroup',
|
|
@@ -626,7 +626,7 @@ export const TYPERT = {
|
|
|
626
626
|
typeSymbol: '@melite/estate-drafts/types#BenchStageRow',
|
|
627
627
|
create: _melite_estate_drafts_bench_ungroup_result$schema,
|
|
628
628
|
},
|
|
629
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
629
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":1526,"column":9},
|
|
630
630
|
},
|
|
631
631
|
{
|
|
632
632
|
id: '@melite/estate-drafts#drafts/get',
|
|
@@ -653,7 +653,7 @@ export const TYPERT = {
|
|
|
653
653
|
typeSymbol: '@melite/estate-drafts/types#DraftGetRow',
|
|
654
654
|
create: _melite_estate_drafts_drafts_get_result$schema,
|
|
655
655
|
},
|
|
656
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
656
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":599,"column":9},
|
|
657
657
|
},
|
|
658
658
|
{
|
|
659
659
|
id: '@melite/estate-drafts#drafts/list',
|
|
@@ -670,7 +670,7 @@ export const TYPERT = {
|
|
|
670
670
|
typeSymbol: '@melite/estate-drafts/types#DraftListResult',
|
|
671
671
|
create: _melite_estate_drafts_drafts_list_result$schema,
|
|
672
672
|
},
|
|
673
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
673
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":593,"column":9},
|
|
674
674
|
},
|
|
675
675
|
{
|
|
676
676
|
id: '@melite/estate-drafts#drafts/patch',
|
|
@@ -727,7 +727,7 @@ export const TYPERT = {
|
|
|
727
727
|
typeSymbol: '@melite/estate-drafts/types#DraftPatchRow',
|
|
728
728
|
create: _melite_estate_drafts_drafts_patch_result$schema,
|
|
729
729
|
},
|
|
730
|
-
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":
|
|
730
|
+
sourceLocation: {"file":"packages/estate/drafts/src/remote.ts","line":686,"column":9},
|
|
731
731
|
},
|
|
732
732
|
],
|
|
733
733
|
model: {
|
|
@@ -913,10 +913,7 @@ export const TYPERT = {
|
|
|
913
913
|
]
|
|
914
914
|
},
|
|
915
915
|
{
|
|
916
|
-
"description": "The drafts Remote owner: list/get over the storage domain.",
|
|
917
|
-
"summary": "The drafts Remote owner: list/get over the storage domain.",
|
|
918
916
|
"tags": [],
|
|
919
|
-
"jsDoc": "/** The drafts Remote owner: list/get over the storage domain. */",
|
|
920
917
|
"key": "draftsRemote",
|
|
921
918
|
"exportName": "DraftsRemoteService",
|
|
922
919
|
"members": [
|
|
@@ -959,8 +956,8 @@ export const TYPERT = {
|
|
|
959
956
|
"kind": "method",
|
|
960
957
|
"name": "patch",
|
|
961
958
|
"signature": "async patch( draftId: string, stageId: string, patch: Record<string, unknown>, expectRev: number | null, signal: AbortSignal, ): Promise<DraftPatchRow>",
|
|
962
|
-
"summary": "The patch arm the adapter above delegates to.",
|
|
963
|
-
"jsDoc": "/** The patch arm the adapter above delegates to. */"
|
|
959
|
+
"summary": "The patch arm the adapter above delegates to — one write transaction on the draft's own key, shared with the bench verbs (the round-14 race audit's R3: the author arm's spread-copy of the log forked against their in-place push).",
|
|
960
|
+
"jsDoc": "/** The patch arm the adapter above delegates to — one write\n * transaction on the draft's own key, shared with the bench\n * verbs (the round-14 race audit's R3: the author arm's\n * spread-copy of the log forked against their in-place push). */"
|
|
964
961
|
}
|
|
965
962
|
],
|
|
966
963
|
"types": [
|
|
@@ -996,6 +993,11 @@ export const TYPERT = {
|
|
|
996
993
|
"description": "A bench write landed. Emitted after every successful bench verb (and after the drafts-patch door's bench-shaped write).",
|
|
997
994
|
"summary": "A bench write landed.",
|
|
998
995
|
"tags": [
|
|
996
|
+
{
|
|
997
|
+
"name": "mode",
|
|
998
|
+
"comment": "emit",
|
|
999
|
+
"text": "@mode emit\n *"
|
|
1000
|
+
},
|
|
999
1001
|
{
|
|
1000
1002
|
"name": "param",
|
|
1001
1003
|
"argument": "session",
|
|
@@ -1003,8 +1005,9 @@ export const TYPERT = {
|
|
|
1003
1005
|
"text": "@param session - the bench session whose doc changed."
|
|
1004
1006
|
}
|
|
1005
1007
|
],
|
|
1006
|
-
"jsDoc": "/**\n * A bench write landed. Emitted after every successful bench verb\n * (and after the drafts-patch door's bench-shaped write).\n * @param session - the bench session whose doc changed.\n */",
|
|
1008
|
+
"jsDoc": "/**\n * A bench write landed. Emitted after every successful bench verb\n * (and after the drafts-patch door's bench-shaped write).\n * @mode emit\n * @param session - the bench session whose doc changed.\n */",
|
|
1007
1009
|
"name": "bench.changed",
|
|
1010
|
+
"mode": "emit",
|
|
1008
1011
|
"signature": "'bench.changed'(session: string): void"
|
|
1009
1012
|
}
|
|
1010
1013
|
],
|