mellos-mapping 0.19.0 → 0.20.2
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 +366 -56
- package/README.zh-CN.md +319 -47
- package/dist/hook-session-start.mjs +239 -0
- package/dist/mmap.mjs +338 -0
- package/dist/server.mjs +1737 -897
- package/dist/store-paths.mjs +107 -0
- package/dist/watch.mjs +1612 -902
- package/lib/domain/ops.d.ts +171 -0
- package/lib/domain/ops.js +384 -0
- package/lib/domain/types.d.ts +283 -0
- package/lib/domain/types.js +153 -0
- package/lib/render/canvas.d.ts +50 -0
- package/lib/render/canvas.js +210 -0
- package/lib/render/draw.d.ts +37 -0
- package/lib/render/draw.js +111 -0
- package/lib/render/layout.d.ts +89 -0
- package/lib/render/layout.js +200 -0
- package/lib/render/options.d.ts +39 -0
- package/lib/render/options.js +10 -0
- package/lib/render/render.d.ts +88 -0
- package/lib/render/render.js +128 -0
- package/lib/render/routing.d.ts +56 -0
- package/lib/render/routing.js +244 -0
- package/lib/render/skins.d.ts +54 -0
- package/lib/render/skins.js +99 -0
- package/lib/render/width.d.ts +24 -0
- package/lib/render/width.js +139 -0
- package/lib/render/zoom-geometry.d.ts +52 -0
- package/lib/render/zoom-geometry.js +56 -0
- package/lib/semantics/semantics.d.ts +169 -0
- package/lib/semantics/semantics.js +380 -0
- package/lib/semantics/vocabulary.d.ts +79 -0
- package/lib/semantics/vocabulary.js +112 -0
- package/lib/store/format.d.ts +67 -0
- package/lib/store/format.js +334 -0
- package/lib/store/store.d.ts +296 -0
- package/lib/store/store.js +734 -0
- package/package.json +41 -5
- package/scripts/codex-register.mjs +89 -20
- package/scripts/install-mmap-command.mjs +293 -0
- package/scripts/mmap.mjs +213 -0
- package/scripts/open-pane.mjs +115 -254
- package/scripts/pane-core.mjs +418 -0
package/dist/server.mjs
CHANGED
|
@@ -243,13 +243,13 @@ var require_scope = __commonJS({
|
|
|
243
243
|
}
|
|
244
244
|
};
|
|
245
245
|
exports.ValueScopeName = ValueScopeName;
|
|
246
|
-
var
|
|
246
|
+
var line2 = (0, code_1._)`\n`;
|
|
247
247
|
var ValueScope = class extends Scope {
|
|
248
248
|
constructor(opts) {
|
|
249
249
|
super(opts);
|
|
250
250
|
this._values = {};
|
|
251
251
|
this._scope = opts.scope;
|
|
252
|
-
this.opts = { ...opts, _n: opts.lines ?
|
|
252
|
+
this.opts = { ...opts, _n: opts.lines ? line2 : code_1.nil };
|
|
253
253
|
}
|
|
254
254
|
get() {
|
|
255
255
|
return this._scope;
|
|
@@ -2232,10 +2232,10 @@ var require_resolve = __commonJS({
|
|
|
2232
2232
|
}
|
|
2233
2233
|
return count;
|
|
2234
2234
|
}
|
|
2235
|
-
function getFullPath(resolver,
|
|
2235
|
+
function getFullPath(resolver, id2 = "", normalize) {
|
|
2236
2236
|
if (normalize !== false)
|
|
2237
|
-
|
|
2238
|
-
const p = resolver.parse(
|
|
2237
|
+
id2 = normalizeId(id2);
|
|
2238
|
+
const p = resolver.parse(id2);
|
|
2239
2239
|
return _getFullPath(resolver, p);
|
|
2240
2240
|
}
|
|
2241
2241
|
exports.getFullPath = getFullPath;
|
|
@@ -2245,13 +2245,13 @@ var require_resolve = __commonJS({
|
|
|
2245
2245
|
}
|
|
2246
2246
|
exports._getFullPath = _getFullPath;
|
|
2247
2247
|
var TRAILING_SLASH_HASH = /#\/?$/;
|
|
2248
|
-
function normalizeId(
|
|
2249
|
-
return
|
|
2248
|
+
function normalizeId(id2) {
|
|
2249
|
+
return id2 ? id2.replace(TRAILING_SLASH_HASH, "") : "";
|
|
2250
2250
|
}
|
|
2251
2251
|
exports.normalizeId = normalizeId;
|
|
2252
|
-
function resolveUrl(resolver, baseId,
|
|
2253
|
-
|
|
2254
|
-
return resolver.resolve(baseId,
|
|
2252
|
+
function resolveUrl(resolver, baseId, id2) {
|
|
2253
|
+
id2 = normalizeId(id2);
|
|
2254
|
+
return resolver.resolve(baseId, id2);
|
|
2255
2255
|
}
|
|
2256
2256
|
exports.resolveUrl = resolveUrl;
|
|
2257
2257
|
var ANCHOR = /^[a-z_][-a-z0-9._]*$/i;
|
|
@@ -3021,8 +3021,8 @@ var require_compile = __commonJS({
|
|
|
3021
3021
|
if (Object.keys(root.schema).length > 0 && refPath === baseId) {
|
|
3022
3022
|
return getJsonPointer.call(this, p, root);
|
|
3023
3023
|
}
|
|
3024
|
-
const
|
|
3025
|
-
const schOrRef = this.refs[
|
|
3024
|
+
const id2 = (0, resolve_1.normalizeId)(refPath);
|
|
3025
|
+
const schOrRef = this.refs[id2] || this.schemas[id2];
|
|
3026
3026
|
if (typeof schOrRef == "string") {
|
|
3027
3027
|
const sch = resolveSchema.call(this, root, schOrRef);
|
|
3028
3028
|
if (typeof (sch === null || sch === void 0 ? void 0 : sch.schema) !== "object")
|
|
@@ -3033,7 +3033,7 @@ var require_compile = __commonJS({
|
|
|
3033
3033
|
return;
|
|
3034
3034
|
if (!schOrRef.validate)
|
|
3035
3035
|
compileSchema.call(this, schOrRef);
|
|
3036
|
-
if (
|
|
3036
|
+
if (id2 === (0, resolve_1.normalizeId)(ref)) {
|
|
3037
3037
|
const { schema } = schOrRef;
|
|
3038
3038
|
const { schemaId } = this.opts;
|
|
3039
3039
|
const schId = schema[schemaId];
|
|
@@ -4176,15 +4176,15 @@ var require_core = __commonJS({
|
|
|
4176
4176
|
this.addSchema(sch, void 0, _meta, _validateSchema);
|
|
4177
4177
|
return this;
|
|
4178
4178
|
}
|
|
4179
|
-
let
|
|
4179
|
+
let id2;
|
|
4180
4180
|
if (typeof schema === "object") {
|
|
4181
4181
|
const { schemaId } = this.opts;
|
|
4182
|
-
|
|
4183
|
-
if (
|
|
4182
|
+
id2 = schema[schemaId];
|
|
4183
|
+
if (id2 !== void 0 && typeof id2 != "string") {
|
|
4184
4184
|
throw new Error(`schema ${schemaId} must be string`);
|
|
4185
4185
|
}
|
|
4186
4186
|
}
|
|
4187
|
-
key = (0, resolve_1.normalizeId)(key ||
|
|
4187
|
+
key = (0, resolve_1.normalizeId)(key || id2);
|
|
4188
4188
|
this._checkUnique(key);
|
|
4189
4189
|
this.schemas[key] = this._addSchema(schema, _meta, key, _validateSchema, true);
|
|
4190
4190
|
return this;
|
|
@@ -4263,11 +4263,11 @@ var require_core = __commonJS({
|
|
|
4263
4263
|
case "object": {
|
|
4264
4264
|
const cacheKey = schemaKeyRef;
|
|
4265
4265
|
this._cache.delete(cacheKey);
|
|
4266
|
-
let
|
|
4267
|
-
if (
|
|
4268
|
-
|
|
4269
|
-
delete this.schemas[
|
|
4270
|
-
delete this.refs[
|
|
4266
|
+
let id2 = schemaKeyRef[this.opts.schemaId];
|
|
4267
|
+
if (id2) {
|
|
4268
|
+
id2 = (0, resolve_1.normalizeId)(id2);
|
|
4269
|
+
delete this.schemas[id2];
|
|
4270
|
+
delete this.refs[id2];
|
|
4271
4271
|
}
|
|
4272
4272
|
return this;
|
|
4273
4273
|
}
|
|
@@ -4374,10 +4374,10 @@ var require_core = __commonJS({
|
|
|
4374
4374
|
}
|
|
4375
4375
|
}
|
|
4376
4376
|
_addSchema(schema, meta, baseId, validateSchema = this.opts.validateSchema, addSchema = this.opts.addUsedSchema) {
|
|
4377
|
-
let
|
|
4377
|
+
let id2;
|
|
4378
4378
|
const { schemaId } = this.opts;
|
|
4379
4379
|
if (typeof schema == "object") {
|
|
4380
|
-
|
|
4380
|
+
id2 = schema[schemaId];
|
|
4381
4381
|
} else {
|
|
4382
4382
|
if (this.opts.jtd)
|
|
4383
4383
|
throw new Error("schema must be object");
|
|
@@ -4387,7 +4387,7 @@ var require_core = __commonJS({
|
|
|
4387
4387
|
let sch = this._cache.get(schema);
|
|
4388
4388
|
if (sch !== void 0)
|
|
4389
4389
|
return sch;
|
|
4390
|
-
baseId = (0, resolve_1.normalizeId)(
|
|
4390
|
+
baseId = (0, resolve_1.normalizeId)(id2 || baseId);
|
|
4391
4391
|
const localRefs = resolve_1.getSchemaRefs.call(this, schema, baseId);
|
|
4392
4392
|
sch = new compile_1.SchemaEnv({ schema, schemaId, meta, baseId, localRefs });
|
|
4393
4393
|
this._cache.set(sch.schema, sch);
|
|
@@ -4400,9 +4400,9 @@ var require_core = __commonJS({
|
|
|
4400
4400
|
this.validateSchema(schema, true);
|
|
4401
4401
|
return sch;
|
|
4402
4402
|
}
|
|
4403
|
-
_checkUnique(
|
|
4404
|
-
if (this.schemas[
|
|
4405
|
-
throw new Error(`schema with key or id "${
|
|
4403
|
+
_checkUnique(id2) {
|
|
4404
|
+
if (this.schemas[id2] || this.refs[id2]) {
|
|
4405
|
+
throw new Error(`schema with key or id "${id2}" already exists`);
|
|
4406
4406
|
}
|
|
4407
4407
|
}
|
|
4408
4408
|
_compileSchemaEnv(sch) {
|
|
@@ -6913,8 +6913,10 @@ var require_dist = __commonJS({
|
|
|
6913
6913
|
});
|
|
6914
6914
|
|
|
6915
6915
|
// src/server/server.ts
|
|
6916
|
-
import {
|
|
6917
|
-
import {
|
|
6916
|
+
import { spawn } from "node:child_process";
|
|
6917
|
+
import { existsSync as existsSync2, realpathSync } from "node:fs";
|
|
6918
|
+
import { homedir } from "node:os";
|
|
6919
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
6918
6920
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6919
6921
|
|
|
6920
6922
|
// node_modules/zod/v3/external.js
|
|
@@ -7451,18 +7453,18 @@ var ParseStatus = class _ParseStatus {
|
|
|
7451
7453
|
if (this.value !== "aborted")
|
|
7452
7454
|
this.value = "aborted";
|
|
7453
7455
|
}
|
|
7454
|
-
static mergeArray(
|
|
7456
|
+
static mergeArray(status2, results) {
|
|
7455
7457
|
const arrayValue = [];
|
|
7456
7458
|
for (const s of results) {
|
|
7457
7459
|
if (s.status === "aborted")
|
|
7458
7460
|
return INVALID;
|
|
7459
7461
|
if (s.status === "dirty")
|
|
7460
|
-
|
|
7462
|
+
status2.dirty();
|
|
7461
7463
|
arrayValue.push(s.value);
|
|
7462
7464
|
}
|
|
7463
|
-
return { status:
|
|
7465
|
+
return { status: status2.value, value: arrayValue };
|
|
7464
7466
|
}
|
|
7465
|
-
static async mergeObjectAsync(
|
|
7467
|
+
static async mergeObjectAsync(status2, pairs) {
|
|
7466
7468
|
const syncPairs = [];
|
|
7467
7469
|
for (const pair of pairs) {
|
|
7468
7470
|
const key = await pair.key;
|
|
@@ -7472,9 +7474,9 @@ var ParseStatus = class _ParseStatus {
|
|
|
7472
7474
|
value
|
|
7473
7475
|
});
|
|
7474
7476
|
}
|
|
7475
|
-
return _ParseStatus.mergeObjectSync(
|
|
7477
|
+
return _ParseStatus.mergeObjectSync(status2, syncPairs);
|
|
7476
7478
|
}
|
|
7477
|
-
static mergeObjectSync(
|
|
7479
|
+
static mergeObjectSync(status2, pairs) {
|
|
7478
7480
|
const finalObject = {};
|
|
7479
7481
|
for (const pair of pairs) {
|
|
7480
7482
|
const { key, value } = pair;
|
|
@@ -7483,14 +7485,14 @@ var ParseStatus = class _ParseStatus {
|
|
|
7483
7485
|
if (value.status === "aborted")
|
|
7484
7486
|
return INVALID;
|
|
7485
7487
|
if (key.status === "dirty")
|
|
7486
|
-
|
|
7488
|
+
status2.dirty();
|
|
7487
7489
|
if (value.status === "dirty")
|
|
7488
|
-
|
|
7490
|
+
status2.dirty();
|
|
7489
7491
|
if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) {
|
|
7490
7492
|
finalObject[key.value] = value.value;
|
|
7491
7493
|
}
|
|
7492
7494
|
}
|
|
7493
|
-
return { status:
|
|
7495
|
+
return { status: status2.value, value: finalObject };
|
|
7494
7496
|
}
|
|
7495
7497
|
};
|
|
7496
7498
|
var INVALID = Object.freeze({
|
|
@@ -7950,7 +7952,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
7950
7952
|
});
|
|
7951
7953
|
return INVALID;
|
|
7952
7954
|
}
|
|
7953
|
-
const
|
|
7955
|
+
const status2 = new ParseStatus();
|
|
7954
7956
|
let ctx = void 0;
|
|
7955
7957
|
for (const check2 of this._def.checks) {
|
|
7956
7958
|
if (check2.kind === "min") {
|
|
@@ -7964,7 +7966,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
7964
7966
|
exact: false,
|
|
7965
7967
|
message: check2.message
|
|
7966
7968
|
});
|
|
7967
|
-
|
|
7969
|
+
status2.dirty();
|
|
7968
7970
|
}
|
|
7969
7971
|
} else if (check2.kind === "max") {
|
|
7970
7972
|
if (input.data.length > check2.value) {
|
|
@@ -7977,7 +7979,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
7977
7979
|
exact: false,
|
|
7978
7980
|
message: check2.message
|
|
7979
7981
|
});
|
|
7980
|
-
|
|
7982
|
+
status2.dirty();
|
|
7981
7983
|
}
|
|
7982
7984
|
} else if (check2.kind === "length") {
|
|
7983
7985
|
const tooBig = input.data.length > check2.value;
|
|
@@ -8003,7 +8005,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8003
8005
|
message: check2.message
|
|
8004
8006
|
});
|
|
8005
8007
|
}
|
|
8006
|
-
|
|
8008
|
+
status2.dirty();
|
|
8007
8009
|
}
|
|
8008
8010
|
} else if (check2.kind === "email") {
|
|
8009
8011
|
if (!emailRegex.test(input.data)) {
|
|
@@ -8013,7 +8015,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8013
8015
|
code: ZodIssueCode.invalid_string,
|
|
8014
8016
|
message: check2.message
|
|
8015
8017
|
});
|
|
8016
|
-
|
|
8018
|
+
status2.dirty();
|
|
8017
8019
|
}
|
|
8018
8020
|
} else if (check2.kind === "emoji") {
|
|
8019
8021
|
if (!emojiRegex) {
|
|
@@ -8026,7 +8028,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8026
8028
|
code: ZodIssueCode.invalid_string,
|
|
8027
8029
|
message: check2.message
|
|
8028
8030
|
});
|
|
8029
|
-
|
|
8031
|
+
status2.dirty();
|
|
8030
8032
|
}
|
|
8031
8033
|
} else if (check2.kind === "uuid") {
|
|
8032
8034
|
if (!uuidRegex.test(input.data)) {
|
|
@@ -8036,7 +8038,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8036
8038
|
code: ZodIssueCode.invalid_string,
|
|
8037
8039
|
message: check2.message
|
|
8038
8040
|
});
|
|
8039
|
-
|
|
8041
|
+
status2.dirty();
|
|
8040
8042
|
}
|
|
8041
8043
|
} else if (check2.kind === "nanoid") {
|
|
8042
8044
|
if (!nanoidRegex.test(input.data)) {
|
|
@@ -8046,7 +8048,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8046
8048
|
code: ZodIssueCode.invalid_string,
|
|
8047
8049
|
message: check2.message
|
|
8048
8050
|
});
|
|
8049
|
-
|
|
8051
|
+
status2.dirty();
|
|
8050
8052
|
}
|
|
8051
8053
|
} else if (check2.kind === "cuid") {
|
|
8052
8054
|
if (!cuidRegex.test(input.data)) {
|
|
@@ -8056,7 +8058,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8056
8058
|
code: ZodIssueCode.invalid_string,
|
|
8057
8059
|
message: check2.message
|
|
8058
8060
|
});
|
|
8059
|
-
|
|
8061
|
+
status2.dirty();
|
|
8060
8062
|
}
|
|
8061
8063
|
} else if (check2.kind === "cuid2") {
|
|
8062
8064
|
if (!cuid2Regex.test(input.data)) {
|
|
@@ -8066,7 +8068,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8066
8068
|
code: ZodIssueCode.invalid_string,
|
|
8067
8069
|
message: check2.message
|
|
8068
8070
|
});
|
|
8069
|
-
|
|
8071
|
+
status2.dirty();
|
|
8070
8072
|
}
|
|
8071
8073
|
} else if (check2.kind === "ulid") {
|
|
8072
8074
|
if (!ulidRegex.test(input.data)) {
|
|
@@ -8076,7 +8078,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8076
8078
|
code: ZodIssueCode.invalid_string,
|
|
8077
8079
|
message: check2.message
|
|
8078
8080
|
});
|
|
8079
|
-
|
|
8081
|
+
status2.dirty();
|
|
8080
8082
|
}
|
|
8081
8083
|
} else if (check2.kind === "url") {
|
|
8082
8084
|
try {
|
|
@@ -8088,7 +8090,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8088
8090
|
code: ZodIssueCode.invalid_string,
|
|
8089
8091
|
message: check2.message
|
|
8090
8092
|
});
|
|
8091
|
-
|
|
8093
|
+
status2.dirty();
|
|
8092
8094
|
}
|
|
8093
8095
|
} else if (check2.kind === "regex") {
|
|
8094
8096
|
check2.regex.lastIndex = 0;
|
|
@@ -8100,7 +8102,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8100
8102
|
code: ZodIssueCode.invalid_string,
|
|
8101
8103
|
message: check2.message
|
|
8102
8104
|
});
|
|
8103
|
-
|
|
8105
|
+
status2.dirty();
|
|
8104
8106
|
}
|
|
8105
8107
|
} else if (check2.kind === "trim") {
|
|
8106
8108
|
input.data = input.data.trim();
|
|
@@ -8112,7 +8114,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8112
8114
|
validation: { includes: check2.value, position: check2.position },
|
|
8113
8115
|
message: check2.message
|
|
8114
8116
|
});
|
|
8115
|
-
|
|
8117
|
+
status2.dirty();
|
|
8116
8118
|
}
|
|
8117
8119
|
} else if (check2.kind === "toLowerCase") {
|
|
8118
8120
|
input.data = input.data.toLowerCase();
|
|
@@ -8126,7 +8128,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8126
8128
|
validation: { startsWith: check2.value },
|
|
8127
8129
|
message: check2.message
|
|
8128
8130
|
});
|
|
8129
|
-
|
|
8131
|
+
status2.dirty();
|
|
8130
8132
|
}
|
|
8131
8133
|
} else if (check2.kind === "endsWith") {
|
|
8132
8134
|
if (!input.data.endsWith(check2.value)) {
|
|
@@ -8136,7 +8138,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8136
8138
|
validation: { endsWith: check2.value },
|
|
8137
8139
|
message: check2.message
|
|
8138
8140
|
});
|
|
8139
|
-
|
|
8141
|
+
status2.dirty();
|
|
8140
8142
|
}
|
|
8141
8143
|
} else if (check2.kind === "datetime") {
|
|
8142
8144
|
const regex = datetimeRegex(check2);
|
|
@@ -8147,7 +8149,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8147
8149
|
validation: "datetime",
|
|
8148
8150
|
message: check2.message
|
|
8149
8151
|
});
|
|
8150
|
-
|
|
8152
|
+
status2.dirty();
|
|
8151
8153
|
}
|
|
8152
8154
|
} else if (check2.kind === "date") {
|
|
8153
8155
|
const regex = dateRegex;
|
|
@@ -8158,7 +8160,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8158
8160
|
validation: "date",
|
|
8159
8161
|
message: check2.message
|
|
8160
8162
|
});
|
|
8161
|
-
|
|
8163
|
+
status2.dirty();
|
|
8162
8164
|
}
|
|
8163
8165
|
} else if (check2.kind === "time") {
|
|
8164
8166
|
const regex = timeRegex(check2);
|
|
@@ -8169,7 +8171,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8169
8171
|
validation: "time",
|
|
8170
8172
|
message: check2.message
|
|
8171
8173
|
});
|
|
8172
|
-
|
|
8174
|
+
status2.dirty();
|
|
8173
8175
|
}
|
|
8174
8176
|
} else if (check2.kind === "duration") {
|
|
8175
8177
|
if (!durationRegex.test(input.data)) {
|
|
@@ -8179,7 +8181,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8179
8181
|
code: ZodIssueCode.invalid_string,
|
|
8180
8182
|
message: check2.message
|
|
8181
8183
|
});
|
|
8182
|
-
|
|
8184
|
+
status2.dirty();
|
|
8183
8185
|
}
|
|
8184
8186
|
} else if (check2.kind === "ip") {
|
|
8185
8187
|
if (!isValidIP(input.data, check2.version)) {
|
|
@@ -8189,7 +8191,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8189
8191
|
code: ZodIssueCode.invalid_string,
|
|
8190
8192
|
message: check2.message
|
|
8191
8193
|
});
|
|
8192
|
-
|
|
8194
|
+
status2.dirty();
|
|
8193
8195
|
}
|
|
8194
8196
|
} else if (check2.kind === "jwt") {
|
|
8195
8197
|
if (!isValidJWT(input.data, check2.alg)) {
|
|
@@ -8199,7 +8201,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8199
8201
|
code: ZodIssueCode.invalid_string,
|
|
8200
8202
|
message: check2.message
|
|
8201
8203
|
});
|
|
8202
|
-
|
|
8204
|
+
status2.dirty();
|
|
8203
8205
|
}
|
|
8204
8206
|
} else if (check2.kind === "cidr") {
|
|
8205
8207
|
if (!isValidCidr(input.data, check2.version)) {
|
|
@@ -8209,7 +8211,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8209
8211
|
code: ZodIssueCode.invalid_string,
|
|
8210
8212
|
message: check2.message
|
|
8211
8213
|
});
|
|
8212
|
-
|
|
8214
|
+
status2.dirty();
|
|
8213
8215
|
}
|
|
8214
8216
|
} else if (check2.kind === "base64") {
|
|
8215
8217
|
if (!base64Regex.test(input.data)) {
|
|
@@ -8219,7 +8221,7 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8219
8221
|
code: ZodIssueCode.invalid_string,
|
|
8220
8222
|
message: check2.message
|
|
8221
8223
|
});
|
|
8222
|
-
|
|
8224
|
+
status2.dirty();
|
|
8223
8225
|
}
|
|
8224
8226
|
} else if (check2.kind === "base64url") {
|
|
8225
8227
|
if (!base64urlRegex.test(input.data)) {
|
|
@@ -8229,13 +8231,13 @@ var ZodString = class _ZodString2 extends ZodType {
|
|
|
8229
8231
|
code: ZodIssueCode.invalid_string,
|
|
8230
8232
|
message: check2.message
|
|
8231
8233
|
});
|
|
8232
|
-
|
|
8234
|
+
status2.dirty();
|
|
8233
8235
|
}
|
|
8234
8236
|
} else {
|
|
8235
8237
|
util.assertNever(check2);
|
|
8236
8238
|
}
|
|
8237
8239
|
}
|
|
8238
|
-
return { status:
|
|
8240
|
+
return { status: status2.value, value: input.data };
|
|
8239
8241
|
}
|
|
8240
8242
|
_regex(regex, validation, message) {
|
|
8241
8243
|
return this.refinement((data) => regex.test(data), {
|
|
@@ -8511,7 +8513,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
|
|
|
8511
8513
|
return INVALID;
|
|
8512
8514
|
}
|
|
8513
8515
|
let ctx = void 0;
|
|
8514
|
-
const
|
|
8516
|
+
const status2 = new ParseStatus();
|
|
8515
8517
|
for (const check2 of this._def.checks) {
|
|
8516
8518
|
if (check2.kind === "int") {
|
|
8517
8519
|
if (!util.isInteger(input.data)) {
|
|
@@ -8522,7 +8524,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
|
|
|
8522
8524
|
received: "float",
|
|
8523
8525
|
message: check2.message
|
|
8524
8526
|
});
|
|
8525
|
-
|
|
8527
|
+
status2.dirty();
|
|
8526
8528
|
}
|
|
8527
8529
|
} else if (check2.kind === "min") {
|
|
8528
8530
|
const tooSmall = check2.inclusive ? input.data < check2.value : input.data <= check2.value;
|
|
@@ -8536,7 +8538,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
|
|
|
8536
8538
|
exact: false,
|
|
8537
8539
|
message: check2.message
|
|
8538
8540
|
});
|
|
8539
|
-
|
|
8541
|
+
status2.dirty();
|
|
8540
8542
|
}
|
|
8541
8543
|
} else if (check2.kind === "max") {
|
|
8542
8544
|
const tooBig = check2.inclusive ? input.data > check2.value : input.data >= check2.value;
|
|
@@ -8550,7 +8552,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
|
|
|
8550
8552
|
exact: false,
|
|
8551
8553
|
message: check2.message
|
|
8552
8554
|
});
|
|
8553
|
-
|
|
8555
|
+
status2.dirty();
|
|
8554
8556
|
}
|
|
8555
8557
|
} else if (check2.kind === "multipleOf") {
|
|
8556
8558
|
if (floatSafeRemainder(input.data, check2.value) !== 0) {
|
|
@@ -8560,7 +8562,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
|
|
|
8560
8562
|
multipleOf: check2.value,
|
|
8561
8563
|
message: check2.message
|
|
8562
8564
|
});
|
|
8563
|
-
|
|
8565
|
+
status2.dirty();
|
|
8564
8566
|
}
|
|
8565
8567
|
} else if (check2.kind === "finite") {
|
|
8566
8568
|
if (!Number.isFinite(input.data)) {
|
|
@@ -8569,13 +8571,13 @@ var ZodNumber = class _ZodNumber extends ZodType {
|
|
|
8569
8571
|
code: ZodIssueCode.not_finite,
|
|
8570
8572
|
message: check2.message
|
|
8571
8573
|
});
|
|
8572
|
-
|
|
8574
|
+
status2.dirty();
|
|
8573
8575
|
}
|
|
8574
8576
|
} else {
|
|
8575
8577
|
util.assertNever(check2);
|
|
8576
8578
|
}
|
|
8577
8579
|
}
|
|
8578
|
-
return { status:
|
|
8580
|
+
return { status: status2.value, value: input.data };
|
|
8579
8581
|
}
|
|
8580
8582
|
gte(value, message) {
|
|
8581
8583
|
return this.setLimit("min", value, true, errorUtil.toString(message));
|
|
@@ -8740,7 +8742,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
|
|
|
8740
8742
|
return this._getInvalidInput(input);
|
|
8741
8743
|
}
|
|
8742
8744
|
let ctx = void 0;
|
|
8743
|
-
const
|
|
8745
|
+
const status2 = new ParseStatus();
|
|
8744
8746
|
for (const check2 of this._def.checks) {
|
|
8745
8747
|
if (check2.kind === "min") {
|
|
8746
8748
|
const tooSmall = check2.inclusive ? input.data < check2.value : input.data <= check2.value;
|
|
@@ -8753,7 +8755,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
|
|
|
8753
8755
|
inclusive: check2.inclusive,
|
|
8754
8756
|
message: check2.message
|
|
8755
8757
|
});
|
|
8756
|
-
|
|
8758
|
+
status2.dirty();
|
|
8757
8759
|
}
|
|
8758
8760
|
} else if (check2.kind === "max") {
|
|
8759
8761
|
const tooBig = check2.inclusive ? input.data > check2.value : input.data >= check2.value;
|
|
@@ -8766,7 +8768,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
|
|
|
8766
8768
|
inclusive: check2.inclusive,
|
|
8767
8769
|
message: check2.message
|
|
8768
8770
|
});
|
|
8769
|
-
|
|
8771
|
+
status2.dirty();
|
|
8770
8772
|
}
|
|
8771
8773
|
} else if (check2.kind === "multipleOf") {
|
|
8772
8774
|
if (input.data % check2.value !== BigInt(0)) {
|
|
@@ -8776,13 +8778,13 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
|
|
|
8776
8778
|
multipleOf: check2.value,
|
|
8777
8779
|
message: check2.message
|
|
8778
8780
|
});
|
|
8779
|
-
|
|
8781
|
+
status2.dirty();
|
|
8780
8782
|
}
|
|
8781
8783
|
} else {
|
|
8782
8784
|
util.assertNever(check2);
|
|
8783
8785
|
}
|
|
8784
8786
|
}
|
|
8785
|
-
return { status:
|
|
8787
|
+
return { status: status2.value, value: input.data };
|
|
8786
8788
|
}
|
|
8787
8789
|
_getInvalidInput(input) {
|
|
8788
8790
|
const ctx = this._getOrReturnCtx(input);
|
|
@@ -8940,7 +8942,7 @@ var ZodDate = class _ZodDate extends ZodType {
|
|
|
8940
8942
|
});
|
|
8941
8943
|
return INVALID;
|
|
8942
8944
|
}
|
|
8943
|
-
const
|
|
8945
|
+
const status2 = new ParseStatus();
|
|
8944
8946
|
let ctx = void 0;
|
|
8945
8947
|
for (const check2 of this._def.checks) {
|
|
8946
8948
|
if (check2.kind === "min") {
|
|
@@ -8954,7 +8956,7 @@ var ZodDate = class _ZodDate extends ZodType {
|
|
|
8954
8956
|
minimum: check2.value,
|
|
8955
8957
|
type: "date"
|
|
8956
8958
|
});
|
|
8957
|
-
|
|
8959
|
+
status2.dirty();
|
|
8958
8960
|
}
|
|
8959
8961
|
} else if (check2.kind === "max") {
|
|
8960
8962
|
if (input.data.getTime() > check2.value) {
|
|
@@ -8967,14 +8969,14 @@ var ZodDate = class _ZodDate extends ZodType {
|
|
|
8967
8969
|
maximum: check2.value,
|
|
8968
8970
|
type: "date"
|
|
8969
8971
|
});
|
|
8970
|
-
|
|
8972
|
+
status2.dirty();
|
|
8971
8973
|
}
|
|
8972
8974
|
} else {
|
|
8973
8975
|
util.assertNever(check2);
|
|
8974
8976
|
}
|
|
8975
8977
|
}
|
|
8976
8978
|
return {
|
|
8977
|
-
status:
|
|
8979
|
+
status: status2.value,
|
|
8978
8980
|
value: new Date(input.data.getTime())
|
|
8979
8981
|
};
|
|
8980
8982
|
}
|
|
@@ -9160,7 +9162,7 @@ ZodVoid.create = (params) => {
|
|
|
9160
9162
|
};
|
|
9161
9163
|
var ZodArray = class _ZodArray extends ZodType {
|
|
9162
9164
|
_parse(input) {
|
|
9163
|
-
const { ctx, status } = this._processInputParams(input);
|
|
9165
|
+
const { ctx, status: status2 } = this._processInputParams(input);
|
|
9164
9166
|
const def = this._def;
|
|
9165
9167
|
if (ctx.parsedType !== ZodParsedType.array) {
|
|
9166
9168
|
addIssueToContext(ctx, {
|
|
@@ -9183,7 +9185,7 @@ var ZodArray = class _ZodArray extends ZodType {
|
|
|
9183
9185
|
exact: true,
|
|
9184
9186
|
message: def.exactLength.message
|
|
9185
9187
|
});
|
|
9186
|
-
|
|
9188
|
+
status2.dirty();
|
|
9187
9189
|
}
|
|
9188
9190
|
}
|
|
9189
9191
|
if (def.minLength !== null) {
|
|
@@ -9196,7 +9198,7 @@ var ZodArray = class _ZodArray extends ZodType {
|
|
|
9196
9198
|
exact: false,
|
|
9197
9199
|
message: def.minLength.message
|
|
9198
9200
|
});
|
|
9199
|
-
|
|
9201
|
+
status2.dirty();
|
|
9200
9202
|
}
|
|
9201
9203
|
}
|
|
9202
9204
|
if (def.maxLength !== null) {
|
|
@@ -9209,20 +9211,20 @@ var ZodArray = class _ZodArray extends ZodType {
|
|
|
9209
9211
|
exact: false,
|
|
9210
9212
|
message: def.maxLength.message
|
|
9211
9213
|
});
|
|
9212
|
-
|
|
9214
|
+
status2.dirty();
|
|
9213
9215
|
}
|
|
9214
9216
|
}
|
|
9215
9217
|
if (ctx.common.async) {
|
|
9216
9218
|
return Promise.all([...ctx.data].map((item, i) => {
|
|
9217
9219
|
return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i));
|
|
9218
9220
|
})).then((result2) => {
|
|
9219
|
-
return ParseStatus.mergeArray(
|
|
9221
|
+
return ParseStatus.mergeArray(status2, result2);
|
|
9220
9222
|
});
|
|
9221
9223
|
}
|
|
9222
9224
|
const result = [...ctx.data].map((item, i) => {
|
|
9223
9225
|
return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i));
|
|
9224
9226
|
});
|
|
9225
|
-
return ParseStatus.mergeArray(
|
|
9227
|
+
return ParseStatus.mergeArray(status2, result);
|
|
9226
9228
|
}
|
|
9227
9229
|
get element() {
|
|
9228
9230
|
return this._def.type;
|
|
@@ -9311,7 +9313,7 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
9311
9313
|
});
|
|
9312
9314
|
return INVALID;
|
|
9313
9315
|
}
|
|
9314
|
-
const { status, ctx } = this._processInputParams(input);
|
|
9316
|
+
const { status: status2, ctx } = this._processInputParams(input);
|
|
9315
9317
|
const { shape, keys: shapeKeys } = this._getCached();
|
|
9316
9318
|
const extraKeys = [];
|
|
9317
9319
|
if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) {
|
|
@@ -9346,7 +9348,7 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
9346
9348
|
code: ZodIssueCode.unrecognized_keys,
|
|
9347
9349
|
keys: extraKeys
|
|
9348
9350
|
});
|
|
9349
|
-
|
|
9351
|
+
status2.dirty();
|
|
9350
9352
|
}
|
|
9351
9353
|
} else if (unknownKeys === "strip") {
|
|
9352
9354
|
} else {
|
|
@@ -9380,10 +9382,10 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
9380
9382
|
}
|
|
9381
9383
|
return syncPairs;
|
|
9382
9384
|
}).then((syncPairs) => {
|
|
9383
|
-
return ParseStatus.mergeObjectSync(
|
|
9385
|
+
return ParseStatus.mergeObjectSync(status2, syncPairs);
|
|
9384
9386
|
});
|
|
9385
9387
|
} else {
|
|
9386
|
-
return ParseStatus.mergeObjectSync(
|
|
9388
|
+
return ParseStatus.mergeObjectSync(status2, pairs);
|
|
9387
9389
|
}
|
|
9388
9390
|
}
|
|
9389
9391
|
get shape() {
|
|
@@ -9861,7 +9863,7 @@ function mergeValues(a, b) {
|
|
|
9861
9863
|
}
|
|
9862
9864
|
var ZodIntersection = class extends ZodType {
|
|
9863
9865
|
_parse(input) {
|
|
9864
|
-
const { status, ctx } = this._processInputParams(input);
|
|
9866
|
+
const { status: status2, ctx } = this._processInputParams(input);
|
|
9865
9867
|
const handleParsed = (parsedLeft, parsedRight) => {
|
|
9866
9868
|
if (isAborted(parsedLeft) || isAborted(parsedRight)) {
|
|
9867
9869
|
return INVALID;
|
|
@@ -9874,9 +9876,9 @@ var ZodIntersection = class extends ZodType {
|
|
|
9874
9876
|
return INVALID;
|
|
9875
9877
|
}
|
|
9876
9878
|
if (isDirty(parsedLeft) || isDirty(parsedRight)) {
|
|
9877
|
-
|
|
9879
|
+
status2.dirty();
|
|
9878
9880
|
}
|
|
9879
|
-
return { status:
|
|
9881
|
+
return { status: status2.value, value: merged.data };
|
|
9880
9882
|
};
|
|
9881
9883
|
if (ctx.common.async) {
|
|
9882
9884
|
return Promise.all([
|
|
@@ -9914,7 +9916,7 @@ ZodIntersection.create = (left, right, params) => {
|
|
|
9914
9916
|
};
|
|
9915
9917
|
var ZodTuple = class _ZodTuple extends ZodType {
|
|
9916
9918
|
_parse(input) {
|
|
9917
|
-
const { status, ctx } = this._processInputParams(input);
|
|
9919
|
+
const { status: status2, ctx } = this._processInputParams(input);
|
|
9918
9920
|
if (ctx.parsedType !== ZodParsedType.array) {
|
|
9919
9921
|
addIssueToContext(ctx, {
|
|
9920
9922
|
code: ZodIssueCode.invalid_type,
|
|
@@ -9942,7 +9944,7 @@ var ZodTuple = class _ZodTuple extends ZodType {
|
|
|
9942
9944
|
exact: false,
|
|
9943
9945
|
type: "array"
|
|
9944
9946
|
});
|
|
9945
|
-
|
|
9947
|
+
status2.dirty();
|
|
9946
9948
|
}
|
|
9947
9949
|
const items = [...ctx.data].map((item, itemIndex) => {
|
|
9948
9950
|
const schema = this._def.items[itemIndex] || this._def.rest;
|
|
@@ -9952,10 +9954,10 @@ var ZodTuple = class _ZodTuple extends ZodType {
|
|
|
9952
9954
|
}).filter((x) => !!x);
|
|
9953
9955
|
if (ctx.common.async) {
|
|
9954
9956
|
return Promise.all(items).then((results) => {
|
|
9955
|
-
return ParseStatus.mergeArray(
|
|
9957
|
+
return ParseStatus.mergeArray(status2, results);
|
|
9956
9958
|
});
|
|
9957
9959
|
} else {
|
|
9958
|
-
return ParseStatus.mergeArray(
|
|
9960
|
+
return ParseStatus.mergeArray(status2, items);
|
|
9959
9961
|
}
|
|
9960
9962
|
}
|
|
9961
9963
|
get items() {
|
|
@@ -9987,7 +9989,7 @@ var ZodRecord = class _ZodRecord extends ZodType {
|
|
|
9987
9989
|
return this._def.valueType;
|
|
9988
9990
|
}
|
|
9989
9991
|
_parse(input) {
|
|
9990
|
-
const { status, ctx } = this._processInputParams(input);
|
|
9992
|
+
const { status: status2, ctx } = this._processInputParams(input);
|
|
9991
9993
|
if (ctx.parsedType !== ZodParsedType.object) {
|
|
9992
9994
|
addIssueToContext(ctx, {
|
|
9993
9995
|
code: ZodIssueCode.invalid_type,
|
|
@@ -10007,9 +10009,9 @@ var ZodRecord = class _ZodRecord extends ZodType {
|
|
|
10007
10009
|
});
|
|
10008
10010
|
}
|
|
10009
10011
|
if (ctx.common.async) {
|
|
10010
|
-
return ParseStatus.mergeObjectAsync(
|
|
10012
|
+
return ParseStatus.mergeObjectAsync(status2, pairs);
|
|
10011
10013
|
} else {
|
|
10012
|
-
return ParseStatus.mergeObjectSync(
|
|
10014
|
+
return ParseStatus.mergeObjectSync(status2, pairs);
|
|
10013
10015
|
}
|
|
10014
10016
|
}
|
|
10015
10017
|
get element() {
|
|
@@ -10040,7 +10042,7 @@ var ZodMap = class extends ZodType {
|
|
|
10040
10042
|
return this._def.valueType;
|
|
10041
10043
|
}
|
|
10042
10044
|
_parse(input) {
|
|
10043
|
-
const { status, ctx } = this._processInputParams(input);
|
|
10045
|
+
const { status: status2, ctx } = this._processInputParams(input);
|
|
10044
10046
|
if (ctx.parsedType !== ZodParsedType.map) {
|
|
10045
10047
|
addIssueToContext(ctx, {
|
|
10046
10048
|
code: ZodIssueCode.invalid_type,
|
|
@@ -10067,11 +10069,11 @@ var ZodMap = class extends ZodType {
|
|
|
10067
10069
|
return INVALID;
|
|
10068
10070
|
}
|
|
10069
10071
|
if (key.status === "dirty" || value.status === "dirty") {
|
|
10070
|
-
|
|
10072
|
+
status2.dirty();
|
|
10071
10073
|
}
|
|
10072
10074
|
finalMap.set(key.value, value.value);
|
|
10073
10075
|
}
|
|
10074
|
-
return { status:
|
|
10076
|
+
return { status: status2.value, value: finalMap };
|
|
10075
10077
|
});
|
|
10076
10078
|
} else {
|
|
10077
10079
|
const finalMap = /* @__PURE__ */ new Map();
|
|
@@ -10082,11 +10084,11 @@ var ZodMap = class extends ZodType {
|
|
|
10082
10084
|
return INVALID;
|
|
10083
10085
|
}
|
|
10084
10086
|
if (key.status === "dirty" || value.status === "dirty") {
|
|
10085
|
-
|
|
10087
|
+
status2.dirty();
|
|
10086
10088
|
}
|
|
10087
10089
|
finalMap.set(key.value, value.value);
|
|
10088
10090
|
}
|
|
10089
|
-
return { status:
|
|
10091
|
+
return { status: status2.value, value: finalMap };
|
|
10090
10092
|
}
|
|
10091
10093
|
}
|
|
10092
10094
|
};
|
|
@@ -10100,7 +10102,7 @@ ZodMap.create = (keyType, valueType, params) => {
|
|
|
10100
10102
|
};
|
|
10101
10103
|
var ZodSet = class _ZodSet extends ZodType {
|
|
10102
10104
|
_parse(input) {
|
|
10103
|
-
const { status, ctx } = this._processInputParams(input);
|
|
10105
|
+
const { status: status2, ctx } = this._processInputParams(input);
|
|
10104
10106
|
if (ctx.parsedType !== ZodParsedType.set) {
|
|
10105
10107
|
addIssueToContext(ctx, {
|
|
10106
10108
|
code: ZodIssueCode.invalid_type,
|
|
@@ -10120,7 +10122,7 @@ var ZodSet = class _ZodSet extends ZodType {
|
|
|
10120
10122
|
exact: false,
|
|
10121
10123
|
message: def.minSize.message
|
|
10122
10124
|
});
|
|
10123
|
-
|
|
10125
|
+
status2.dirty();
|
|
10124
10126
|
}
|
|
10125
10127
|
}
|
|
10126
10128
|
if (def.maxSize !== null) {
|
|
@@ -10133,7 +10135,7 @@ var ZodSet = class _ZodSet extends ZodType {
|
|
|
10133
10135
|
exact: false,
|
|
10134
10136
|
message: def.maxSize.message
|
|
10135
10137
|
});
|
|
10136
|
-
|
|
10138
|
+
status2.dirty();
|
|
10137
10139
|
}
|
|
10138
10140
|
}
|
|
10139
10141
|
const valueType = this._def.valueType;
|
|
@@ -10143,10 +10145,10 @@ var ZodSet = class _ZodSet extends ZodType {
|
|
|
10143
10145
|
if (element.status === "aborted")
|
|
10144
10146
|
return INVALID;
|
|
10145
10147
|
if (element.status === "dirty")
|
|
10146
|
-
|
|
10148
|
+
status2.dirty();
|
|
10147
10149
|
parsedSet.add(element.value);
|
|
10148
10150
|
}
|
|
10149
|
-
return { status:
|
|
10151
|
+
return { status: status2.value, value: parsedSet };
|
|
10150
10152
|
}
|
|
10151
10153
|
const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i)));
|
|
10152
10154
|
if (ctx.common.async) {
|
|
@@ -10477,15 +10479,15 @@ var ZodEffects = class extends ZodType {
|
|
|
10477
10479
|
return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema;
|
|
10478
10480
|
}
|
|
10479
10481
|
_parse(input) {
|
|
10480
|
-
const { status, ctx } = this._processInputParams(input);
|
|
10482
|
+
const { status: status2, ctx } = this._processInputParams(input);
|
|
10481
10483
|
const effect = this._def.effect || null;
|
|
10482
10484
|
const checkCtx = {
|
|
10483
10485
|
addIssue: (arg) => {
|
|
10484
10486
|
addIssueToContext(ctx, arg);
|
|
10485
10487
|
if (arg.fatal) {
|
|
10486
|
-
|
|
10488
|
+
status2.abort();
|
|
10487
10489
|
} else {
|
|
10488
|
-
|
|
10490
|
+
status2.dirty();
|
|
10489
10491
|
}
|
|
10490
10492
|
},
|
|
10491
10493
|
get path() {
|
|
@@ -10497,7 +10499,7 @@ var ZodEffects = class extends ZodType {
|
|
|
10497
10499
|
const processed = effect.transform(ctx.data, checkCtx);
|
|
10498
10500
|
if (ctx.common.async) {
|
|
10499
10501
|
return Promise.resolve(processed).then(async (processed2) => {
|
|
10500
|
-
if (
|
|
10502
|
+
if (status2.value === "aborted")
|
|
10501
10503
|
return INVALID;
|
|
10502
10504
|
const result = await this._def.schema._parseAsync({
|
|
10503
10505
|
data: processed2,
|
|
@@ -10508,12 +10510,12 @@ var ZodEffects = class extends ZodType {
|
|
|
10508
10510
|
return INVALID;
|
|
10509
10511
|
if (result.status === "dirty")
|
|
10510
10512
|
return DIRTY(result.value);
|
|
10511
|
-
if (
|
|
10513
|
+
if (status2.value === "dirty")
|
|
10512
10514
|
return DIRTY(result.value);
|
|
10513
10515
|
return result;
|
|
10514
10516
|
});
|
|
10515
10517
|
} else {
|
|
10516
|
-
if (
|
|
10518
|
+
if (status2.value === "aborted")
|
|
10517
10519
|
return INVALID;
|
|
10518
10520
|
const result = this._def.schema._parseSync({
|
|
10519
10521
|
data: processed,
|
|
@@ -10524,7 +10526,7 @@ var ZodEffects = class extends ZodType {
|
|
|
10524
10526
|
return INVALID;
|
|
10525
10527
|
if (result.status === "dirty")
|
|
10526
10528
|
return DIRTY(result.value);
|
|
10527
|
-
if (
|
|
10529
|
+
if (status2.value === "dirty")
|
|
10528
10530
|
return DIRTY(result.value);
|
|
10529
10531
|
return result;
|
|
10530
10532
|
}
|
|
@@ -10549,17 +10551,17 @@ var ZodEffects = class extends ZodType {
|
|
|
10549
10551
|
if (inner.status === "aborted")
|
|
10550
10552
|
return INVALID;
|
|
10551
10553
|
if (inner.status === "dirty")
|
|
10552
|
-
|
|
10554
|
+
status2.dirty();
|
|
10553
10555
|
executeRefinement(inner.value);
|
|
10554
|
-
return { status:
|
|
10556
|
+
return { status: status2.value, value: inner.value };
|
|
10555
10557
|
} else {
|
|
10556
10558
|
return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => {
|
|
10557
10559
|
if (inner.status === "aborted")
|
|
10558
10560
|
return INVALID;
|
|
10559
10561
|
if (inner.status === "dirty")
|
|
10560
|
-
|
|
10562
|
+
status2.dirty();
|
|
10561
10563
|
return executeRefinement(inner.value).then(() => {
|
|
10562
|
-
return { status:
|
|
10564
|
+
return { status: status2.value, value: inner.value };
|
|
10563
10565
|
});
|
|
10564
10566
|
});
|
|
10565
10567
|
}
|
|
@@ -10577,13 +10579,13 @@ var ZodEffects = class extends ZodType {
|
|
|
10577
10579
|
if (result instanceof Promise) {
|
|
10578
10580
|
throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
|
|
10579
10581
|
}
|
|
10580
|
-
return { status:
|
|
10582
|
+
return { status: status2.value, value: result };
|
|
10581
10583
|
} else {
|
|
10582
10584
|
return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => {
|
|
10583
10585
|
if (!isValid(base))
|
|
10584
10586
|
return INVALID;
|
|
10585
10587
|
return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({
|
|
10586
|
-
status:
|
|
10588
|
+
status: status2.value,
|
|
10587
10589
|
value: result
|
|
10588
10590
|
}));
|
|
10589
10591
|
});
|
|
@@ -10762,7 +10764,7 @@ var ZodBranded = class extends ZodType {
|
|
|
10762
10764
|
};
|
|
10763
10765
|
var ZodPipeline = class _ZodPipeline extends ZodType {
|
|
10764
10766
|
_parse(input) {
|
|
10765
|
-
const { status, ctx } = this._processInputParams(input);
|
|
10767
|
+
const { status: status2, ctx } = this._processInputParams(input);
|
|
10766
10768
|
if (ctx.common.async) {
|
|
10767
10769
|
const handleAsync = async () => {
|
|
10768
10770
|
const inResult = await this._def.in._parseAsync({
|
|
@@ -10773,7 +10775,7 @@ var ZodPipeline = class _ZodPipeline extends ZodType {
|
|
|
10773
10775
|
if (inResult.status === "aborted")
|
|
10774
10776
|
return INVALID;
|
|
10775
10777
|
if (inResult.status === "dirty") {
|
|
10776
|
-
|
|
10778
|
+
status2.dirty();
|
|
10777
10779
|
return DIRTY(inResult.value);
|
|
10778
10780
|
} else {
|
|
10779
10781
|
return this._def.out._parseAsync({
|
|
@@ -10793,7 +10795,7 @@ var ZodPipeline = class _ZodPipeline extends ZodType {
|
|
|
10793
10795
|
if (inResult.status === "aborted")
|
|
10794
10796
|
return INVALID;
|
|
10795
10797
|
if (inResult.status === "dirty") {
|
|
10796
|
-
|
|
10798
|
+
status2.dirty();
|
|
10797
10799
|
return {
|
|
10798
10800
|
status: "dirty",
|
|
10799
10801
|
value: inResult.value
|
|
@@ -12132,8 +12134,8 @@ var Doc = class {
|
|
|
12132
12134
|
const lines = content.split("\n").filter((x) => x);
|
|
12133
12135
|
const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));
|
|
12134
12136
|
const dedented = lines.map((x) => x.slice(minIndent)).map((x) => " ".repeat(this.indent * 2) + x);
|
|
12135
|
-
for (const
|
|
12136
|
-
this.content.push(
|
|
12137
|
+
for (const line2 of dedented) {
|
|
12138
|
+
this.content.push(line2);
|
|
12137
12139
|
}
|
|
12138
12140
|
}
|
|
12139
12141
|
compile() {
|
|
@@ -12728,38 +12730,38 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
12728
12730
|
doc.write(`const newResult = {}`);
|
|
12729
12731
|
for (const key of normalized.keys) {
|
|
12730
12732
|
if (normalized.optionalKeys.has(key)) {
|
|
12731
|
-
const
|
|
12732
|
-
doc.write(`const ${
|
|
12733
|
+
const id2 = ids[key];
|
|
12734
|
+
doc.write(`const ${id2} = ${parseStr(key)};`);
|
|
12733
12735
|
const k = esc(key);
|
|
12734
12736
|
doc.write(`
|
|
12735
|
-
if (${
|
|
12737
|
+
if (${id2}.issues.length) {
|
|
12736
12738
|
if (input[${k}] === undefined) {
|
|
12737
12739
|
if (${k} in input) {
|
|
12738
12740
|
newResult[${k}] = undefined;
|
|
12739
12741
|
}
|
|
12740
12742
|
} else {
|
|
12741
12743
|
payload.issues = payload.issues.concat(
|
|
12742
|
-
${
|
|
12744
|
+
${id2}.issues.map((iss) => ({
|
|
12743
12745
|
...iss,
|
|
12744
12746
|
path: iss.path ? [${k}, ...iss.path] : [${k}],
|
|
12745
12747
|
}))
|
|
12746
12748
|
);
|
|
12747
12749
|
}
|
|
12748
|
-
} else if (${
|
|
12750
|
+
} else if (${id2}.value === undefined) {
|
|
12749
12751
|
if (${k} in input) newResult[${k}] = undefined;
|
|
12750
12752
|
} else {
|
|
12751
|
-
newResult[${k}] = ${
|
|
12753
|
+
newResult[${k}] = ${id2}.value;
|
|
12752
12754
|
}
|
|
12753
12755
|
`);
|
|
12754
12756
|
} else {
|
|
12755
|
-
const
|
|
12756
|
-
doc.write(`const ${
|
|
12757
|
+
const id2 = ids[key];
|
|
12758
|
+
doc.write(`const ${id2} = ${parseStr(key)};`);
|
|
12757
12759
|
doc.write(`
|
|
12758
|
-
if (${
|
|
12760
|
+
if (${id2}.issues.length) payload.issues = payload.issues.concat(${id2}.issues.map(iss => ({
|
|
12759
12761
|
...iss,
|
|
12760
12762
|
path: iss.path ? [${esc(key)}, ...iss.path] : [${esc(key)}]
|
|
12761
12763
|
})));`);
|
|
12762
|
-
doc.write(`newResult[${esc(key)}] = ${
|
|
12764
|
+
doc.write(`newResult[${esc(key)}] = ${id2}.value`);
|
|
12763
12765
|
}
|
|
12764
12766
|
}
|
|
12765
12767
|
doc.write(`payload.value = newResult;`);
|
|
@@ -14492,13 +14494,13 @@ var JSONSchemaGenerator = class {
|
|
|
14492
14494
|
const defsSegment = this.target === "draft-2020-12" ? "$defs" : "definitions";
|
|
14493
14495
|
if (params.external) {
|
|
14494
14496
|
const externalId = params.external.registry.get(entry[0])?.id;
|
|
14495
|
-
const uriGenerator = params.external.uri ?? ((
|
|
14497
|
+
const uriGenerator = params.external.uri ?? ((id3) => id3);
|
|
14496
14498
|
if (externalId) {
|
|
14497
14499
|
return { ref: uriGenerator(externalId) };
|
|
14498
14500
|
}
|
|
14499
|
-
const
|
|
14500
|
-
entry[1].defId =
|
|
14501
|
-
return { defId:
|
|
14501
|
+
const id2 = entry[1].defId ?? entry[1].schema.id ?? `schema${this.counter++}`;
|
|
14502
|
+
entry[1].defId = id2;
|
|
14503
|
+
return { defId: id2, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id2}` };
|
|
14502
14504
|
}
|
|
14503
14505
|
if (entry[1] === root) {
|
|
14504
14506
|
return { ref: "#" };
|
|
@@ -14546,8 +14548,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14546
14548
|
continue;
|
|
14547
14549
|
}
|
|
14548
14550
|
}
|
|
14549
|
-
const
|
|
14550
|
-
if (
|
|
14551
|
+
const id2 = this.metadataRegistry.get(entry[0])?.id;
|
|
14552
|
+
if (id2) {
|
|
14551
14553
|
extractToDef(entry);
|
|
14552
14554
|
continue;
|
|
14553
14555
|
}
|
|
@@ -14601,10 +14603,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14601
14603
|
console.warn(`Invalid target: ${this.target}`);
|
|
14602
14604
|
}
|
|
14603
14605
|
if (params.external?.uri) {
|
|
14604
|
-
const
|
|
14605
|
-
if (!
|
|
14606
|
+
const id2 = params.external.registry.get(schema)?.id;
|
|
14607
|
+
if (!id2)
|
|
14606
14608
|
throw new Error("Schema is missing an `id` property");
|
|
14607
|
-
result.$id = params.external.uri(
|
|
14609
|
+
result.$id = params.external.uri(id2);
|
|
14608
14610
|
}
|
|
14609
14611
|
Object.assign(result, root.def);
|
|
14610
14612
|
const defs = params.external?.defs ?? {};
|
|
@@ -17197,8 +17199,8 @@ var UrlElicitationRequiredError = class extends McpError {
|
|
|
17197
17199
|
};
|
|
17198
17200
|
|
|
17199
17201
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
|
|
17200
|
-
function isTerminal(
|
|
17201
|
-
return
|
|
17202
|
+
function isTerminal(status2) {
|
|
17203
|
+
return status2 === "completed" || status2 === "failed" || status2 === "cancelled";
|
|
17202
17204
|
}
|
|
17203
17205
|
|
|
17204
17206
|
// node_modules/zod-to-json-schema/dist/esm/Options.js
|
|
@@ -19417,8 +19419,8 @@ var Protocol = class {
|
|
|
19417
19419
|
}
|
|
19418
19420
|
return task;
|
|
19419
19421
|
},
|
|
19420
|
-
storeTaskResult: async (taskId,
|
|
19421
|
-
await taskStore.storeTaskResult(taskId,
|
|
19422
|
+
storeTaskResult: async (taskId, status2, result) => {
|
|
19423
|
+
await taskStore.storeTaskResult(taskId, status2, result, sessionId);
|
|
19422
19424
|
const task = await taskStore.getTask(taskId, sessionId);
|
|
19423
19425
|
if (task) {
|
|
19424
19426
|
const notification = TaskStatusNotificationSchema.parse({
|
|
@@ -19434,15 +19436,15 @@ var Protocol = class {
|
|
|
19434
19436
|
getTaskResult: (taskId) => {
|
|
19435
19437
|
return taskStore.getTaskResult(taskId, sessionId);
|
|
19436
19438
|
},
|
|
19437
|
-
updateTaskStatus: async (taskId,
|
|
19439
|
+
updateTaskStatus: async (taskId, status2, statusMessage) => {
|
|
19438
19440
|
const task = await taskStore.getTask(taskId, sessionId);
|
|
19439
19441
|
if (!task) {
|
|
19440
19442
|
throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
|
|
19441
19443
|
}
|
|
19442
19444
|
if (isTerminal(task.status)) {
|
|
19443
|
-
throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${
|
|
19445
|
+
throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status2}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
|
|
19444
19446
|
}
|
|
19445
|
-
await taskStore.updateTaskStatus(taskId,
|
|
19447
|
+
await taskStore.updateTaskStatus(taskId, status2, statusMessage, sessionId);
|
|
19446
19448
|
const updatedTask = await taskStore.getTask(taskId, sessionId);
|
|
19447
19449
|
if (updatedTask) {
|
|
19448
19450
|
const notification = TaskStatusNotificationSchema.parse({
|
|
@@ -19637,7 +19639,7 @@ var ExperimentalServerTasks = class {
|
|
|
19637
19639
|
if (hasPreviousToolUse) {
|
|
19638
19640
|
const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
|
|
19639
19641
|
const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
|
|
19640
|
-
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((
|
|
19642
|
+
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id2) => toolResultIds.has(id2))) {
|
|
19641
19643
|
throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
|
|
19642
19644
|
}
|
|
19643
19645
|
}
|
|
@@ -20062,7 +20064,7 @@ var Server = class extends Protocol {
|
|
|
20062
20064
|
if (hasPreviousToolUse) {
|
|
20063
20065
|
const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
|
|
20064
20066
|
const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
|
|
20065
|
-
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((
|
|
20067
|
+
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id2) => toolResultIds.has(id2))) {
|
|
20066
20068
|
throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
|
|
20067
20069
|
}
|
|
20068
20070
|
}
|
|
@@ -21072,16 +21074,16 @@ var ReadBuffer = class {
|
|
|
21072
21074
|
if (index === -1) {
|
|
21073
21075
|
return null;
|
|
21074
21076
|
}
|
|
21075
|
-
const
|
|
21077
|
+
const line2 = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
|
|
21076
21078
|
this._buffer = this._buffer.subarray(index + 1);
|
|
21077
|
-
return deserializeMessage(
|
|
21079
|
+
return deserializeMessage(line2);
|
|
21078
21080
|
}
|
|
21079
21081
|
clear() {
|
|
21080
21082
|
this._buffer = void 0;
|
|
21081
21083
|
}
|
|
21082
21084
|
};
|
|
21083
|
-
function deserializeMessage(
|
|
21084
|
-
return JSONRPCMessageSchema.parse(JSON.parse(
|
|
21085
|
+
function deserializeMessage(line2) {
|
|
21086
|
+
return JSONRPCMessageSchema.parse(JSON.parse(line2));
|
|
21085
21087
|
}
|
|
21086
21088
|
function serializeMessage(message) {
|
|
21087
21089
|
return JSON.stringify(message) + "\n";
|
|
@@ -21177,6 +21179,12 @@ function makeNodeKind(raw) {
|
|
|
21177
21179
|
function makeSubmapRef(raw) {
|
|
21178
21180
|
return ID_RULE.test(raw) ? ok(raw) : err({ kind: "invalid-id", raw, rule: ID_RULE_TEXT });
|
|
21179
21181
|
}
|
|
21182
|
+
var RANK_MIN = 0;
|
|
21183
|
+
var RANK_MAX = 99;
|
|
21184
|
+
var RANK_RULE_TEXT = `an integer in ${RANK_MIN}..${RANK_MAX}, 0 = bottom / most primitive`;
|
|
21185
|
+
function makeRank(raw) {
|
|
21186
|
+
return Number.isInteger(raw) && raw >= RANK_MIN && raw <= RANK_MAX ? ok(raw) : err({ kind: "invalid-rank", raw, rule: RANK_RULE_TEXT });
|
|
21187
|
+
}
|
|
21180
21188
|
var MAP_KINDS = ["dev", "architecture", "dataflow", "behavior-tree", "sequence"];
|
|
21181
21189
|
function makeMapKind(raw) {
|
|
21182
21190
|
return MAP_KINDS.includes(raw) ? ok(raw) : err({ kind: "invalid-map-kind", raw });
|
|
@@ -21190,6 +21198,8 @@ function describeMapError(e) {
|
|
|
21190
21198
|
switch (e.kind) {
|
|
21191
21199
|
case "invalid-id":
|
|
21192
21200
|
return `invalid id "${e.raw}" (rule: ${e.rule})`;
|
|
21201
|
+
case "invalid-rank":
|
|
21202
|
+
return `invalid rank ${e.raw} (rule: ${e.rule})`;
|
|
21193
21203
|
case "invalid-status":
|
|
21194
21204
|
return `invalid status "${e.raw}" (expected: ${NODE_STATUSES.join(" | ")})`;
|
|
21195
21205
|
case "duplicate-layer":
|
|
@@ -21210,6 +21220,8 @@ function describeMapError(e) {
|
|
|
21210
21220
|
return `node "${e.id}" cannot depend on itself`;
|
|
21211
21221
|
case "duplicate-group":
|
|
21212
21222
|
return `group "${e.id}" already exists`;
|
|
21223
|
+
case "id-collision":
|
|
21224
|
+
return `id "${e.id}" already names a ${e.taken} on this map; nodes and groups share one id namespace (both render as boxes, so one id must mean one box) \u2014 rename "${e.id}"`;
|
|
21213
21225
|
case "unknown-group":
|
|
21214
21226
|
return `group "${e.id}" does not exist`;
|
|
21215
21227
|
case "invalid-map-kind":
|
|
@@ -21221,23 +21233,23 @@ function describeMapError(e) {
|
|
|
21221
21233
|
case "group-layer-mismatch":
|
|
21222
21234
|
return `node "${e.node}" (layer ${e.nodeLayer}) cannot join group "${e.group}" (layer ${e.groupLayer}); groups cluster nodes within one band`;
|
|
21223
21235
|
case "layer-not-empty":
|
|
21224
|
-
return `layer "${e.id}" still holds node "${e.occupant}"; move or remove
|
|
21236
|
+
return `layer "${e.id}" still holds node "${e.occupant}"; move its nodes to another band (moveNode) or remove them (removeNode) first`;
|
|
21225
21237
|
case "layer-holds-group":
|
|
21226
|
-
return `layer "${e.id}" still holds group "${e.occupant}"; remove its groups first`;
|
|
21238
|
+
return `layer "${e.id}" still holds group "${e.occupant}"; remove its groups (removeGroup) first`;
|
|
21227
21239
|
case "edge-not-downward":
|
|
21228
21240
|
return `edge ${e.from} (rank ${e.fromRank}) -> ${e.to} (rank ${e.toRank}) is not strictly downward; dependencies may only point to a lower layer`;
|
|
21229
21241
|
}
|
|
21230
21242
|
}
|
|
21231
21243
|
|
|
21232
21244
|
// src/domain/ops.ts
|
|
21233
|
-
function findLayer(map,
|
|
21234
|
-
return map.layers.find((l) => l.id ===
|
|
21245
|
+
function findLayer(map, id2) {
|
|
21246
|
+
return map.layers.find((l) => l.id === id2);
|
|
21235
21247
|
}
|
|
21236
|
-
function findNode(map,
|
|
21237
|
-
return map.nodes.find((n) => n.id ===
|
|
21248
|
+
function findNode(map, id2) {
|
|
21249
|
+
return map.nodes.find((n) => n.id === id2);
|
|
21238
21250
|
}
|
|
21239
|
-
function findGroup(map,
|
|
21240
|
-
return map.groups.find((g) => g.id ===
|
|
21251
|
+
function findGroup(map, id2) {
|
|
21252
|
+
return map.groups.find((g) => g.id === id2);
|
|
21241
21253
|
}
|
|
21242
21254
|
function checkMembership(map, node, nodeLayer, group) {
|
|
21243
21255
|
const g = findGroup(map, group);
|
|
@@ -21249,26 +21261,38 @@ function checkMembership(map, node, nodeLayer, group) {
|
|
|
21249
21261
|
function hasEdge(map, from, to) {
|
|
21250
21262
|
return map.edges.some((e) => e.from === from && e.to === to);
|
|
21251
21263
|
}
|
|
21264
|
+
function checkIdSpace(map, id2, declaring) {
|
|
21265
|
+
const taken = declaring === "node" ? map.groups.some((g) => g.id === id2) : map.nodes.some((n) => n.id === id2);
|
|
21266
|
+
return taken ? { kind: "id-collision", id: id2, taken: declaring === "node" ? "group" : "node" } : void 0;
|
|
21267
|
+
}
|
|
21252
21268
|
function setTitle(map, title) {
|
|
21269
|
+
if (title === null || title === void 0) {
|
|
21270
|
+
const { title: _dropped, ...rest } = map;
|
|
21271
|
+
return rest;
|
|
21272
|
+
}
|
|
21253
21273
|
return { ...map, title };
|
|
21254
21274
|
}
|
|
21255
21275
|
function setKind(map, kind) {
|
|
21256
21276
|
return { ...map, kind };
|
|
21257
21277
|
}
|
|
21258
|
-
function findLane(map,
|
|
21259
|
-
return map.lanes.find((l) => l.id ===
|
|
21278
|
+
function findLane(map, id2) {
|
|
21279
|
+
return map.lanes.find((l) => l.id === id2);
|
|
21260
21280
|
}
|
|
21261
21281
|
function declareLane(map, input) {
|
|
21262
21282
|
if (findLane(map, input.id)) return err({ kind: "duplicate-lane", id: input.id });
|
|
21263
21283
|
return ok({ ...map, lanes: [...map.lanes, { id: input.id, label: input.label }] });
|
|
21264
21284
|
}
|
|
21265
|
-
function
|
|
21266
|
-
if (!findLane(map,
|
|
21285
|
+
function updateLane(map, id2, label) {
|
|
21286
|
+
if (!findLane(map, id2)) return err({ kind: "unknown-lane", id: id2 });
|
|
21287
|
+
return ok({ ...map, lanes: map.lanes.map((l) => l.id === id2 ? { ...l, label } : l) });
|
|
21288
|
+
}
|
|
21289
|
+
function removeLane(map, id2) {
|
|
21290
|
+
if (!findLane(map, id2)) return err({ kind: "unknown-lane", id: id2 });
|
|
21267
21291
|
return ok({
|
|
21268
21292
|
...map,
|
|
21269
|
-
lanes: map.lanes.filter((l) => l.id !==
|
|
21293
|
+
lanes: map.lanes.filter((l) => l.id !== id2),
|
|
21270
21294
|
nodes: map.nodes.map((n) => {
|
|
21271
|
-
if (n.lane !==
|
|
21295
|
+
if (n.lane !== id2) return n;
|
|
21272
21296
|
const { lane: _dropped, ...rest } = n;
|
|
21273
21297
|
return rest;
|
|
21274
21298
|
})
|
|
@@ -21280,18 +21304,47 @@ function declareLayer(map, input) {
|
|
|
21280
21304
|
if (rankHolder) return err({ kind: "duplicate-rank", rank: input.rank, existing: rankHolder.id });
|
|
21281
21305
|
return ok({ ...map, layers: [...map.layers, { id: input.id, name: input.name, rank: input.rank }] });
|
|
21282
21306
|
}
|
|
21307
|
+
function updateLayer(map, id2, input) {
|
|
21308
|
+
const layer = findLayer(map, id2);
|
|
21309
|
+
if (!layer) return err({ kind: "unknown-layer", id: id2 });
|
|
21310
|
+
if (input.rank !== void 0 && input.rank !== layer.rank) {
|
|
21311
|
+
const rankHolder = map.layers.find((l) => l.rank === input.rank && l.id !== id2);
|
|
21312
|
+
if (rankHolder) return err({ kind: "duplicate-rank", rank: input.rank, existing: rankHolder.id });
|
|
21313
|
+
const rankAfter = (nodeId) => {
|
|
21314
|
+
const nodeLayer = findNode(map, nodeId).layer;
|
|
21315
|
+
return nodeLayer === id2 ? input.rank : findLayer(map, nodeLayer).rank;
|
|
21316
|
+
};
|
|
21317
|
+
for (const e of map.edges) {
|
|
21318
|
+
const fromRank = rankAfter(e.from);
|
|
21319
|
+
const toRank = rankAfter(e.to);
|
|
21320
|
+
if (fromRank <= toRank) return err({ kind: "edge-not-downward", from: e.from, fromRank, to: e.to, toRank });
|
|
21321
|
+
}
|
|
21322
|
+
}
|
|
21323
|
+
const updated = {
|
|
21324
|
+
...layer,
|
|
21325
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
21326
|
+
...input.rank !== void 0 ? { rank: input.rank } : {}
|
|
21327
|
+
};
|
|
21328
|
+
return ok({ ...map, layers: map.layers.map((l) => l.id === id2 ? updated : l) });
|
|
21329
|
+
}
|
|
21283
21330
|
function declareGroup(map, input) {
|
|
21284
21331
|
if (findGroup(map, input.id)) return err({ kind: "duplicate-group", id: input.id });
|
|
21332
|
+
const collision = checkIdSpace(map, input.id, "group");
|
|
21333
|
+
if (collision) return err(collision);
|
|
21285
21334
|
if (!findLayer(map, input.layer)) return err({ kind: "unknown-layer", id: input.layer });
|
|
21286
21335
|
return ok({ ...map, groups: [...map.groups, { id: input.id, label: input.label, layer: input.layer }] });
|
|
21287
21336
|
}
|
|
21288
|
-
function
|
|
21289
|
-
if (!findGroup(map,
|
|
21337
|
+
function updateGroup(map, id2, label) {
|
|
21338
|
+
if (!findGroup(map, id2)) return err({ kind: "unknown-group", id: id2 });
|
|
21339
|
+
return ok({ ...map, groups: map.groups.map((g) => g.id === id2 ? { ...g, label } : g) });
|
|
21340
|
+
}
|
|
21341
|
+
function removeGroup(map, id2) {
|
|
21342
|
+
if (!findGroup(map, id2)) return err({ kind: "unknown-group", id: id2 });
|
|
21290
21343
|
return ok({
|
|
21291
21344
|
...map,
|
|
21292
|
-
groups: map.groups.filter((g) => g.id !==
|
|
21345
|
+
groups: map.groups.filter((g) => g.id !== id2),
|
|
21293
21346
|
nodes: map.nodes.map((n) => {
|
|
21294
|
-
if (n.group !==
|
|
21347
|
+
if (n.group !== id2) return n;
|
|
21295
21348
|
const { group: _dropped, ...rest } = n;
|
|
21296
21349
|
return rest;
|
|
21297
21350
|
})
|
|
@@ -21303,11 +21356,13 @@ function aggregateStatus(nodes) {
|
|
|
21303
21356
|
if (nodes.length > 0 && nodes.every((n) => n.status === "done")) return "done";
|
|
21304
21357
|
return "planned";
|
|
21305
21358
|
}
|
|
21306
|
-
function groupStatus(map,
|
|
21307
|
-
return aggregateStatus(map.nodes.filter((n) => n.group ===
|
|
21359
|
+
function groupStatus(map, id2) {
|
|
21360
|
+
return aggregateStatus(map.nodes.filter((n) => n.group === id2));
|
|
21308
21361
|
}
|
|
21309
21362
|
function declareNode(map, input) {
|
|
21310
21363
|
if (findNode(map, input.id)) return err({ kind: "duplicate-node", id: input.id });
|
|
21364
|
+
const collision = checkIdSpace(map, input.id, "node");
|
|
21365
|
+
if (collision) return err(collision);
|
|
21311
21366
|
if (!findLayer(map, input.layer)) return err({ kind: "unknown-layer", id: input.layer });
|
|
21312
21367
|
if (input.group !== void 0) {
|
|
21313
21368
|
const bad = checkMembership(map, input.id, input.layer, input.group);
|
|
@@ -21319,6 +21374,7 @@ function declareNode(map, input) {
|
|
|
21319
21374
|
label: input.label,
|
|
21320
21375
|
layer: input.layer,
|
|
21321
21376
|
status: input.status ?? "planned",
|
|
21377
|
+
...input.evidence !== void 0 ? { evidence: input.evidence } : {},
|
|
21322
21378
|
...input.detail !== void 0 ? { detail: input.detail } : {},
|
|
21323
21379
|
...input.group !== void 0 ? { group: input.group } : {},
|
|
21324
21380
|
...input.kind !== void 0 ? { kind: input.kind } : {},
|
|
@@ -21339,6 +21395,9 @@ function linkNodes(map, from, to, label) {
|
|
|
21339
21395
|
if (fromRank <= toRank) return err({ kind: "edge-not-downward", from, fromRank, to, toRank });
|
|
21340
21396
|
return ok({ ...map, edges: [...map.edges, { from, to, ...label !== void 0 ? { label } : {} }] });
|
|
21341
21397
|
}
|
|
21398
|
+
function resolveOptional(input, current) {
|
|
21399
|
+
return input === void 0 ? current : input === null ? void 0 : input;
|
|
21400
|
+
}
|
|
21342
21401
|
function updateNode(map, input) {
|
|
21343
21402
|
const node = findNode(map, input.id);
|
|
21344
21403
|
if (!node) return err({ kind: "unknown-node", id: input.id });
|
|
@@ -21349,55 +21408,225 @@ function updateNode(map, input) {
|
|
|
21349
21408
|
if (input.lane !== void 0 && input.lane !== null && !findLane(map, input.lane)) {
|
|
21350
21409
|
return err({ kind: "unknown-lane", id: input.lane });
|
|
21351
21410
|
}
|
|
21352
|
-
const {
|
|
21353
|
-
|
|
21354
|
-
|
|
21355
|
-
|
|
21356
|
-
|
|
21411
|
+
const {
|
|
21412
|
+
group: currentGroup,
|
|
21413
|
+
kind: currentKind,
|
|
21414
|
+
lane: currentLane,
|
|
21415
|
+
submap: currentSubmap,
|
|
21416
|
+
evidence: currentEvidence,
|
|
21417
|
+
detail: currentDetail,
|
|
21418
|
+
...bare
|
|
21419
|
+
} = node;
|
|
21420
|
+
const nextGroup = resolveOptional(input.group, currentGroup);
|
|
21421
|
+
const nextKind = resolveOptional(input.kind, currentKind);
|
|
21422
|
+
const nextLane = resolveOptional(input.lane, currentLane);
|
|
21423
|
+
const nextSubmap = resolveOptional(input.submap, currentSubmap);
|
|
21424
|
+
const nextEvidence = resolveOptional(input.evidence, currentEvidence);
|
|
21425
|
+
const nextDetail = resolveOptional(input.detail, currentDetail);
|
|
21357
21426
|
const updated = {
|
|
21358
21427
|
...bare,
|
|
21428
|
+
...nextEvidence !== void 0 ? { evidence: nextEvidence } : {},
|
|
21429
|
+
...nextDetail !== void 0 ? { detail: nextDetail } : {},
|
|
21359
21430
|
...nextGroup !== void 0 ? { group: nextGroup } : {},
|
|
21360
21431
|
...nextKind !== void 0 ? { kind: nextKind } : {},
|
|
21361
21432
|
...nextLane !== void 0 ? { lane: nextLane } : {},
|
|
21362
21433
|
...nextSubmap !== void 0 ? { submap: nextSubmap } : {},
|
|
21363
21434
|
...input.status !== void 0 ? { status: input.status } : {},
|
|
21364
|
-
...input.label !== void 0 ? { label: input.label } : {}
|
|
21365
|
-
...input.evidence !== void 0 ? { evidence: input.evidence } : {},
|
|
21366
|
-
...input.detail !== void 0 ? { detail: input.detail } : {}
|
|
21435
|
+
...input.label !== void 0 ? { label: input.label } : {}
|
|
21367
21436
|
};
|
|
21368
21437
|
return ok({ ...map, nodes: map.nodes.map((n) => n.id === input.id ? updated : n) });
|
|
21369
21438
|
}
|
|
21370
|
-
function
|
|
21371
|
-
|
|
21439
|
+
function moveNode(map, id2, layer) {
|
|
21440
|
+
const node = findNode(map, id2);
|
|
21441
|
+
if (!node) return err({ kind: "unknown-node", id: id2 });
|
|
21442
|
+
const target = findLayer(map, layer);
|
|
21443
|
+
if (!target) return err({ kind: "unknown-layer", id: layer });
|
|
21444
|
+
if (node.group !== void 0) {
|
|
21445
|
+
const bad = checkMembership(map, id2, layer, node.group);
|
|
21446
|
+
if (bad) return err(bad);
|
|
21447
|
+
}
|
|
21448
|
+
const rankAfter = (nodeId) => nodeId === id2 ? target.rank : findLayer(map, findNode(map, nodeId).layer).rank;
|
|
21449
|
+
for (const e of map.edges) {
|
|
21450
|
+
if (e.from !== id2 && e.to !== id2) continue;
|
|
21451
|
+
const fromRank = rankAfter(e.from);
|
|
21452
|
+
const toRank = rankAfter(e.to);
|
|
21453
|
+
if (fromRank <= toRank) return err({ kind: "edge-not-downward", from: e.from, fromRank, to: e.to, toRank });
|
|
21454
|
+
}
|
|
21455
|
+
return ok({ ...map, nodes: map.nodes.map((n) => n.id === id2 ? { ...n, layer } : n) });
|
|
21456
|
+
}
|
|
21457
|
+
function removeNode(map, id2) {
|
|
21458
|
+
if (!findNode(map, id2)) return err({ kind: "unknown-node", id: id2 });
|
|
21372
21459
|
return ok({
|
|
21373
21460
|
...map,
|
|
21374
|
-
nodes: map.nodes.filter((n) => n.id !==
|
|
21375
|
-
edges: map.edges.filter((e) => e.from !==
|
|
21461
|
+
nodes: map.nodes.filter((n) => n.id !== id2),
|
|
21462
|
+
edges: map.edges.filter((e) => e.from !== id2 && e.to !== id2)
|
|
21376
21463
|
});
|
|
21377
21464
|
}
|
|
21378
21465
|
function removeEdge(map, from, to) {
|
|
21379
21466
|
if (!hasEdge(map, from, to)) return err({ kind: "unknown-edge", from, to });
|
|
21380
21467
|
return ok({ ...map, edges: map.edges.filter((e) => !(e.from === from && e.to === to)) });
|
|
21381
21468
|
}
|
|
21382
|
-
function removeLayer(map,
|
|
21383
|
-
if (!findLayer(map,
|
|
21384
|
-
const occupant = map.nodes.find((n) => n.layer ===
|
|
21385
|
-
if (occupant) return err({ kind: "layer-not-empty", id, occupant: occupant.id });
|
|
21386
|
-
const groupOccupant = map.groups.find((g) => g.layer ===
|
|
21387
|
-
if (groupOccupant) return err({ kind: "layer-holds-group", id, occupant: groupOccupant.id });
|
|
21388
|
-
return ok({ ...map, layers: map.layers.filter((l) => l.id !==
|
|
21469
|
+
function removeLayer(map, id2) {
|
|
21470
|
+
if (!findLayer(map, id2)) return err({ kind: "unknown-layer", id: id2 });
|
|
21471
|
+
const occupant = map.nodes.find((n) => n.layer === id2);
|
|
21472
|
+
if (occupant) return err({ kind: "layer-not-empty", id: id2, occupant: occupant.id });
|
|
21473
|
+
const groupOccupant = map.groups.find((g) => g.layer === id2);
|
|
21474
|
+
if (groupOccupant) return err({ kind: "layer-holds-group", id: id2, occupant: groupOccupant.id });
|
|
21475
|
+
return ok({ ...map, layers: map.layers.filter((l) => l.id !== id2) });
|
|
21389
21476
|
}
|
|
21390
21477
|
|
|
21391
|
-
// src/
|
|
21478
|
+
// src/semantics/vocabulary.ts
|
|
21479
|
+
var STATUS_GLYPHS = {
|
|
21480
|
+
planned: ["\xB7", "."],
|
|
21481
|
+
"in-progress": ["\u283F", "*"],
|
|
21482
|
+
done: ["\u25A0", "#"],
|
|
21483
|
+
regressed: ["\u2717", "X"]
|
|
21484
|
+
};
|
|
21485
|
+
function statusGlyph(status2, unicode) {
|
|
21486
|
+
const [uni, ascii] = STATUS_GLYPHS[status2];
|
|
21487
|
+
return unicode ? uni : ascii;
|
|
21488
|
+
}
|
|
21489
|
+
var UNVERIFIED_DONE_GLYPHS = ["\u25A1", "o"];
|
|
21490
|
+
function unverifiedDoneGlyph(unicode) {
|
|
21491
|
+
const [uni, ascii] = UNVERIFIED_DONE_GLYPHS;
|
|
21492
|
+
return unicode ? uni : ascii;
|
|
21493
|
+
}
|
|
21494
|
+
var SPINNER_FRAMES = {
|
|
21495
|
+
unicode: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"],
|
|
21496
|
+
ascii: ["|", "/", "-", "\\"]
|
|
21497
|
+
};
|
|
21498
|
+
function spinnerGlyph(frame, unicode) {
|
|
21499
|
+
const frames = SPINNER_FRAMES[unicode ? "unicode" : "ascii"];
|
|
21500
|
+
return frames[(frame % frames.length + frames.length) % frames.length];
|
|
21501
|
+
}
|
|
21502
|
+
var NODE_KIND_GLYPHS = {
|
|
21503
|
+
selector: ["?", "?"],
|
|
21504
|
+
sequence: ["\xBB", ">"],
|
|
21505
|
+
parallel: ["\u2016", "="],
|
|
21506
|
+
decorator: ["\u25CC", "o"],
|
|
21507
|
+
condition: ["\u25C7", "c"],
|
|
21508
|
+
action: ["\xB7", "."],
|
|
21509
|
+
source: ["\u25CB", "o"],
|
|
21510
|
+
transform: ["\u25D0", "%"],
|
|
21511
|
+
sink: ["\u25CF", "*"],
|
|
21512
|
+
service: ["\u25C6", "S"],
|
|
21513
|
+
db: ["\u25A4", "D"],
|
|
21514
|
+
queue: ["\u2263", "Q"],
|
|
21515
|
+
ui: ["\u25A3", "U"]
|
|
21516
|
+
};
|
|
21517
|
+
function kindGlyph(kind, unicode) {
|
|
21518
|
+
const pair = NODE_KIND_GLYPHS[kind];
|
|
21519
|
+
return pair === void 0 ? void 0 : unicode ? pair[0] : pair[1];
|
|
21520
|
+
}
|
|
21521
|
+
|
|
21522
|
+
// src/semantics/semantics.ts
|
|
21392
21523
|
var ZOOM_MIN = -4;
|
|
21393
21524
|
var ZOOM_MAX = 2;
|
|
21394
21525
|
var ZOOM_DEFAULT = 0;
|
|
21395
21526
|
function clampZoom(n) {
|
|
21396
21527
|
return Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, Math.round(n)));
|
|
21397
21528
|
}
|
|
21529
|
+
function zoomMode(zoom) {
|
|
21530
|
+
if (zoom >= 1) return "detail";
|
|
21531
|
+
if (zoom <= -4) return "overview";
|
|
21532
|
+
return "boxes";
|
|
21533
|
+
}
|
|
21534
|
+
function isNeutralKind(map) {
|
|
21535
|
+
return map.kind !== void 0 && map.kind !== "dev";
|
|
21536
|
+
}
|
|
21537
|
+
function aggregateMap(map) {
|
|
21538
|
+
if (map.groups.length === 0) return void 0;
|
|
21539
|
+
const representative = /* @__PURE__ */ new Map();
|
|
21540
|
+
for (const n of map.nodes) representative.set(n.id, n.group ?? n.id);
|
|
21541
|
+
const nodes = map.groups.map((g) => {
|
|
21542
|
+
const members = map.nodes.filter((n) => n.group === g.id);
|
|
21543
|
+
const done = members.filter((n) => n.status === "done").length;
|
|
21544
|
+
return {
|
|
21545
|
+
id: g.id,
|
|
21546
|
+
// neutral kinds document structure, not progress — no member counts
|
|
21547
|
+
label: isNeutralKind(map) ? g.label : `${g.label} ${done}/${members.length}`,
|
|
21548
|
+
layer: g.layer,
|
|
21549
|
+
status: groupStatus(map, g.id)
|
|
21550
|
+
};
|
|
21551
|
+
});
|
|
21552
|
+
for (const n of map.nodes) if (n.group === void 0) nodes.push(n);
|
|
21553
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21554
|
+
const edges = [];
|
|
21555
|
+
for (const e of map.edges) {
|
|
21556
|
+
const from = representative.get(e.from);
|
|
21557
|
+
const to = representative.get(e.to);
|
|
21558
|
+
if (from === to || seen.has(`${from}->${to}`)) continue;
|
|
21559
|
+
seen.add(`${from}->${to}`);
|
|
21560
|
+
edges.push({ from, to });
|
|
21561
|
+
}
|
|
21562
|
+
return {
|
|
21563
|
+
...map.title !== void 0 ? { title: map.title } : {},
|
|
21564
|
+
...map.kind !== void 0 ? { kind: map.kind } : {},
|
|
21565
|
+
layers: map.layers,
|
|
21566
|
+
groups: [],
|
|
21567
|
+
lanes: map.lanes,
|
|
21568
|
+
nodes,
|
|
21569
|
+
edges
|
|
21570
|
+
};
|
|
21571
|
+
}
|
|
21572
|
+
function flipForSequence(map) {
|
|
21573
|
+
if (map.kind !== "sequence") return map;
|
|
21574
|
+
return {
|
|
21575
|
+
...map,
|
|
21576
|
+
// VIOLATION: state-explicit-in-types - `-l.rank as Rank` produces a value
|
|
21577
|
+
// the Rank brand promises cannot exist: mirroring 0..99 gives -99..0, and
|
|
21578
|
+
// makeRank would refuse every one of them. The alternative is a second
|
|
21579
|
+
// ordered-position type (an unbranded `order` field) threaded through the
|
|
21580
|
+
// renderer's whole layout stage purely so this one derived map can be
|
|
21581
|
+
// typed — a large change to express "these ranks are an order, not a
|
|
21582
|
+
// stored value". What makes it safe is the same thing that makes it
|
|
21583
|
+
// wrong: this map only ever reaches a renderer, which compares ranks and
|
|
21584
|
+
// never writes them (same contract as aggregateMap).
|
|
21585
|
+
layers: map.layers.map((l) => ({ ...l, rank: -l.rank })),
|
|
21586
|
+
edges: map.edges.map((e) => ({ from: e.to, to: e.from, ...e.label !== void 0 ? { label: e.label } : {} }))
|
|
21587
|
+
};
|
|
21588
|
+
}
|
|
21589
|
+
|
|
21590
|
+
// src/render/width.ts
|
|
21398
21591
|
var WIDE_RANGES = [
|
|
21399
21592
|
[4352, 4447],
|
|
21400
21593
|
// Hangul Jamo
|
|
21594
|
+
// Wide symbols scattered through the BMP — mostly emoji that predate the
|
|
21595
|
+
// emoji planes (⌚ ⏰ ⚡ ✅ ✨ ❌ ❓ ⭐ ⬛ …).
|
|
21596
|
+
[8986, 8987],
|
|
21597
|
+
[9001, 9002],
|
|
21598
|
+
[9193, 9196],
|
|
21599
|
+
[9200, 9200],
|
|
21600
|
+
[9203, 9203],
|
|
21601
|
+
[9725, 9726],
|
|
21602
|
+
[9748, 9749],
|
|
21603
|
+
[9800, 9811],
|
|
21604
|
+
[9855, 9855],
|
|
21605
|
+
[9875, 9875],
|
|
21606
|
+
[9889, 9889],
|
|
21607
|
+
[9898, 9899],
|
|
21608
|
+
[9917, 9918],
|
|
21609
|
+
[9924, 9925],
|
|
21610
|
+
[9934, 9934],
|
|
21611
|
+
[9940, 9940],
|
|
21612
|
+
[9962, 9962],
|
|
21613
|
+
[9970, 9971],
|
|
21614
|
+
[9973, 9973],
|
|
21615
|
+
[9978, 9978],
|
|
21616
|
+
[9981, 9981],
|
|
21617
|
+
[9989, 9989],
|
|
21618
|
+
[9994, 9995],
|
|
21619
|
+
[10024, 10024],
|
|
21620
|
+
[10060, 10060],
|
|
21621
|
+
[10062, 10062],
|
|
21622
|
+
[10067, 10069],
|
|
21623
|
+
[10071, 10071],
|
|
21624
|
+
[10133, 10135],
|
|
21625
|
+
[10160, 10160],
|
|
21626
|
+
[10175, 10175],
|
|
21627
|
+
[11035, 11036],
|
|
21628
|
+
[11088, 11088],
|
|
21629
|
+
[11093, 11093],
|
|
21401
21630
|
[11904, 42191],
|
|
21402
21631
|
// CJK radicals .. Yi (covers CJK Unified Ideographs)
|
|
21403
21632
|
[43360, 43391],
|
|
@@ -21410,14 +21639,41 @@ var WIDE_RANGES = [
|
|
|
21410
21639
|
[65280, 65376],
|
|
21411
21640
|
// fullwidth forms
|
|
21412
21641
|
[65504, 65510],
|
|
21642
|
+
[127744, 128591],
|
|
21643
|
+
// pictographs, transport, emoticons (🚀 🎯 😀 …)
|
|
21644
|
+
[128640, 128767],
|
|
21645
|
+
[129280, 129535],
|
|
21646
|
+
// supplemental symbols (🤖 🧱 …)
|
|
21647
|
+
[129648, 129791],
|
|
21648
|
+
// symbols extended-A
|
|
21413
21649
|
[131072, 262141]
|
|
21414
21650
|
// CJK extension planes
|
|
21415
21651
|
];
|
|
21416
|
-
|
|
21417
|
-
|
|
21418
|
-
|
|
21652
|
+
var ZERO_WIDTH_RANGES = [
|
|
21653
|
+
[768, 879],
|
|
21654
|
+
// combining diacritical marks (decomposed 'e' + ´)
|
|
21655
|
+
[6832, 6911],
|
|
21656
|
+
[7616, 7679],
|
|
21657
|
+
[8203, 8207],
|
|
21658
|
+
// zero-width space .. RLM, zero-width joiner among them
|
|
21659
|
+
[8400, 8432],
|
|
21660
|
+
// combining marks for symbols
|
|
21661
|
+
[65024, 65039],
|
|
21662
|
+
// variation selectors, VS16 (emoji presentation) included
|
|
21663
|
+
[65056, 65071],
|
|
21664
|
+
// combining half marks
|
|
21665
|
+
[127995, 127999]
|
|
21666
|
+
// emoji skin tone modifiers — always applied to a base
|
|
21667
|
+
];
|
|
21668
|
+
function inRanges(cp, ranges) {
|
|
21669
|
+
for (const [lo, hi] of ranges) {
|
|
21670
|
+
if (cp >= lo && cp <= hi) return true;
|
|
21419
21671
|
}
|
|
21420
|
-
return
|
|
21672
|
+
return false;
|
|
21673
|
+
}
|
|
21674
|
+
function charWidth(cp) {
|
|
21675
|
+
if (inRanges(cp, ZERO_WIDTH_RANGES)) return 0;
|
|
21676
|
+
return inRanges(cp, WIDE_RANGES) ? 2 : 1;
|
|
21421
21677
|
}
|
|
21422
21678
|
function displayWidth(text2) {
|
|
21423
21679
|
let w = 0;
|
|
@@ -21438,27 +21694,40 @@ function fitWidth(s, width) {
|
|
|
21438
21694
|
}
|
|
21439
21695
|
function wrapWidth(s, width) {
|
|
21440
21696
|
const lines = [];
|
|
21441
|
-
let
|
|
21697
|
+
let line2 = "";
|
|
21442
21698
|
let w = 0;
|
|
21443
21699
|
for (const ch of s.replace(/\r/g, "")) {
|
|
21444
21700
|
if (ch === "\n") {
|
|
21445
|
-
lines.push(
|
|
21446
|
-
|
|
21701
|
+
lines.push(line2);
|
|
21702
|
+
line2 = "";
|
|
21447
21703
|
w = 0;
|
|
21448
21704
|
continue;
|
|
21449
21705
|
}
|
|
21450
21706
|
const cw = displayWidth(ch);
|
|
21451
21707
|
if (w + cw > width) {
|
|
21452
|
-
lines.push(
|
|
21453
|
-
|
|
21708
|
+
lines.push(line2);
|
|
21709
|
+
line2 = "";
|
|
21454
21710
|
w = 0;
|
|
21455
21711
|
}
|
|
21456
|
-
|
|
21712
|
+
line2 += ch;
|
|
21457
21713
|
w += cw;
|
|
21458
21714
|
}
|
|
21459
|
-
if (
|
|
21715
|
+
if (line2 !== "") lines.push(line2);
|
|
21460
21716
|
return lines;
|
|
21461
21717
|
}
|
|
21718
|
+
|
|
21719
|
+
// src/render/canvas.ts
|
|
21720
|
+
var SGR = {
|
|
21721
|
+
none: "",
|
|
21722
|
+
dim: "2",
|
|
21723
|
+
amber: "33",
|
|
21724
|
+
green: "32",
|
|
21725
|
+
greenDim: "32;2",
|
|
21726
|
+
// done, but nothing behind the claim: green, not fully lit
|
|
21727
|
+
red: "31",
|
|
21728
|
+
faint: "90"
|
|
21729
|
+
};
|
|
21730
|
+
var ANSI_RESET = "\x1B[0m";
|
|
21462
21731
|
var UP = 1;
|
|
21463
21732
|
var DOWN = 2;
|
|
21464
21733
|
var LEFT = 4;
|
|
@@ -21493,15 +21762,6 @@ function maskChar(mask, heavyHorizontal, unicode) {
|
|
|
21493
21762
|
}
|
|
21494
21763
|
return LIGHT_BY_MASK[mask] ?? "\u253C";
|
|
21495
21764
|
}
|
|
21496
|
-
var SGR = {
|
|
21497
|
-
none: "",
|
|
21498
|
-
dim: "2",
|
|
21499
|
-
amber: "33",
|
|
21500
|
-
green: "32",
|
|
21501
|
-
red: "31",
|
|
21502
|
-
faint: "90"
|
|
21503
|
-
};
|
|
21504
|
-
var ANSI_RESET = "\x1B[0m";
|
|
21505
21765
|
var BORDER_JUNCTION = {
|
|
21506
21766
|
"\u2500": { down: "\u252C", up: "\u2534" },
|
|
21507
21767
|
"\u254C": { down: "\u252C", up: "\u2534" },
|
|
@@ -21527,11 +21787,17 @@ var Canvas = class {
|
|
|
21527
21787
|
text(x, y, s, style, bold = false) {
|
|
21528
21788
|
let cx = x;
|
|
21529
21789
|
for (const ch of s) {
|
|
21790
|
+
const w = charWidth(ch.codePointAt(0));
|
|
21791
|
+
if (w === 0) {
|
|
21792
|
+
const base = this.cell(Math.max(0, cx - 1), y);
|
|
21793
|
+
const target = base.literal === "" ? this.cell(Math.max(0, cx - 2), y) : base;
|
|
21794
|
+
target.literal = (target.literal ?? "") + ch;
|
|
21795
|
+
continue;
|
|
21796
|
+
}
|
|
21530
21797
|
const c = this.cell(cx, y);
|
|
21531
21798
|
c.literal = ch;
|
|
21532
21799
|
c.style = style;
|
|
21533
21800
|
c.bold = bold;
|
|
21534
|
-
const w = charWidth(ch.codePointAt(0));
|
|
21535
21801
|
if (w === 2) {
|
|
21536
21802
|
const phantom = this.cell(cx + 1, y);
|
|
21537
21803
|
phantom.literal = "";
|
|
@@ -21566,7 +21832,7 @@ var Canvas = class {
|
|
|
21566
21832
|
const out = [];
|
|
21567
21833
|
for (let y = vp.y; y < vp.y + vp.height; y++) {
|
|
21568
21834
|
const row = this.rows[y] ?? [];
|
|
21569
|
-
let
|
|
21835
|
+
let line2 = "";
|
|
21570
21836
|
let open = "";
|
|
21571
21837
|
const end = Math.min(vp.x + vp.width, row.length);
|
|
21572
21838
|
for (let x = Math.max(0, vp.x); x < end; x++) {
|
|
@@ -21581,13 +21847,13 @@ var Canvas = class {
|
|
|
21581
21847
|
}
|
|
21582
21848
|
const params = ch === " " ? "" : isWire ? c.bright ? "1" : SGR.faint : [SGR[c.style], c.bold ? "1" : ""].filter(Boolean).join(";");
|
|
21583
21849
|
if (opts.color && params !== open) {
|
|
21584
|
-
|
|
21850
|
+
line2 += (open !== "" ? ANSI_RESET : "") + (params !== "" ? `\x1B[${params}m` : "");
|
|
21585
21851
|
open = params;
|
|
21586
21852
|
}
|
|
21587
|
-
|
|
21853
|
+
line2 += ch;
|
|
21588
21854
|
}
|
|
21589
|
-
if (opts.color && open !== "")
|
|
21590
|
-
out.push(
|
|
21855
|
+
if (opts.color && open !== "") line2 += ANSI_RESET;
|
|
21856
|
+
out.push(line2.replace(/ +$/, ""));
|
|
21591
21857
|
}
|
|
21592
21858
|
return out;
|
|
21593
21859
|
}
|
|
@@ -21610,163 +21876,33 @@ function drawPath(canvas, points, bright = false) {
|
|
|
21610
21876
|
}
|
|
21611
21877
|
}
|
|
21612
21878
|
}
|
|
21613
|
-
|
|
21614
|
-
|
|
21615
|
-
function skinFor(status, unicode) {
|
|
21616
|
-
const style = status === "planned" ? "dim" : status === "in-progress" ? "amber" : status === "done" ? "green" : "red";
|
|
21617
|
-
if (!unicode) {
|
|
21618
|
-
return status === "planned" ? { h: ".", v: ":", corners: ["+", "+", "+", "+"], style } : { h: "-", v: "|", corners: ["+", "+", "+", "+"], style };
|
|
21619
|
-
}
|
|
21620
|
-
switch (status) {
|
|
21621
|
-
case "planned":
|
|
21622
|
-
return { h: "\u254C", v: "\u254E", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style };
|
|
21623
|
-
case "in-progress":
|
|
21624
|
-
return { h: "\u2500", v: "\u2502", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style };
|
|
21625
|
-
case "done":
|
|
21626
|
-
case "regressed":
|
|
21627
|
-
return { h: "\u2501", v: "\u2503", corners: ["\u250F", "\u2513", "\u2517", "\u251B"], style };
|
|
21628
|
-
}
|
|
21629
|
-
}
|
|
21630
|
-
function glyphFor(status, opts) {
|
|
21631
|
-
const spinner = opts.unicode ? SPINNER_UNICODE : SPINNER_ASCII;
|
|
21632
|
-
switch (status) {
|
|
21633
|
-
case "planned":
|
|
21634
|
-
return opts.unicode ? "\xB7" : ".";
|
|
21635
|
-
case "in-progress":
|
|
21636
|
-
return spinner[opts.spinnerFrame % spinner.length];
|
|
21637
|
-
case "done":
|
|
21638
|
-
return opts.unicode ? "\u25A0" : "#";
|
|
21639
|
-
case "regressed":
|
|
21640
|
-
return opts.unicode ? "\u2717" : "X";
|
|
21641
|
-
}
|
|
21642
|
-
}
|
|
21643
|
-
var NODE_KIND_GLYPHS = {
|
|
21644
|
-
selector: ["?", "?"],
|
|
21645
|
-
sequence: ["\xBB", ">"],
|
|
21646
|
-
parallel: ["\u2016", "="],
|
|
21647
|
-
decorator: ["\u25CC", "o"],
|
|
21648
|
-
condition: ["\u25C7", "c"],
|
|
21649
|
-
action: ["\xB7", "."],
|
|
21650
|
-
source: ["\u25CB", "o"],
|
|
21651
|
-
transform: ["\u25D0", "%"],
|
|
21652
|
-
sink: ["\u25CF", "*"],
|
|
21653
|
-
service: ["\u25C6", "S"],
|
|
21654
|
-
db: ["\u25A4", "D"],
|
|
21655
|
-
queue: ["\u2263", "Q"],
|
|
21656
|
-
ui: ["\u25A3", "U"]
|
|
21657
|
-
};
|
|
21658
|
-
function kindGlyph(kind, unicode) {
|
|
21659
|
-
const pair = NODE_KIND_GLYPHS[kind];
|
|
21660
|
-
return pair === void 0 ? void 0 : unicode ? pair[0] : pair[1];
|
|
21661
|
-
}
|
|
21662
|
-
function isNeutralKind(map) {
|
|
21663
|
-
return map.kind !== void 0 && map.kind !== "dev";
|
|
21664
|
-
}
|
|
21665
|
-
function neutralSkin(unicode) {
|
|
21666
|
-
return unicode ? { h: "\u2500", v: "\u2502", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style: "none" } : { h: "-", v: "|", corners: ["+", "+", "+", "+"], style: "none" };
|
|
21667
|
-
}
|
|
21879
|
+
|
|
21880
|
+
// src/render/zoom-geometry.ts
|
|
21668
21881
|
var BOX_H = 3;
|
|
21669
21882
|
var BOX_GAP = 2;
|
|
21670
21883
|
var LEFT_MARGIN = 2;
|
|
21884
|
+
var BAR_MIN_RUN = 7;
|
|
21671
21885
|
var DETAIL_BUDGET = { innerMin: 22, innerMax: 32, noteRows: 3 };
|
|
21672
21886
|
var DETAIL_PLUS_BUDGET = { innerMin: 30, innerMax: 48, noteRows: 12 };
|
|
21673
21887
|
function zoomGeometry(zoom) {
|
|
21888
|
+
const m = zoomMode(zoom);
|
|
21889
|
+
const mode = m === "overview" ? "constellation" : m;
|
|
21674
21890
|
switch (zoom) {
|
|
21675
21891
|
case 2:
|
|
21676
|
-
return { mode
|
|
21892
|
+
return { mode, scale: 1, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false, detail: DETAIL_PLUS_BUDGET };
|
|
21677
21893
|
case 1:
|
|
21678
|
-
return { mode
|
|
21894
|
+
return { mode, scale: 1, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false, detail: DETAIL_BUDGET };
|
|
21679
21895
|
case 0:
|
|
21680
|
-
return { mode
|
|
21896
|
+
return { mode, scale: 1, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false };
|
|
21681
21897
|
case -1:
|
|
21682
|
-
return { mode
|
|
21898
|
+
return { mode, scale: 0.85, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false };
|
|
21683
21899
|
case -2:
|
|
21684
|
-
return { mode
|
|
21900
|
+
return { mode, scale: 0.7, pad: 0, boxGap: BOX_GAP, breathe: 0, titleGap: 0, barGap: 1, bandCounts: false };
|
|
21685
21901
|
case -3:
|
|
21686
|
-
return { mode
|
|
21902
|
+
return { mode, scale: 0.55, pad: 0, boxGap: 1, breathe: 0, titleGap: 0, barGap: 1, bandCounts: true };
|
|
21687
21903
|
case -4:
|
|
21688
|
-
return { mode
|
|
21689
|
-
}
|
|
21690
|
-
}
|
|
21691
|
-
var LABEL_BUDGET_MIN = 4;
|
|
21692
|
-
function boxSpec(node, geo, unicode, neutral) {
|
|
21693
|
-
const glyph = node.kind !== void 0 ? kindGlyph(node.kind, unicode) : void 0;
|
|
21694
|
-
const badge = node.submap !== void 0 ? unicode ? " \u229E" : " +" : "";
|
|
21695
|
-
const badgeW = displayWidth(badge);
|
|
21696
|
-
const text2 = !neutral && glyph !== void 0 ? `${glyph} ${node.label}` : node.label;
|
|
21697
|
-
if (geo.mode === "constellation") {
|
|
21698
|
-
return { w: 3, h: 1, label: "", pad: 0, borderless: true, extra: [] };
|
|
21699
|
-
}
|
|
21700
|
-
if (geo.mode === "detail" && geo.detail !== void 0) {
|
|
21701
|
-
const budget2 = geo.detail;
|
|
21702
|
-
const innerW = Math.min(Math.max(displayWidth(text2) + badgeW + 4, budget2.innerMin), budget2.innerMax);
|
|
21703
|
-
const extra = [];
|
|
21704
|
-
if (node.evidence !== void 0) extra.push({ text: fitWidth(` ${node.evidence}`, innerW), style: "faint" });
|
|
21705
|
-
if (node.detail !== void 0) {
|
|
21706
|
-
const wrapped = wrapWidth(node.detail, innerW - 2);
|
|
21707
|
-
for (let i = 0; i < Math.min(wrapped.length, budget2.noteRows); i++) {
|
|
21708
|
-
const cut = i === budget2.noteRows - 1 && wrapped.length > budget2.noteRows;
|
|
21709
|
-
extra.push({ text: ` ${cut ? fitWidth(wrapped[i] + "\u2026", innerW - 2) : wrapped[i]}`, style: "none" });
|
|
21710
|
-
}
|
|
21711
|
-
}
|
|
21712
|
-
return {
|
|
21713
|
-
w: innerW + 2,
|
|
21714
|
-
h: BOX_H + extra.length,
|
|
21715
|
-
label: fitWidth(text2, innerW - 4 - badgeW) + badge,
|
|
21716
|
-
pad: 1,
|
|
21717
|
-
borderless: false,
|
|
21718
|
-
extra
|
|
21719
|
-
};
|
|
21720
|
-
}
|
|
21721
|
-
const budget = Math.max(LABEL_BUDGET_MIN, Math.ceil(displayWidth(text2) * geo.scale));
|
|
21722
|
-
const label = fitWidth(text2, budget) + badge;
|
|
21723
|
-
return {
|
|
21724
|
-
w: displayWidth(label) + 4 + 2 * geo.pad,
|
|
21725
|
-
h: BOX_H,
|
|
21726
|
-
label,
|
|
21727
|
-
pad: geo.pad,
|
|
21728
|
-
borderless: false,
|
|
21729
|
-
extra: []
|
|
21730
|
-
};
|
|
21731
|
-
}
|
|
21732
|
-
function renderMap(map, opts) {
|
|
21733
|
-
const built = buildCanvas(map, opts);
|
|
21734
|
-
return built.canvas.emit(opts);
|
|
21735
|
-
}
|
|
21736
|
-
function aggregateMap(map) {
|
|
21737
|
-
if (map.groups.length === 0) return void 0;
|
|
21738
|
-
const representative = /* @__PURE__ */ new Map();
|
|
21739
|
-
for (const n of map.nodes) representative.set(n.id, n.group ?? n.id);
|
|
21740
|
-
const nodes = map.groups.map((g) => {
|
|
21741
|
-
const members = map.nodes.filter((n) => n.group === g.id);
|
|
21742
|
-
const done = members.filter((n) => n.status === "done").length;
|
|
21743
|
-
return {
|
|
21744
|
-
id: g.id,
|
|
21745
|
-
// neutral kinds document structure, not progress — no member counts
|
|
21746
|
-
label: isNeutralKind(map) ? g.label : `${g.label} ${done}/${members.length}`,
|
|
21747
|
-
layer: g.layer,
|
|
21748
|
-
status: groupStatus(map, g.id)
|
|
21749
|
-
};
|
|
21750
|
-
});
|
|
21751
|
-
for (const n of map.nodes) if (n.group === void 0) nodes.push(n);
|
|
21752
|
-
const seen = /* @__PURE__ */ new Set();
|
|
21753
|
-
const edges = [];
|
|
21754
|
-
for (const e of map.edges) {
|
|
21755
|
-
const from = representative.get(e.from);
|
|
21756
|
-
const to = representative.get(e.to);
|
|
21757
|
-
if (from === to || seen.has(`${from}->${to}`)) continue;
|
|
21758
|
-
seen.add(`${from}->${to}`);
|
|
21759
|
-
edges.push({ from, to });
|
|
21904
|
+
return { mode, scale: 0, pad: 0, boxGap: BOX_GAP, breathe: 0, titleGap: 0, barGap: 1, bandCounts: true };
|
|
21760
21905
|
}
|
|
21761
|
-
return {
|
|
21762
|
-
...map.title !== void 0 ? { title: map.title } : {},
|
|
21763
|
-
...map.kind !== void 0 ? { kind: map.kind } : {},
|
|
21764
|
-
layers: map.layers,
|
|
21765
|
-
groups: [],
|
|
21766
|
-
lanes: map.lanes,
|
|
21767
|
-
nodes,
|
|
21768
|
-
edges
|
|
21769
|
-
};
|
|
21770
21906
|
}
|
|
21771
21907
|
var AGGREGATE_GEO = {
|
|
21772
21908
|
mode: "boxes",
|
|
@@ -21778,107 +21914,31 @@ var AGGREGATE_GEO = {
|
|
|
21778
21914
|
barGap: 1,
|
|
21779
21915
|
bandCounts: false
|
|
21780
21916
|
};
|
|
21781
|
-
|
|
21782
|
-
|
|
21783
|
-
|
|
21784
|
-
|
|
21785
|
-
|
|
21786
|
-
|
|
21917
|
+
|
|
21918
|
+
// src/render/routing.ts
|
|
21919
|
+
function routeEdges(map, columns) {
|
|
21920
|
+
const { bandIndexOf, bandBoxes, boxOf, contentWidth } = columns;
|
|
21921
|
+
const pending = map.edges.map((e) => {
|
|
21922
|
+
const from = boxOf.get(e.from);
|
|
21923
|
+
const to = boxOf.get(e.to);
|
|
21924
|
+
return {
|
|
21925
|
+
from,
|
|
21926
|
+
to,
|
|
21927
|
+
fromBand: bandIndexOf.get(from.node.layer),
|
|
21928
|
+
toBand: bandIndexOf.get(to.node.layer)
|
|
21929
|
+
};
|
|
21930
|
+
});
|
|
21931
|
+
const gapVerticals = Array.from(
|
|
21932
|
+
{ length: Math.max(0, columns.bands.length - 1) },
|
|
21933
|
+
() => /* @__PURE__ */ new Map()
|
|
21934
|
+
);
|
|
21935
|
+
const verticalFree = (gap, x, edge) => {
|
|
21936
|
+
const owner = gapVerticals[gap]?.get(x);
|
|
21937
|
+
return owner === void 0 || owner === edge;
|
|
21938
|
+
};
|
|
21939
|
+
const takeVertical = (gap, x, edge) => {
|
|
21940
|
+
gapVerticals[gap]?.set(x, edge);
|
|
21787
21941
|
};
|
|
21788
|
-
}
|
|
21789
|
-
function buildCanvas(map, opts) {
|
|
21790
|
-
const oriented = flipForSequence(map);
|
|
21791
|
-
const plainGeo = zoomGeometry(opts.zoom ?? ZOOM_DEFAULT);
|
|
21792
|
-
const aggregated = plainGeo.mode === "constellation" ? aggregateMap(oriented) : void 0;
|
|
21793
|
-
return buildCanvasWith(aggregated ?? oriented, opts, aggregated !== void 0 ? AGGREGATE_GEO : plainGeo);
|
|
21794
|
-
}
|
|
21795
|
-
function buildCanvasWith(map, opts, geo) {
|
|
21796
|
-
const canvas = new Canvas();
|
|
21797
|
-
const neutral = isNeutralKind(map);
|
|
21798
|
-
const bands = [...map.layers].sort((a, b) => b.rank - a.rank);
|
|
21799
|
-
if (bands.length === 0) {
|
|
21800
|
-
canvas.text(0, 0, map.title ?? "mellos mapping", "none", true);
|
|
21801
|
-
canvas.text(0, 2, "(empty map \u2014 declare layers and nodes to begin)", "dim");
|
|
21802
|
-
return { canvas, hits: [] };
|
|
21803
|
-
}
|
|
21804
|
-
const bandIndexOf = new Map(bands.map((l, i) => [l.id, i]));
|
|
21805
|
-
const boxes = /* @__PURE__ */ new Map();
|
|
21806
|
-
const bandBoxes = bands.map(() => []);
|
|
21807
|
-
for (const node of map.nodes) {
|
|
21808
|
-
const band = bandIndexOf.get(node.layer);
|
|
21809
|
-
const box = { node, ...boxSpec(node, geo, opts.unicode, neutral), x: LEFT_MARGIN, y: 0 };
|
|
21810
|
-
bandBoxes[band].push(box);
|
|
21811
|
-
boxes.set(node.id, box);
|
|
21812
|
-
}
|
|
21813
|
-
const laneCount = map.lanes.length;
|
|
21814
|
-
const laneX = [];
|
|
21815
|
-
const laneW = [];
|
|
21816
|
-
if (laneCount === 0) {
|
|
21817
|
-
for (const row of bandBoxes) {
|
|
21818
|
-
let x = LEFT_MARGIN;
|
|
21819
|
-
for (const box of row) {
|
|
21820
|
-
box.x = x;
|
|
21821
|
-
x += box.w + geo.boxGap;
|
|
21822
|
-
}
|
|
21823
|
-
}
|
|
21824
|
-
} else {
|
|
21825
|
-
const laneGap = geo.boxGap + 2;
|
|
21826
|
-
const laneIndexOf = new Map(map.lanes.map((l, i) => [l.id, i]));
|
|
21827
|
-
const regions = laneCount + 1;
|
|
21828
|
-
const grouped = bandBoxes.map((row) => {
|
|
21829
|
-
const cells = Array.from({ length: regions }, () => []);
|
|
21830
|
-
for (const box of row) {
|
|
21831
|
-
const lane = box.node.lane;
|
|
21832
|
-
cells[lane !== void 0 ? laneIndexOf.get(lane) : regions - 1].push(box);
|
|
21833
|
-
}
|
|
21834
|
-
return cells;
|
|
21835
|
-
});
|
|
21836
|
-
const regionW = Array.from({ length: regions }, () => 0);
|
|
21837
|
-
for (const cells of grouped) {
|
|
21838
|
-
for (let i = 0; i < regions; i++) {
|
|
21839
|
-
const rowW = cells[i].reduce((sum, b, k) => sum + b.w + (k > 0 ? geo.boxGap : 0), 0);
|
|
21840
|
-
regionW[i] = Math.max(regionW[i], rowW);
|
|
21841
|
-
}
|
|
21842
|
-
}
|
|
21843
|
-
for (let i = 0; i < laneCount; i++) regionW[i] = Math.max(regionW[i], displayWidth(map.lanes[i].label) + 2);
|
|
21844
|
-
let x0 = LEFT_MARGIN;
|
|
21845
|
-
for (let i = 0; i < regions; i++) {
|
|
21846
|
-
laneX.push(x0);
|
|
21847
|
-
laneW.push(regionW[i]);
|
|
21848
|
-
x0 += regionW[i] + laneGap;
|
|
21849
|
-
}
|
|
21850
|
-
for (const cells of grouped) {
|
|
21851
|
-
for (let i = 0; i < regions; i++) {
|
|
21852
|
-
let x = laneX[i];
|
|
21853
|
-
for (const box of cells[i]) {
|
|
21854
|
-
box.x = x;
|
|
21855
|
-
x += box.w + geo.boxGap;
|
|
21856
|
-
}
|
|
21857
|
-
}
|
|
21858
|
-
}
|
|
21859
|
-
}
|
|
21860
|
-
const bandLabel = bands.map((l, i) => {
|
|
21861
|
-
const row = bandBoxes[i];
|
|
21862
|
-
const done = row.filter((b) => b.node.status === "done").length;
|
|
21863
|
-
return geo.bandCounts && row.length > 0 && !neutral ? ` ${l.name} ${done}/${row.length}` : ` ${l.name}`;
|
|
21864
|
-
});
|
|
21865
|
-
let contentWidth = LEFT_MARGIN;
|
|
21866
|
-
for (const row of bandBoxes) {
|
|
21867
|
-
const last = row[row.length - 1];
|
|
21868
|
-
if (last) contentWidth = Math.max(contentWidth, last.x + last.w);
|
|
21869
|
-
}
|
|
21870
|
-
for (let i = 0; i < laneCount; i++) contentWidth = Math.max(contentWidth, laneX[i] + laneW[i]);
|
|
21871
|
-
for (const label of bandLabel) contentWidth = Math.max(contentWidth, LEFT_MARGIN + displayWidth(label) + 7);
|
|
21872
|
-
const routes = map.edges.map((e) => {
|
|
21873
|
-
const fromBox = boxes.get(e.from);
|
|
21874
|
-
const toBox = boxes.get(e.to);
|
|
21875
|
-
return {
|
|
21876
|
-
fromBox,
|
|
21877
|
-
toBox,
|
|
21878
|
-
fromBand: bandIndexOf.get(fromBox.node.layer),
|
|
21879
|
-
toBand: bandIndexOf.get(toBox.node.layer)
|
|
21880
|
-
};
|
|
21881
|
-
});
|
|
21882
21942
|
const claimedColumns = /* @__PURE__ */ new Map();
|
|
21883
21943
|
const isFree = (box, x) => !(claimedColumns.get(box)?.has(x) ?? false);
|
|
21884
21944
|
const claim = (box, x) => {
|
|
@@ -21887,60 +21947,65 @@ function buildCanvasWith(map, opts, geo) {
|
|
|
21887
21947
|
set.add(x);
|
|
21888
21948
|
return x;
|
|
21889
21949
|
};
|
|
21890
|
-
const
|
|
21891
|
-
for (const r of routes) {
|
|
21950
|
+
for (const r of pending) {
|
|
21892
21951
|
if (r.toBand - r.fromBand !== 1) continue;
|
|
21893
|
-
const lo = Math.max(r.
|
|
21894
|
-
const hi = Math.min(r.
|
|
21952
|
+
const lo = Math.max(r.from.x + 1, r.to.x + 1);
|
|
21953
|
+
const hi = Math.min(r.from.x + r.from.w - 2, r.to.x + r.to.w - 2);
|
|
21895
21954
|
if (lo > hi) continue;
|
|
21896
21955
|
const mid = Math.floor((lo + hi) / 2);
|
|
21897
|
-
for (let d = 0; d <= hi - lo &&
|
|
21956
|
+
for (let d = 0; d <= hi - lo && r.straightX === void 0; d++) {
|
|
21898
21957
|
for (const x of d === 0 ? [mid] : [mid - d, mid + d]) {
|
|
21899
|
-
if (x >= lo && x <= hi && isFree(r.
|
|
21900
|
-
straightX
|
|
21958
|
+
if (x >= lo && x <= hi && isFree(r.from, x) && isFree(r.to, x) && verticalFree(r.fromBand, x, r)) {
|
|
21959
|
+
r.straightX = claim(r.to, claim(r.from, x));
|
|
21960
|
+
takeVertical(r.fromBand, x, r);
|
|
21901
21961
|
break;
|
|
21902
21962
|
}
|
|
21903
21963
|
}
|
|
21904
21964
|
}
|
|
21905
21965
|
}
|
|
21906
|
-
const bent =
|
|
21966
|
+
const bent = pending.filter((r) => r.straightX === void 0);
|
|
21907
21967
|
const outgoing = /* @__PURE__ */ new Map();
|
|
21908
21968
|
const incoming = /* @__PURE__ */ new Map();
|
|
21909
21969
|
for (const r of bent) {
|
|
21910
|
-
outgoing.set(r.
|
|
21911
|
-
incoming.set(r.
|
|
21970
|
+
outgoing.set(r.from, [...outgoing.get(r.from) ?? [], r]);
|
|
21971
|
+
incoming.set(r.to, [...incoming.get(r.to) ?? [], r]);
|
|
21912
21972
|
}
|
|
21913
|
-
const freeSlot = (box, k, n) => {
|
|
21973
|
+
const freeSlot = (box, k, n, edge, gap) => {
|
|
21914
21974
|
const lo = box.x + 1;
|
|
21915
21975
|
const hi = box.x + box.w - 2;
|
|
21916
21976
|
const ideal = box.x + Math.min(box.w - 2, Math.max(1, Math.round((k + 1) * (box.w - 1) / (n + 1))));
|
|
21917
21977
|
for (let d = 0; d <= hi - lo; d++) {
|
|
21918
21978
|
for (const x of d === 0 ? [ideal] : [ideal - d, ideal + d]) {
|
|
21919
|
-
if (x >= lo && x <= hi && isFree(box, x)
|
|
21979
|
+
if (x >= lo && x <= hi && isFree(box, x) && verticalFree(gap, x, edge)) {
|
|
21980
|
+
takeVertical(gap, x, edge);
|
|
21981
|
+
return claim(box, x);
|
|
21982
|
+
}
|
|
21920
21983
|
}
|
|
21921
21984
|
}
|
|
21922
21985
|
return ideal;
|
|
21923
21986
|
};
|
|
21924
|
-
const attach = /* @__PURE__ */ new Map();
|
|
21925
21987
|
for (const r of bent) {
|
|
21926
|
-
const outs = outgoing.get(r.
|
|
21927
|
-
const ins = incoming.get(r.
|
|
21928
|
-
|
|
21929
|
-
|
|
21930
|
-
ex: freeSlot(r.toBox, ins.indexOf(r), ins.length)
|
|
21931
|
-
});
|
|
21988
|
+
const outs = outgoing.get(r.from);
|
|
21989
|
+
const ins = incoming.get(r.to);
|
|
21990
|
+
r.exitX = freeSlot(r.from, outs.indexOf(r), outs.length, r, r.fromBand);
|
|
21991
|
+
r.entryX = freeSlot(r.to, ins.indexOf(r), ins.length, r, r.toBand - 1);
|
|
21932
21992
|
}
|
|
21933
|
-
const skipRoutes = bent.filter((r) => r.toBand - r.fromBand > 1);
|
|
21934
21993
|
const usedDescent = /* @__PURE__ */ new Set();
|
|
21935
|
-
const descentX = /* @__PURE__ */ new Map();
|
|
21936
21994
|
let fallbackCount = 0;
|
|
21937
21995
|
const blockedByBox = (band, x) => bandBoxes[band].some((b) => x >= b.x && x <= b.x + b.w - 1);
|
|
21938
|
-
|
|
21939
|
-
|
|
21996
|
+
const descentGapsFree = (r, c) => {
|
|
21997
|
+
for (let g = r.fromBand; g <= r.toBand - 1; g++) {
|
|
21998
|
+
if (!verticalFree(g, c, r)) return false;
|
|
21999
|
+
}
|
|
22000
|
+
return true;
|
|
22001
|
+
};
|
|
22002
|
+
for (const r of bent.filter((e) => e.toBand - e.fromBand > 1)) {
|
|
22003
|
+
const ex = r.entryX;
|
|
21940
22004
|
let chosen;
|
|
21941
22005
|
for (let d = 0; d <= contentWidth && chosen === void 0; d++) {
|
|
21942
22006
|
for (const c of d === 0 ? [ex] : [ex - d, ex + d]) {
|
|
21943
22007
|
if (c < LEFT_MARGIN || c > contentWidth + 1 || usedDescent.has(c)) continue;
|
|
22008
|
+
if (!descentGapsFree(r, c)) continue;
|
|
21944
22009
|
let blocked = false;
|
|
21945
22010
|
for (let b = r.fromBand + 1; b < r.toBand && !blocked; b++) blocked = blockedByBox(b, c);
|
|
21946
22011
|
if (!blocked) {
|
|
@@ -21951,167 +22016,182 @@ function buildCanvasWith(map, opts, geo) {
|
|
|
21951
22016
|
}
|
|
21952
22017
|
if (chosen === void 0) chosen = contentWidth + 2 + fallbackCount++ * 2;
|
|
21953
22018
|
usedDescent.add(chosen);
|
|
21954
|
-
|
|
22019
|
+
for (let g = r.fromBand; g <= r.toBand - 1; g++) takeVertical(g, chosen, r);
|
|
22020
|
+
r.descentX = chosen;
|
|
21955
22021
|
}
|
|
21956
|
-
const
|
|
21957
|
-
const
|
|
21958
|
-
|
|
21959
|
-
|
|
22022
|
+
const gapCount = Math.max(0, columns.bands.length - 1);
|
|
22023
|
+
const gapSegments = Array.from(
|
|
22024
|
+
{ length: gapCount },
|
|
22025
|
+
() => []
|
|
22026
|
+
);
|
|
21960
22027
|
for (const r of bent) {
|
|
21961
|
-
const
|
|
21962
|
-
|
|
21963
|
-
|
|
21964
|
-
gapSegments[r.toBand - 1].push(
|
|
21965
|
-
|
|
22028
|
+
const sx = r.exitX;
|
|
22029
|
+
const ex = r.entryX;
|
|
22030
|
+
if (r.descentX === void 0) {
|
|
22031
|
+
gapSegments[r.toBand - 1].push({
|
|
22032
|
+
edge: r,
|
|
22033
|
+
kind: "landing",
|
|
22034
|
+
segment: { lo: Math.min(sx, ex), hi: Math.max(sx, ex) }
|
|
22035
|
+
});
|
|
21966
22036
|
} else {
|
|
21967
|
-
const c = descentX
|
|
21968
|
-
|
|
21969
|
-
|
|
21970
|
-
|
|
21971
|
-
|
|
21972
|
-
|
|
22037
|
+
const c = r.descentX;
|
|
22038
|
+
gapSegments[r.fromBand].push({ edge: r, kind: "exit", segment: { lo: Math.min(sx, c), hi: Math.max(sx, c) } });
|
|
22039
|
+
gapSegments[r.toBand - 1].push({
|
|
22040
|
+
edge: r,
|
|
22041
|
+
kind: "landing",
|
|
22042
|
+
segment: { lo: Math.min(c, ex), hi: Math.max(c, ex) }
|
|
22043
|
+
});
|
|
21973
22044
|
}
|
|
21974
22045
|
}
|
|
21975
|
-
const
|
|
21976
|
-
const
|
|
22046
|
+
const exitRow = /* @__PURE__ */ new Map();
|
|
22047
|
+
const landingRow = /* @__PURE__ */ new Map();
|
|
22048
|
+
const gapRowCount = gapSegments.map((entries) => {
|
|
21977
22049
|
const rowEnds = [];
|
|
21978
|
-
for (const
|
|
21979
|
-
let row = rowEnds.findIndex((end) =>
|
|
22050
|
+
for (const e of [...entries].sort((a, b) => a.segment.lo - b.segment.lo)) {
|
|
22051
|
+
let row = rowEnds.findIndex((end) => e.segment.lo > end + 1);
|
|
21980
22052
|
if (row === -1) {
|
|
21981
|
-
rowEnds.push(
|
|
22053
|
+
rowEnds.push(e.segment.hi);
|
|
21982
22054
|
row = rowEnds.length - 1;
|
|
21983
22055
|
} else {
|
|
21984
|
-
rowEnds[row] = Math.max(rowEnds[row],
|
|
22056
|
+
rowEnds[row] = Math.max(rowEnds[row], e.segment.hi);
|
|
21985
22057
|
}
|
|
21986
|
-
|
|
22058
|
+
(e.kind === "exit" ? exitRow : landingRow).set(e.edge, row);
|
|
21987
22059
|
}
|
|
21988
22060
|
return rowEnds.length;
|
|
21989
22061
|
});
|
|
21990
|
-
|
|
21991
|
-
|
|
21992
|
-
|
|
21993
|
-
|
|
21994
|
-
|
|
21995
|
-
y += 1 + geo.barGap;
|
|
21996
|
-
}
|
|
21997
|
-
const barY = [];
|
|
21998
|
-
const gapTrackStartY = [];
|
|
21999
|
-
for (let b = 0; b < bands.length; b++) {
|
|
22000
|
-
barY.push(y);
|
|
22001
|
-
y += 1 + geo.barGap;
|
|
22002
|
-
const row = bandBoxes[b];
|
|
22003
|
-
for (const box of row) box.y = y;
|
|
22004
|
-
y += row.reduce((max, box) => Math.max(max, box.h), geo.mode === "constellation" ? 1 : BOX_H);
|
|
22005
|
-
if (b < gapCount) {
|
|
22006
|
-
y += geo.breathe;
|
|
22007
|
-
gapTrackStartY.push(y);
|
|
22008
|
-
y += gapRowCount[b];
|
|
22009
|
-
y += geo.breathe;
|
|
22010
|
-
}
|
|
22011
|
-
}
|
|
22012
|
-
const legendY = y + 1;
|
|
22013
|
-
const rowYOf = (gap, s) => gapTrackStartY[gap] + segmentRow.get(s);
|
|
22014
|
-
if (map.title !== void 0) canvas.text(LEFT_MARGIN, 0, map.title, "none", true);
|
|
22015
|
-
if (laneHeaderY !== void 0) {
|
|
22016
|
-
for (let i = 0; i < laneCount; i++) {
|
|
22017
|
-
const label = fitWidth(map.lanes[i].label, laneW[i]);
|
|
22018
|
-
const cx = laneX[i] + Math.max(0, Math.floor((laneW[i] - displayWidth(label)) / 2));
|
|
22019
|
-
canvas.text(cx, laneHeaderY, label, "faint", true);
|
|
22020
|
-
}
|
|
22021
|
-
}
|
|
22022
|
-
for (let b = 0; b < bands.length; b++) {
|
|
22023
|
-
const label = bandLabel[b];
|
|
22024
|
-
for (let x = 0; x < totalWidth; x++) canvas.line(x, barY[b], LEFT | RIGHT, true);
|
|
22025
|
-
const labelStart = (fallbackCount > 0 ? contentWidth : totalWidth) - displayWidth(label);
|
|
22026
|
-
canvas.text(labelStart, barY[b], label, "none", true);
|
|
22027
|
-
}
|
|
22028
|
-
for (const box of boxes.values()) {
|
|
22029
|
-
drawBox(canvas, box, opts, neutral, opts.focus !== void 0 && box.node.id === opts.focus);
|
|
22030
|
-
}
|
|
22031
|
-
for (const r of routes) {
|
|
22032
|
-
const sy = r.fromBox.y + r.fromBox.h - 1;
|
|
22033
|
-
const ey = r.toBox.y;
|
|
22034
|
-
const bright = opts.focus !== void 0 && (r.fromBox.node.id === opts.focus || r.toBox.node.id === opts.focus);
|
|
22035
|
-
const direct = straightX.get(r);
|
|
22036
|
-
if (direct !== void 0) {
|
|
22037
|
-
drawPath(
|
|
22038
|
-
canvas,
|
|
22039
|
-
[
|
|
22040
|
-
[direct, sy],
|
|
22041
|
-
[direct, ey]
|
|
22042
|
-
],
|
|
22043
|
-
bright
|
|
22044
|
-
);
|
|
22045
|
-
continue;
|
|
22046
|
-
}
|
|
22047
|
-
const { sx, ex } = attach.get(r);
|
|
22048
|
-
const segments = segmentOf.get(r);
|
|
22049
|
-
const landingY = rowYOf(r.toBand - 1, segments.landing);
|
|
22050
|
-
if (r.toBand - r.fromBand === 1) {
|
|
22051
|
-
drawPath(
|
|
22052
|
-
canvas,
|
|
22053
|
-
[
|
|
22054
|
-
[sx, sy],
|
|
22055
|
-
[sx, landingY],
|
|
22056
|
-
[ex, landingY],
|
|
22057
|
-
[ex, ey]
|
|
22058
|
-
],
|
|
22059
|
-
bright
|
|
22060
|
-
);
|
|
22061
|
-
} else {
|
|
22062
|
-
const c = descentX.get(r);
|
|
22063
|
-
const exitY = rowYOf(r.fromBand, segments.exit);
|
|
22064
|
-
drawPath(
|
|
22065
|
-
canvas,
|
|
22066
|
-
[
|
|
22067
|
-
[sx, sy],
|
|
22068
|
-
[sx, exitY],
|
|
22069
|
-
[c, exitY],
|
|
22070
|
-
[c, landingY],
|
|
22071
|
-
[ex, landingY],
|
|
22072
|
-
[ex, ey]
|
|
22073
|
-
],
|
|
22074
|
-
bright
|
|
22075
|
-
);
|
|
22062
|
+
const edges = pending.map((r) => {
|
|
22063
|
+
const common = { from: r.from, to: r.to, fromBand: r.fromBand, toBand: r.toBand };
|
|
22064
|
+
if (r.straightX !== void 0) return { ...common, kind: "straight", x: r.straightX };
|
|
22065
|
+
if (r.descentX === void 0) {
|
|
22066
|
+
return { ...common, kind: "dogleg", exitX: r.exitX, entryX: r.entryX, landingRow: landingRow.get(r) };
|
|
22076
22067
|
}
|
|
22068
|
+
return {
|
|
22069
|
+
...common,
|
|
22070
|
+
kind: "thread",
|
|
22071
|
+
exitX: r.exitX,
|
|
22072
|
+
entryX: r.entryX,
|
|
22073
|
+
descentX: r.descentX,
|
|
22074
|
+
exitRow: exitRow.get(r),
|
|
22075
|
+
landingRow: landingRow.get(r)
|
|
22076
|
+
};
|
|
22077
|
+
});
|
|
22078
|
+
return { edges, gapRowCount, fallbackCount };
|
|
22079
|
+
}
|
|
22080
|
+
function edgePolyline(edge, rows) {
|
|
22081
|
+
const from = rows.boxOf.get(edge.from.node.id);
|
|
22082
|
+
const to = rows.boxOf.get(edge.to.node.id);
|
|
22083
|
+
const sy = from.y + from.h - 1;
|
|
22084
|
+
const ey = to.y;
|
|
22085
|
+
if (edge.kind === "straight") {
|
|
22086
|
+
return [
|
|
22087
|
+
[edge.x, sy],
|
|
22088
|
+
[edge.x, ey]
|
|
22089
|
+
];
|
|
22077
22090
|
}
|
|
22078
|
-
|
|
22079
|
-
if (
|
|
22080
|
-
|
|
22081
|
-
|
|
22082
|
-
|
|
22083
|
-
|
|
22084
|
-
|
|
22085
|
-
seen.add(k);
|
|
22086
|
-
lx = canvas.text(lx, legendY, " ", "none");
|
|
22087
|
-
lx = canvas.text(lx, legendY, `${kindGlyph(k, opts.unicode)} ${k}`, "none");
|
|
22088
|
-
}
|
|
22089
|
-
} else {
|
|
22090
|
-
const legendOpts = { ...opts, spinnerFrame: 0 };
|
|
22091
|
-
const legendEntries = [
|
|
22092
|
-
["planned", "dim"],
|
|
22093
|
-
["in-progress", "amber"],
|
|
22094
|
-
["done", "green"],
|
|
22095
|
-
["regressed", "red"]
|
|
22091
|
+
const landingY = rows.gapTrackStartY[edge.toBand - 1] + edge.landingRow;
|
|
22092
|
+
if (edge.kind === "dogleg") {
|
|
22093
|
+
return [
|
|
22094
|
+
[edge.exitX, sy],
|
|
22095
|
+
[edge.exitX, landingY],
|
|
22096
|
+
[edge.entryX, landingY],
|
|
22097
|
+
[edge.entryX, ey]
|
|
22096
22098
|
];
|
|
22097
|
-
|
|
22098
|
-
|
|
22099
|
-
|
|
22099
|
+
}
|
|
22100
|
+
const exitY = rows.gapTrackStartY[edge.fromBand] + edge.exitRow;
|
|
22101
|
+
return [
|
|
22102
|
+
[edge.exitX, sy],
|
|
22103
|
+
[edge.exitX, exitY],
|
|
22104
|
+
[edge.descentX, exitY],
|
|
22105
|
+
[edge.descentX, landingY],
|
|
22106
|
+
[edge.entryX, landingY],
|
|
22107
|
+
[edge.entryX, ey]
|
|
22108
|
+
];
|
|
22109
|
+
}
|
|
22110
|
+
|
|
22111
|
+
// src/render/skins.ts
|
|
22112
|
+
function styleFor(face) {
|
|
22113
|
+
switch (face) {
|
|
22114
|
+
case "planned":
|
|
22115
|
+
return "dim";
|
|
22116
|
+
case "in-progress":
|
|
22117
|
+
return "amber";
|
|
22118
|
+
case "done":
|
|
22119
|
+
return "green";
|
|
22120
|
+
case "done-unverified":
|
|
22121
|
+
return "greenDim";
|
|
22122
|
+
case "regressed":
|
|
22123
|
+
return "red";
|
|
22124
|
+
}
|
|
22125
|
+
}
|
|
22126
|
+
function skinFor(face, unicode) {
|
|
22127
|
+
const style = styleFor(face);
|
|
22128
|
+
if (!unicode) {
|
|
22129
|
+
return face === "planned" ? { h: ".", v: ":", corners: ["+", "+", "+", "+"], style } : { h: "-", v: "|", corners: ["+", "+", "+", "+"], style };
|
|
22130
|
+
}
|
|
22131
|
+
switch (face) {
|
|
22132
|
+
case "planned":
|
|
22133
|
+
return { h: "\u254C", v: "\u254E", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style };
|
|
22134
|
+
case "in-progress":
|
|
22135
|
+
return { h: "\u2500", v: "\u2502", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style };
|
|
22136
|
+
// an unverified done keeps the heavy border of done — it is the same
|
|
22137
|
+
// claim, told with a hollow glyph and a dimmer green
|
|
22138
|
+
case "done":
|
|
22139
|
+
case "done-unverified":
|
|
22140
|
+
case "regressed":
|
|
22141
|
+
return { h: "\u2501", v: "\u2503", corners: ["\u250F", "\u2513", "\u2517", "\u251B"], style };
|
|
22142
|
+
}
|
|
22143
|
+
}
|
|
22144
|
+
function glyphFor(face, opts) {
|
|
22145
|
+
if (face === "in-progress") return spinnerGlyph(opts.spinnerFrame, opts.unicode);
|
|
22146
|
+
return face === "done-unverified" ? unverifiedDoneGlyph(opts.unicode) : statusGlyph(face, opts.unicode);
|
|
22147
|
+
}
|
|
22148
|
+
function neutralSkin(unicode) {
|
|
22149
|
+
return unicode ? { h: "\u2500", v: "\u2502", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style: "none" } : { h: "-", v: "|", corners: ["+", "+", "+", "+"], style: "none" };
|
|
22150
|
+
}
|
|
22151
|
+
function neutralGlyph(node, unicode) {
|
|
22152
|
+
return (node.kind !== void 0 ? kindGlyph(node.kind, unicode) : void 0) ?? (unicode ? "\xB7" : ".");
|
|
22153
|
+
}
|
|
22154
|
+
function unverifiedDoneIds(declared, drawn) {
|
|
22155
|
+
const out = /* @__PURE__ */ new Set();
|
|
22156
|
+
const declaredById = new Map(declared.nodes.map((n) => [n.id, n]));
|
|
22157
|
+
for (const node of drawn.nodes) {
|
|
22158
|
+
if (node.status !== "done") continue;
|
|
22159
|
+
const own = declaredById.get(node.id);
|
|
22160
|
+
if (own !== void 0) {
|
|
22161
|
+
if (own.evidence === void 0) out.add(node.id);
|
|
22162
|
+
} else if (declared.nodes.some(
|
|
22163
|
+
(m) => m.group === node.id && m.status === "done" && m.evidence === void 0
|
|
22164
|
+
)) {
|
|
22165
|
+
out.add(node.id);
|
|
22100
22166
|
}
|
|
22101
22167
|
}
|
|
22102
|
-
|
|
22103
|
-
|
|
22104
|
-
|
|
22105
|
-
|
|
22106
|
-
|
|
22107
|
-
|
|
22108
|
-
|
|
22109
|
-
|
|
22168
|
+
return out;
|
|
22169
|
+
}
|
|
22170
|
+
|
|
22171
|
+
// src/render/draw.ts
|
|
22172
|
+
function drawTitle(canvas, title) {
|
|
22173
|
+
canvas.text(LEFT_MARGIN, 0, title, "none", true);
|
|
22174
|
+
}
|
|
22175
|
+
function drawLaneHeaders(canvas, map, columns, rows) {
|
|
22176
|
+
if (rows.laneHeaderY === void 0) return;
|
|
22177
|
+
for (let i = 0; i < map.lanes.length; i++) {
|
|
22178
|
+
const region = columns.lanes[i];
|
|
22179
|
+
const label = fitWidth(map.lanes[i].label, region.w);
|
|
22180
|
+
const cx = region.x + Math.max(0, Math.floor((region.w - displayWidth(label)) / 2));
|
|
22181
|
+
canvas.text(cx, rows.laneHeaderY, label, "faint", true);
|
|
22182
|
+
}
|
|
22183
|
+
}
|
|
22184
|
+
function drawBands(canvas, columns, rows, wiredWidth, totalWidth) {
|
|
22185
|
+
for (let b = 0; b < columns.bands.length; b++) {
|
|
22186
|
+
const label = columns.bandLabel[b];
|
|
22187
|
+
for (let x = 0; x < wiredWidth; x++) canvas.line(x, rows.barY[b], LEFT | RIGHT, true);
|
|
22188
|
+
canvas.text(totalWidth - displayWidth(label), rows.barY[b], label, "none", true);
|
|
22189
|
+
}
|
|
22110
22190
|
}
|
|
22111
|
-
function drawBox(canvas, box, opts, neutral, focused = false) {
|
|
22191
|
+
function drawBox(canvas, box, opts, neutral, face, focused = false) {
|
|
22112
22192
|
const { node, x, y, w } = box;
|
|
22113
|
-
const skin = neutral ? neutralSkin(opts.unicode) : skinFor(
|
|
22114
|
-
const slotGlyph = neutral ? (node
|
|
22193
|
+
const skin = neutral ? neutralSkin(opts.unicode) : skinFor(face, opts.unicode);
|
|
22194
|
+
const slotGlyph = neutral ? neutralGlyph(node, opts.unicode) : glyphFor(face, opts);
|
|
22115
22195
|
if (box.borderless) {
|
|
22116
22196
|
canvas.text(x + 1, y, slotGlyph, skin.style, true);
|
|
22117
22197
|
return;
|
|
@@ -22131,66 +22211,234 @@ function drawBox(canvas, box, opts, neutral, focused = false) {
|
|
|
22131
22211
|
}
|
|
22132
22212
|
canvas.text(x, y + box.h - 1, skin.corners[2] + skin.h.repeat(inner) + skin.corners[3], skin.style, focused);
|
|
22133
22213
|
}
|
|
22134
|
-
|
|
22135
|
-
|
|
22136
|
-
|
|
22137
|
-
|
|
22138
|
-
|
|
22139
|
-
var STATE_FILE_RELATIVE_PATH = join(".claude", "mellos-mapping.json");
|
|
22140
|
-
var PAGES_DIR_NAME = "mellos-mapping.pages";
|
|
22141
|
-
function pageFilePath(defaultFile, page) {
|
|
22142
|
-
return page === void 0 ? defaultFile : join(dirname(defaultFile), PAGES_DIR_NAME, `${page}.json`);
|
|
22214
|
+
function drawEdges(canvas, edges, rows, opts) {
|
|
22215
|
+
for (const edge of edges) {
|
|
22216
|
+
const bright = opts.focus !== void 0 && (edge.from.node.id === opts.focus || edge.to.node.id === opts.focus);
|
|
22217
|
+
drawPath(canvas, edgePolyline(edge, rows), bright);
|
|
22218
|
+
}
|
|
22143
22219
|
}
|
|
22144
|
-
|
|
22145
|
-
|
|
22146
|
-
|
|
22147
|
-
|
|
22220
|
+
function drawLegend(canvas, map, opts, legendY, neutral, anyUnverified) {
|
|
22221
|
+
let lx = LEFT_MARGIN;
|
|
22222
|
+
if (neutral) {
|
|
22223
|
+
lx = canvas.text(lx, legendY, map.kind, "faint");
|
|
22224
|
+
const seen = /* @__PURE__ */ new Set();
|
|
22225
|
+
for (const n of map.nodes) {
|
|
22226
|
+
const k = n.kind;
|
|
22227
|
+
if (k === void 0 || seen.has(k) || kindGlyph(k, opts.unicode) === void 0) continue;
|
|
22228
|
+
seen.add(k);
|
|
22229
|
+
lx = canvas.text(lx, legendY, " ", "none");
|
|
22230
|
+
lx = canvas.text(lx, legendY, `${kindGlyph(k, opts.unicode)} ${k}`, "none");
|
|
22231
|
+
}
|
|
22232
|
+
return;
|
|
22233
|
+
}
|
|
22234
|
+
const legendOpts = { ...opts, spinnerFrame: 0 };
|
|
22235
|
+
const faces = ["planned", "in-progress", "done", "regressed"];
|
|
22236
|
+
if (anyUnverified) faces.push("done-unverified");
|
|
22237
|
+
for (const face of faces) {
|
|
22238
|
+
if (lx > LEFT_MARGIN) lx = canvas.text(lx, legendY, " ", "none");
|
|
22239
|
+
const word = face === "done-unverified" ? "done, no evidence" : face;
|
|
22240
|
+
lx = canvas.text(lx, legendY, `${glyphFor(face, legendOpts)} ${word}`, styleFor(face));
|
|
22241
|
+
}
|
|
22148
22242
|
}
|
|
22149
|
-
|
|
22150
|
-
|
|
22151
|
-
|
|
22243
|
+
|
|
22244
|
+
// src/render/layout.ts
|
|
22245
|
+
var LABEL_BUDGET_MIN = 4;
|
|
22246
|
+
function boxSpec(node, geo, unicode, neutral) {
|
|
22247
|
+
const glyph = node.kind !== void 0 ? kindGlyph(node.kind, unicode) : void 0;
|
|
22248
|
+
const badge = node.submap !== void 0 ? unicode ? " \u229E" : " +" : "";
|
|
22249
|
+
const badgeW = displayWidth(badge);
|
|
22250
|
+
const text2 = !neutral && glyph !== void 0 ? `${glyph} ${node.label}` : node.label;
|
|
22251
|
+
if (geo.mode === "constellation") {
|
|
22252
|
+
return { w: 3, h: 1, label: "", pad: 0, borderless: true, extra: [] };
|
|
22253
|
+
}
|
|
22254
|
+
if (geo.mode === "detail" && geo.detail !== void 0) {
|
|
22255
|
+
const budget2 = geo.detail;
|
|
22256
|
+
const innerW = Math.min(Math.max(displayWidth(text2) + badgeW + 4, budget2.innerMin), budget2.innerMax);
|
|
22257
|
+
const extra = [];
|
|
22258
|
+
if (node.evidence !== void 0) extra.push({ text: fitWidth(` ${node.evidence}`, innerW), style: "faint" });
|
|
22259
|
+
if (node.detail !== void 0) {
|
|
22260
|
+
const wrapped = wrapWidth(node.detail, innerW - 2);
|
|
22261
|
+
for (let i = 0; i < Math.min(wrapped.length, budget2.noteRows); i++) {
|
|
22262
|
+
const cut = i === budget2.noteRows - 1 && wrapped.length > budget2.noteRows;
|
|
22263
|
+
extra.push({ text: ` ${cut ? fitWidth(wrapped[i] + "\u2026", innerW - 2) : wrapped[i]}`, style: "none" });
|
|
22264
|
+
}
|
|
22265
|
+
}
|
|
22266
|
+
return {
|
|
22267
|
+
w: innerW + 2,
|
|
22268
|
+
h: BOX_H + extra.length,
|
|
22269
|
+
label: fitWidth(text2, innerW - 4 - badgeW) + badge,
|
|
22270
|
+
pad: 1,
|
|
22271
|
+
borderless: false,
|
|
22272
|
+
extra
|
|
22273
|
+
};
|
|
22274
|
+
}
|
|
22275
|
+
const budget = Math.max(LABEL_BUDGET_MIN, Math.ceil(displayWidth(text2) * geo.scale));
|
|
22276
|
+
const label = fitWidth(text2, budget) + badge;
|
|
22277
|
+
return {
|
|
22278
|
+
w: displayWidth(label) + 4 + 2 * geo.pad,
|
|
22279
|
+
h: BOX_H,
|
|
22280
|
+
label,
|
|
22281
|
+
pad: geo.pad,
|
|
22282
|
+
borderless: false,
|
|
22283
|
+
extra: []
|
|
22284
|
+
};
|
|
22152
22285
|
}
|
|
22153
|
-
function
|
|
22154
|
-
|
|
22155
|
-
|
|
22156
|
-
|
|
22157
|
-
|
|
22158
|
-
|
|
22159
|
-
|
|
22160
|
-
|
|
22286
|
+
function layoutColumns(map, geo, unicode, neutral) {
|
|
22287
|
+
const bands = [...map.layers].sort((a, b) => b.rank - a.rank);
|
|
22288
|
+
const bandIndexOf = new Map(bands.map((l, i) => [l.id, i]));
|
|
22289
|
+
const sized = /* @__PURE__ */ new Map();
|
|
22290
|
+
const bandSized = bands.map(() => []);
|
|
22291
|
+
for (const node of map.nodes) {
|
|
22292
|
+
const spec = { node, ...boxSpec(node, geo, unicode, neutral) };
|
|
22293
|
+
bandSized[bandIndexOf.get(node.layer)].push(spec);
|
|
22294
|
+
sized.set(node.id, spec);
|
|
22295
|
+
}
|
|
22296
|
+
const columnOf = /* @__PURE__ */ new Map();
|
|
22297
|
+
const lanes = [];
|
|
22298
|
+
if (map.lanes.length === 0) {
|
|
22299
|
+
for (const row of bandSized) {
|
|
22300
|
+
let x = LEFT_MARGIN;
|
|
22301
|
+
for (const spec of row) {
|
|
22302
|
+
columnOf.set(spec, x);
|
|
22303
|
+
x += spec.w + geo.boxGap;
|
|
22304
|
+
}
|
|
22305
|
+
}
|
|
22306
|
+
} else {
|
|
22307
|
+
const laneCount = map.lanes.length;
|
|
22308
|
+
const laneGap = geo.boxGap + 2;
|
|
22309
|
+
const laneIndexOf = new Map(map.lanes.map((l, i) => [l.id, i]));
|
|
22310
|
+
const regions = laneCount + 1;
|
|
22311
|
+
const grouped = bandSized.map((row) => {
|
|
22312
|
+
const cells = Array.from({ length: regions }, () => []);
|
|
22313
|
+
for (const spec of row) {
|
|
22314
|
+
const lane = spec.node.lane;
|
|
22315
|
+
cells[lane !== void 0 ? laneIndexOf.get(lane) : regions - 1].push(spec);
|
|
22316
|
+
}
|
|
22317
|
+
return cells;
|
|
22318
|
+
});
|
|
22319
|
+
const regionW = Array.from({ length: regions }, () => 0);
|
|
22320
|
+
for (const cells of grouped) {
|
|
22321
|
+
for (let i = 0; i < regions; i++) {
|
|
22322
|
+
const rowW = cells[i].reduce((sum, b, k) => sum + b.w + (k > 0 ? geo.boxGap : 0), 0);
|
|
22323
|
+
regionW[i] = Math.max(regionW[i], rowW);
|
|
22324
|
+
}
|
|
22325
|
+
}
|
|
22326
|
+
for (let i = 0; i < laneCount; i++) regionW[i] = Math.max(regionW[i], displayWidth(map.lanes[i].label) + 2);
|
|
22327
|
+
let x0 = LEFT_MARGIN;
|
|
22328
|
+
for (let i = 0; i < regions; i++) {
|
|
22329
|
+
lanes.push({ x: x0, w: regionW[i] });
|
|
22330
|
+
x0 += regionW[i] + laneGap;
|
|
22331
|
+
}
|
|
22332
|
+
for (const cells of grouped) {
|
|
22333
|
+
for (let i = 0; i < regions; i++) {
|
|
22334
|
+
let x = lanes[i].x;
|
|
22335
|
+
for (const spec of cells[i]) {
|
|
22336
|
+
columnOf.set(spec, x);
|
|
22337
|
+
x += spec.w + geo.boxGap;
|
|
22338
|
+
}
|
|
22339
|
+
}
|
|
22340
|
+
}
|
|
22161
22341
|
}
|
|
22342
|
+
const placed = /* @__PURE__ */ new Map();
|
|
22343
|
+
for (const [, spec] of sized) placed.set(spec, { ...spec, x: columnOf.get(spec) ?? LEFT_MARGIN });
|
|
22344
|
+
const bandBoxes = bandSized.map((row) => row.map((spec) => placed.get(spec)));
|
|
22345
|
+
const boxOf = /* @__PURE__ */ new Map();
|
|
22346
|
+
for (const node of map.nodes) boxOf.set(node.id, placed.get(sized.get(node.id)));
|
|
22347
|
+
const bandLabel = bands.map((l, i) => {
|
|
22348
|
+
const row = bandBoxes[i];
|
|
22349
|
+
const done = row.filter((b) => b.node.status === "done").length;
|
|
22350
|
+
return geo.bandCounts && row.length > 0 && !neutral ? ` ${l.name} ${done}/${row.length}` : ` ${l.name}`;
|
|
22351
|
+
});
|
|
22352
|
+
let contentWidth = LEFT_MARGIN + BAR_MIN_RUN;
|
|
22353
|
+
for (const row of bandBoxes) for (const box of row) contentWidth = Math.max(contentWidth, box.x + box.w);
|
|
22354
|
+
for (const lane of lanes) contentWidth = Math.max(contentWidth, lane.x + lane.w);
|
|
22355
|
+
return { bands, bandIndexOf, bandBoxes, boxOf, lanes, bandLabel, contentWidth };
|
|
22162
22356
|
}
|
|
22163
|
-
function
|
|
22164
|
-
|
|
22165
|
-
|
|
22166
|
-
|
|
22167
|
-
|
|
22168
|
-
|
|
22169
|
-
|
|
22170
|
-
throw e;
|
|
22357
|
+
function layoutRows(columns, geo, gapRowCount, hasTitle, hasLanes) {
|
|
22358
|
+
let y = 0;
|
|
22359
|
+
if (hasTitle) y += 1 + geo.titleGap;
|
|
22360
|
+
let laneHeaderY;
|
|
22361
|
+
if (hasLanes) {
|
|
22362
|
+
laneHeaderY = y;
|
|
22363
|
+
y += 1 + geo.barGap;
|
|
22171
22364
|
}
|
|
22172
|
-
|
|
22173
|
-
|
|
22174
|
-
|
|
22175
|
-
|
|
22176
|
-
|
|
22365
|
+
const barY = [];
|
|
22366
|
+
const gapTrackStartY = [];
|
|
22367
|
+
const bandBoxes = [];
|
|
22368
|
+
const placed = /* @__PURE__ */ new Map();
|
|
22369
|
+
const gapCount = columns.bands.length - 1;
|
|
22370
|
+
for (let b = 0; b < columns.bands.length; b++) {
|
|
22371
|
+
barY.push(y);
|
|
22372
|
+
y += 1 + geo.barGap;
|
|
22373
|
+
const row = columns.bandBoxes[b];
|
|
22374
|
+
for (const box of row) placed.set(box, { ...box, y });
|
|
22375
|
+
bandBoxes.push(row.map((box) => placed.get(box)));
|
|
22376
|
+
y += row.reduce((max, box) => Math.max(max, box.h), geo.mode === "constellation" ? 1 : BOX_H);
|
|
22377
|
+
if (b < gapCount) {
|
|
22378
|
+
y += geo.breathe;
|
|
22379
|
+
gapTrackStartY.push(y);
|
|
22380
|
+
y += gapRowCount[b];
|
|
22381
|
+
y += geo.breathe;
|
|
22382
|
+
}
|
|
22177
22383
|
}
|
|
22178
|
-
|
|
22179
|
-
|
|
22180
|
-
|
|
22384
|
+
const boxOf = /* @__PURE__ */ new Map();
|
|
22385
|
+
for (const [id2, box] of columns.boxOf) boxOf.set(id2, placed.get(box));
|
|
22386
|
+
return { boxOf, bandBoxes, barY, gapTrackStartY, laneHeaderY, legendY: y + 1 };
|
|
22387
|
+
}
|
|
22388
|
+
|
|
22389
|
+
// src/render/render.ts
|
|
22390
|
+
function renderMap(map, opts) {
|
|
22391
|
+
const built = buildCanvas(map, opts);
|
|
22392
|
+
return built.canvas.emit(opts);
|
|
22393
|
+
}
|
|
22394
|
+
function buildCanvas(map, opts) {
|
|
22395
|
+
const oriented = flipForSequence(map);
|
|
22396
|
+
const plainGeo = zoomGeometry(opts.zoom ?? ZOOM_DEFAULT);
|
|
22397
|
+
const aggregated = plainGeo.mode === "constellation" ? aggregateMap(oriented) : void 0;
|
|
22398
|
+
const drawn = aggregated ?? oriented;
|
|
22399
|
+
return paint(drawn, opts, aggregated !== void 0 ? AGGREGATE_GEO : plainGeo, unverifiedDoneIds(oriented, drawn));
|
|
22400
|
+
}
|
|
22401
|
+
function paint(map, opts, geo, unverified) {
|
|
22402
|
+
const canvas = new Canvas();
|
|
22403
|
+
if (map.layers.length === 0) {
|
|
22404
|
+
canvas.text(0, 0, map.title ?? "mellos mapping", "none", true);
|
|
22405
|
+
canvas.text(0, 2, "(empty map \u2014 declare layers and nodes to begin)", "dim");
|
|
22406
|
+
return { canvas, hits: [] };
|
|
22181
22407
|
}
|
|
22182
|
-
const
|
|
22183
|
-
|
|
22184
|
-
|
|
22185
|
-
const
|
|
22186
|
-
|
|
22408
|
+
const neutral = isNeutralKind(map);
|
|
22409
|
+
const columns = layoutColumns(map, geo, opts.unicode, neutral);
|
|
22410
|
+
const routing = routeEdges(map, columns);
|
|
22411
|
+
const rows = layoutRows(columns, geo, routing.gapRowCount, map.title !== void 0, map.lanes.length > 0);
|
|
22412
|
+
const wiredWidth = routing.fallbackCount > 0 ? columns.contentWidth + 2 + routing.fallbackCount * 2 : columns.contentWidth;
|
|
22413
|
+
const totalWidth = wiredWidth + Math.max(...columns.bandLabel.map(displayWidth));
|
|
22414
|
+
if (map.title !== void 0) drawTitle(canvas, map.title);
|
|
22415
|
+
drawLaneHeaders(canvas, map, columns, rows);
|
|
22416
|
+
drawBands(canvas, columns, rows, wiredWidth, totalWidth);
|
|
22417
|
+
const faceOf = (id2, status2) => unverified.has(id2) ? "done-unverified" : status2;
|
|
22418
|
+
for (const box of rows.boxOf.values()) {
|
|
22419
|
+
const id2 = box.node.id;
|
|
22420
|
+
drawBox(canvas, box, opts, neutral, faceOf(id2, box.node.status), opts.focus !== void 0 && id2 === opts.focus);
|
|
22421
|
+
}
|
|
22422
|
+
drawEdges(canvas, routing.edges, rows, opts);
|
|
22423
|
+
drawLegend(canvas, map, opts, rows.legendY, neutral, unverified.size > 0);
|
|
22424
|
+
const hits = [...rows.boxOf.values()].map((b) => ({
|
|
22425
|
+
id: b.node.id,
|
|
22426
|
+
x: b.x,
|
|
22427
|
+
y: b.y,
|
|
22428
|
+
w: b.w,
|
|
22429
|
+
h: b.h
|
|
22430
|
+
}));
|
|
22431
|
+
return { canvas, hits };
|
|
22187
22432
|
}
|
|
22188
|
-
|
|
22189
|
-
|
|
22190
|
-
|
|
22191
|
-
|
|
22192
|
-
|
|
22193
|
-
|
|
22433
|
+
|
|
22434
|
+
// src/store/store.ts
|
|
22435
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
22436
|
+
import { basename, dirname, join } from "node:path";
|
|
22437
|
+
|
|
22438
|
+
// src/store/format.ts
|
|
22439
|
+
var STATE_FILE_VERSION = 1;
|
|
22440
|
+
function makePageId(raw) {
|
|
22441
|
+
return ID_RULE.test(raw) ? ok(raw) : err({ kind: "invalid-id", raw, rule: ID_RULE_TEXT });
|
|
22194
22442
|
}
|
|
22195
22443
|
function describeStoreError(e) {
|
|
22196
22444
|
switch (e.kind) {
|
|
@@ -22202,132 +22450,198 @@ function describeStoreError(e) {
|
|
|
22202
22450
|
return `map file ${e.path} has an unexpected shape: ${e.detail}`;
|
|
22203
22451
|
case "invariant-violation":
|
|
22204
22452
|
return `map file ${e.path} violates a structural invariant: ${describeMapError(e.violation)}`;
|
|
22453
|
+
case "save-failed":
|
|
22454
|
+
return `could not write ${e.path}: ${e.detail}`;
|
|
22455
|
+
case "delete-failed":
|
|
22456
|
+
return `could not delete ${e.path}: ${e.detail}`;
|
|
22205
22457
|
}
|
|
22206
22458
|
}
|
|
22207
22459
|
function isRecord(v) {
|
|
22208
22460
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
22209
22461
|
}
|
|
22210
|
-
function
|
|
22211
|
-
|
|
22462
|
+
function describeValue(v) {
|
|
22463
|
+
if (v === void 0) return "missing";
|
|
22464
|
+
if (v === null) return "null";
|
|
22465
|
+
if (Array.isArray(v)) return "an array";
|
|
22466
|
+
return `a ${typeof v}`;
|
|
22467
|
+
}
|
|
22468
|
+
function badShape(path, where, expected, got) {
|
|
22469
|
+
return err({ kind: "bad-shape", path, detail: `${where} is ${describeValue(got)}, expected ${expected}` });
|
|
22212
22470
|
}
|
|
22213
|
-
function
|
|
22214
|
-
|
|
22471
|
+
function arrayField(raw, key, path, presence) {
|
|
22472
|
+
const v = raw[key];
|
|
22473
|
+
if (Array.isArray(v)) return ok(v);
|
|
22474
|
+
if (v === void 0 && presence === "optional") return ok([]);
|
|
22475
|
+
return badShape(path, `"${key}"`, "an array", v);
|
|
22476
|
+
}
|
|
22477
|
+
function requiredString(rec, key, where, path) {
|
|
22478
|
+
const v = rec[key];
|
|
22479
|
+
return typeof v === "string" ? ok(v) : badShape(path, `${where}.${key}`, "a string", v);
|
|
22480
|
+
}
|
|
22481
|
+
function optionalString(rec, key, where, path) {
|
|
22482
|
+
const v = rec[key];
|
|
22483
|
+
if (v === void 0) return ok(void 0);
|
|
22484
|
+
return typeof v === "string" ? ok(v) : badShape(path, `${where}.${key}`, "a string", v);
|
|
22215
22485
|
}
|
|
22216
22486
|
function parseMap(raw, path) {
|
|
22217
22487
|
if (!isRecord(raw)) return err({ kind: "bad-shape", path, detail: "root is not an object" });
|
|
22218
22488
|
if (raw["version"] !== STATE_FILE_VERSION) {
|
|
22219
22489
|
return err({ kind: "bad-shape", path, detail: `version is ${String(raw["version"])}, expected ${STATE_FILE_VERSION}` });
|
|
22220
22490
|
}
|
|
22491
|
+
const layers = arrayField(raw, "layers", path, "required");
|
|
22492
|
+
if (!layers.ok) return layers;
|
|
22493
|
+
const nodes = arrayField(raw, "nodes", path, "required");
|
|
22494
|
+
if (!nodes.ok) return nodes;
|
|
22495
|
+
const edges = arrayField(raw, "edges", path, "required");
|
|
22496
|
+
if (!edges.ok) return edges;
|
|
22497
|
+
const lanes = arrayField(raw, "lanes", path, "optional");
|
|
22498
|
+
if (!lanes.ok) return lanes;
|
|
22499
|
+
const groups = arrayField(raw, "groups", path, "optional");
|
|
22500
|
+
if (!groups.ok) return groups;
|
|
22221
22501
|
let map = EMPTY_MAP;
|
|
22222
|
-
const title = optionalString(raw
|
|
22223
|
-
if (title
|
|
22224
|
-
|
|
22225
|
-
|
|
22226
|
-
|
|
22502
|
+
const title = optionalString(raw, "title", "map", path);
|
|
22503
|
+
if (!title.ok) return title;
|
|
22504
|
+
if (title.value !== void 0) map = setTitle(map, title.value);
|
|
22505
|
+
const rawKind = optionalString(raw, "kind", "map", path);
|
|
22506
|
+
if (!rawKind.ok) return rawKind;
|
|
22507
|
+
if (rawKind.value !== void 0) {
|
|
22508
|
+
const kind = makeMapKind(rawKind.value);
|
|
22227
22509
|
if (!kind.ok) return err({ kind: "invariant-violation", path, violation: kind.error });
|
|
22228
22510
|
map = setKind(map, kind.value);
|
|
22229
22511
|
}
|
|
22230
|
-
for (const [i, rawLayer] of
|
|
22231
|
-
|
|
22232
|
-
|
|
22233
|
-
|
|
22234
|
-
|
|
22235
|
-
const
|
|
22236
|
-
if (
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
const
|
|
22512
|
+
for (const [i, rawLayer] of layers.value.entries()) {
|
|
22513
|
+
const where = `layers[${i}]`;
|
|
22514
|
+
if (!isRecord(rawLayer)) return badShape(path, where, "an object", rawLayer);
|
|
22515
|
+
const rawId = requiredString(rawLayer, "id", where, path);
|
|
22516
|
+
if (!rawId.ok) return rawId;
|
|
22517
|
+
const id2 = makeLayerId(rawId.value);
|
|
22518
|
+
if (!id2.ok) return err({ kind: "invariant-violation", path, violation: id2.error });
|
|
22519
|
+
const name = requiredString(rawLayer, "name", where, path);
|
|
22520
|
+
if (!name.ok) return name;
|
|
22521
|
+
const rawRank = rawLayer["rank"];
|
|
22522
|
+
if (typeof rawRank !== "number") return badShape(path, `${where}.rank`, "a number", rawRank);
|
|
22523
|
+
const rank2 = makeRank(rawRank);
|
|
22524
|
+
if (!rank2.ok) return err({ kind: "invariant-violation", path, violation: rank2.error });
|
|
22525
|
+
const next = declareLayer(map, { id: id2.value, name: name.value, rank: rank2.value });
|
|
22240
22526
|
if (!next.ok) return err({ kind: "invariant-violation", path, violation: next.error });
|
|
22241
22527
|
map = next.value;
|
|
22242
22528
|
}
|
|
22243
|
-
for (const [i, rawLane] of
|
|
22244
|
-
|
|
22245
|
-
|
|
22246
|
-
|
|
22247
|
-
|
|
22248
|
-
|
|
22249
|
-
|
|
22529
|
+
for (const [i, rawLane] of lanes.value.entries()) {
|
|
22530
|
+
const where = `lanes[${i}]`;
|
|
22531
|
+
if (!isRecord(rawLane)) return badShape(path, where, "an object", rawLane);
|
|
22532
|
+
const rawId = requiredString(rawLane, "id", where, path);
|
|
22533
|
+
if (!rawId.ok) return rawId;
|
|
22534
|
+
const id2 = makeLaneId(rawId.value);
|
|
22535
|
+
if (!id2.ok) return err({ kind: "invariant-violation", path, violation: id2.error });
|
|
22536
|
+
const label = requiredString(rawLane, "label", where, path);
|
|
22537
|
+
if (!label.ok) return label;
|
|
22538
|
+
const declared = declareLane(map, { id: id2.value, label: label.value });
|
|
22250
22539
|
if (!declared.ok) return err({ kind: "invariant-violation", path, violation: declared.error });
|
|
22251
22540
|
map = declared.value;
|
|
22252
22541
|
}
|
|
22253
|
-
for (const [i, rawGroup] of
|
|
22254
|
-
|
|
22255
|
-
|
|
22256
|
-
|
|
22257
|
-
|
|
22542
|
+
for (const [i, rawGroup] of groups.value.entries()) {
|
|
22543
|
+
const where = `groups[${i}]`;
|
|
22544
|
+
if (!isRecord(rawGroup)) return badShape(path, where, "an object", rawGroup);
|
|
22545
|
+
const rawId = requiredString(rawGroup, "id", where, path);
|
|
22546
|
+
if (!rawId.ok) return rawId;
|
|
22547
|
+
const id2 = makeGroupId(rawId.value);
|
|
22548
|
+
if (!id2.ok) return err({ kind: "invariant-violation", path, violation: id2.error });
|
|
22549
|
+
const rawLayer = requiredString(rawGroup, "layer", where, path);
|
|
22550
|
+
if (!rawLayer.ok) return rawLayer;
|
|
22551
|
+
const layer = makeLayerId(rawLayer.value);
|
|
22258
22552
|
if (!layer.ok) return err({ kind: "invariant-violation", path, violation: layer.error });
|
|
22259
|
-
const label =
|
|
22260
|
-
if (label
|
|
22261
|
-
const declared = declareGroup(map, { id:
|
|
22553
|
+
const label = requiredString(rawGroup, "label", where, path);
|
|
22554
|
+
if (!label.ok) return label;
|
|
22555
|
+
const declared = declareGroup(map, { id: id2.value, label: label.value, layer: layer.value });
|
|
22262
22556
|
if (!declared.ok) return err({ kind: "invariant-violation", path, violation: declared.error });
|
|
22263
22557
|
map = declared.value;
|
|
22264
22558
|
}
|
|
22265
|
-
for (const [i, rawNode] of
|
|
22266
|
-
|
|
22267
|
-
|
|
22268
|
-
|
|
22269
|
-
|
|
22559
|
+
for (const [i, rawNode] of nodes.value.entries()) {
|
|
22560
|
+
const where = `nodes[${i}]`;
|
|
22561
|
+
if (!isRecord(rawNode)) return badShape(path, where, "an object", rawNode);
|
|
22562
|
+
const rawId = requiredString(rawNode, "id", where, path);
|
|
22563
|
+
if (!rawId.ok) return rawId;
|
|
22564
|
+
const id2 = makeNodeId(rawId.value);
|
|
22565
|
+
if (!id2.ok) return err({ kind: "invariant-violation", path, violation: id2.error });
|
|
22566
|
+
const rawLayer = requiredString(rawNode, "layer", where, path);
|
|
22567
|
+
if (!rawLayer.ok) return rawLayer;
|
|
22568
|
+
const layer = makeLayerId(rawLayer.value);
|
|
22270
22569
|
if (!layer.ok) return err({ kind: "invariant-violation", path, violation: layer.error });
|
|
22271
|
-
const
|
|
22272
|
-
if (!
|
|
22273
|
-
const
|
|
22274
|
-
if (
|
|
22275
|
-
const
|
|
22276
|
-
|
|
22570
|
+
const rawStatus = requiredString(rawNode, "status", where, path);
|
|
22571
|
+
if (!rawStatus.ok) return rawStatus;
|
|
22572
|
+
const status2 = makeNodeStatus(rawStatus.value);
|
|
22573
|
+
if (!status2.ok) return err({ kind: "invariant-violation", path, violation: status2.error });
|
|
22574
|
+
const label = requiredString(rawNode, "label", where, path);
|
|
22575
|
+
if (!label.ok) return label;
|
|
22576
|
+
const detail = optionalString(rawNode, "detail", where, path);
|
|
22577
|
+
if (!detail.ok) return detail;
|
|
22578
|
+
const rawGroup = optionalString(rawNode, "group", where, path);
|
|
22579
|
+
if (!rawGroup.ok) return rawGroup;
|
|
22277
22580
|
let group;
|
|
22278
|
-
if (rawGroup !== void 0) {
|
|
22279
|
-
const made = makeGroupId(rawGroup);
|
|
22581
|
+
if (rawGroup.value !== void 0) {
|
|
22582
|
+
const made = makeGroupId(rawGroup.value);
|
|
22280
22583
|
if (!made.ok) return err({ kind: "invariant-violation", path, violation: made.error });
|
|
22281
22584
|
group = made.value;
|
|
22282
22585
|
}
|
|
22283
|
-
const rawNodeKind = optionalString(rawNode
|
|
22284
|
-
|
|
22285
|
-
|
|
22286
|
-
|
|
22586
|
+
const rawNodeKind = optionalString(rawNode, "kind", where, path);
|
|
22587
|
+
if (!rawNodeKind.ok) return rawNodeKind;
|
|
22588
|
+
let nodeKind2;
|
|
22589
|
+
if (rawNodeKind.value !== void 0) {
|
|
22590
|
+
const made = makeNodeKind(rawNodeKind.value);
|
|
22287
22591
|
if (!made.ok) return err({ kind: "invariant-violation", path, violation: made.error });
|
|
22288
|
-
|
|
22592
|
+
nodeKind2 = made.value;
|
|
22289
22593
|
}
|
|
22290
|
-
const rawLane = optionalString(rawNode
|
|
22594
|
+
const rawLane = optionalString(rawNode, "lane", where, path);
|
|
22595
|
+
if (!rawLane.ok) return rawLane;
|
|
22291
22596
|
let lane;
|
|
22292
|
-
if (rawLane !== void 0) {
|
|
22293
|
-
const made = makeLaneId(rawLane);
|
|
22597
|
+
if (rawLane.value !== void 0) {
|
|
22598
|
+
const made = makeLaneId(rawLane.value);
|
|
22294
22599
|
if (!made.ok) return err({ kind: "invariant-violation", path, violation: made.error });
|
|
22295
22600
|
lane = made.value;
|
|
22296
22601
|
}
|
|
22297
|
-
const rawSubmap = optionalString(rawNode
|
|
22602
|
+
const rawSubmap = optionalString(rawNode, "submap", where, path);
|
|
22603
|
+
if (!rawSubmap.ok) return rawSubmap;
|
|
22298
22604
|
let submap;
|
|
22299
|
-
if (rawSubmap !== void 0) {
|
|
22300
|
-
const made = makeSubmapRef(rawSubmap);
|
|
22605
|
+
if (rawSubmap.value !== void 0) {
|
|
22606
|
+
const made = makeSubmapRef(rawSubmap.value);
|
|
22301
22607
|
if (!made.ok) return err({ kind: "invariant-violation", path, violation: made.error });
|
|
22302
22608
|
submap = made.value;
|
|
22303
22609
|
}
|
|
22304
22610
|
const declared = declareNode(map, {
|
|
22305
|
-
id:
|
|
22306
|
-
label,
|
|
22611
|
+
id: id2.value,
|
|
22612
|
+
label: label.value,
|
|
22307
22613
|
layer: layer.value,
|
|
22308
|
-
status:
|
|
22309
|
-
...detail !== void 0 ? { detail } : {},
|
|
22614
|
+
status: status2.value,
|
|
22615
|
+
...detail.value !== void 0 ? { detail: detail.value } : {},
|
|
22310
22616
|
...group !== void 0 ? { group } : {},
|
|
22311
|
-
...
|
|
22617
|
+
...nodeKind2 !== void 0 ? { kind: nodeKind2 } : {},
|
|
22312
22618
|
...lane !== void 0 ? { lane } : {},
|
|
22313
22619
|
...submap !== void 0 ? { submap } : {}
|
|
22314
22620
|
});
|
|
22315
22621
|
if (!declared.ok) return err({ kind: "invariant-violation", path, violation: declared.error });
|
|
22316
22622
|
map = declared.value;
|
|
22317
|
-
const evidence = optionalString(rawNode
|
|
22318
|
-
if (evidence
|
|
22319
|
-
|
|
22623
|
+
const evidence = optionalString(rawNode, "evidence", where, path);
|
|
22624
|
+
if (!evidence.ok) return evidence;
|
|
22625
|
+
if (evidence.value !== void 0) {
|
|
22626
|
+
const updated = updateNode(map, { id: id2.value, evidence: evidence.value });
|
|
22320
22627
|
if (!updated.ok) return err({ kind: "invariant-violation", path, violation: updated.error });
|
|
22321
22628
|
map = updated.value;
|
|
22322
22629
|
}
|
|
22323
22630
|
}
|
|
22324
|
-
for (const [i, rawEdge] of
|
|
22325
|
-
|
|
22326
|
-
|
|
22631
|
+
for (const [i, rawEdge] of edges.value.entries()) {
|
|
22632
|
+
const where = `edges[${i}]`;
|
|
22633
|
+
if (!isRecord(rawEdge)) return badShape(path, where, "an object", rawEdge);
|
|
22634
|
+
const rawFrom = requiredString(rawEdge, "from", where, path);
|
|
22635
|
+
if (!rawFrom.ok) return rawFrom;
|
|
22636
|
+
const from = makeNodeId(rawFrom.value);
|
|
22327
22637
|
if (!from.ok) return err({ kind: "invariant-violation", path, violation: from.error });
|
|
22328
|
-
const
|
|
22638
|
+
const rawTo = requiredString(rawEdge, "to", where, path);
|
|
22639
|
+
if (!rawTo.ok) return rawTo;
|
|
22640
|
+
const to = makeNodeId(rawTo.value);
|
|
22329
22641
|
if (!to.ok) return err({ kind: "invariant-violation", path, violation: to.error });
|
|
22330
|
-
const
|
|
22642
|
+
const label = optionalString(rawEdge, "label", where, path);
|
|
22643
|
+
if (!label.ok) return label;
|
|
22644
|
+
const linked = linkNodes(map, from.value, to.value, label.value);
|
|
22331
22645
|
if (!linked.ok) return err({ kind: "invariant-violation", path, violation: linked.error });
|
|
22332
22646
|
map = linked.value;
|
|
22333
22647
|
}
|
|
@@ -22346,6 +22660,222 @@ function serializeMap(map) {
|
|
|
22346
22660
|
};
|
|
22347
22661
|
return JSON.stringify(body, null, 2) + "\n";
|
|
22348
22662
|
}
|
|
22663
|
+
|
|
22664
|
+
// src/store/store.ts
|
|
22665
|
+
var RENAME_MAX_ATTEMPTS = 10;
|
|
22666
|
+
var RENAME_BACKOFF_STEP_MS = 10;
|
|
22667
|
+
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOENT"]);
|
|
22668
|
+
function sleepSync(ms) {
|
|
22669
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
22670
|
+
}
|
|
22671
|
+
function discardTemp(tmp) {
|
|
22672
|
+
try {
|
|
22673
|
+
rmSync(tmp, { force: true });
|
|
22674
|
+
} catch {
|
|
22675
|
+
}
|
|
22676
|
+
}
|
|
22677
|
+
function errnoOf(e) {
|
|
22678
|
+
return e.code ?? e.message;
|
|
22679
|
+
}
|
|
22680
|
+
function writeFileAtomic(path, contents) {
|
|
22681
|
+
const tmp = `${path}.${process.pid}.${Math.random().toString(36).slice(2, 10)}.tmp`;
|
|
22682
|
+
try {
|
|
22683
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
22684
|
+
writeFileSync(tmp, contents, "utf8");
|
|
22685
|
+
} catch (e) {
|
|
22686
|
+
discardTemp(tmp);
|
|
22687
|
+
return err({ kind: "save-failed", path, detail: `writing the temp file failed: ${errnoOf(e)}` });
|
|
22688
|
+
}
|
|
22689
|
+
let attempt = 1;
|
|
22690
|
+
for (; ; ) {
|
|
22691
|
+
try {
|
|
22692
|
+
renameSync(tmp, path);
|
|
22693
|
+
return ok(void 0);
|
|
22694
|
+
} catch (e) {
|
|
22695
|
+
const code = errnoOf(e);
|
|
22696
|
+
if (!TRANSIENT_RENAME_CODES.has(code) || attempt >= RENAME_MAX_ATTEMPTS) {
|
|
22697
|
+
discardTemp(tmp);
|
|
22698
|
+
return err({ kind: "save-failed", path, detail: `${code} after ${attempt} attempt(s)` });
|
|
22699
|
+
}
|
|
22700
|
+
sleepSync(attempt * RENAME_BACKOFF_STEP_MS);
|
|
22701
|
+
attempt += 1;
|
|
22702
|
+
}
|
|
22703
|
+
}
|
|
22704
|
+
}
|
|
22705
|
+
function isRecord2(v) {
|
|
22706
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
22707
|
+
}
|
|
22708
|
+
function stripBom(text2) {
|
|
22709
|
+
return text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
|
|
22710
|
+
}
|
|
22711
|
+
var STORE_DIR_NAME = ".mellos";
|
|
22712
|
+
var STATE_FILE_RELATIVE_PATH = join(STORE_DIR_NAME, "map.json");
|
|
22713
|
+
var PAGES_DIR_NAME = "pages";
|
|
22714
|
+
function pageFilePath(defaultFile, page2) {
|
|
22715
|
+
return page2 === void 0 ? defaultFile : join(dirname(defaultFile), PAGES_DIR_NAME, `${page2}.json`);
|
|
22716
|
+
}
|
|
22717
|
+
function pageIdOfFile(defaultFile, path) {
|
|
22718
|
+
if (path === defaultFile) return void 0;
|
|
22719
|
+
const name = basename(path);
|
|
22720
|
+
return name.endsWith(".json") ? name.slice(0, -".json".length) : name;
|
|
22721
|
+
}
|
|
22722
|
+
function listPageFiles(defaultFile) {
|
|
22723
|
+
const out = [];
|
|
22724
|
+
if (existsSync(defaultFile)) out.push(defaultFile);
|
|
22725
|
+
let entries = [];
|
|
22726
|
+
try {
|
|
22727
|
+
entries = readdirSync(join(dirname(defaultFile), PAGES_DIR_NAME));
|
|
22728
|
+
} catch {
|
|
22729
|
+
}
|
|
22730
|
+
for (const e of entries.sort()) {
|
|
22731
|
+
if (e.endsWith(".json")) out.push(join(dirname(defaultFile), PAGES_DIR_NAME, e));
|
|
22732
|
+
}
|
|
22733
|
+
return out;
|
|
22734
|
+
}
|
|
22735
|
+
function deletePageFile(path) {
|
|
22736
|
+
try {
|
|
22737
|
+
rmSync(path, { force: true });
|
|
22738
|
+
return ok(void 0);
|
|
22739
|
+
} catch (e) {
|
|
22740
|
+
return err({ kind: "delete-failed", path, detail: errnoOf(e) });
|
|
22741
|
+
}
|
|
22742
|
+
}
|
|
22743
|
+
var VIEWERS_DIR_NAME = "viewers";
|
|
22744
|
+
var VIEWER_FILE_VERSION = 1;
|
|
22745
|
+
var VIEWER_STALE_MS = 5e3;
|
|
22746
|
+
var VIEWER_SWEEP_MS = 6e4;
|
|
22747
|
+
function viewersDirPath(defaultFile) {
|
|
22748
|
+
return join(dirname(defaultFile), VIEWERS_DIR_NAME);
|
|
22749
|
+
}
|
|
22750
|
+
function viewerPidOf(fileName) {
|
|
22751
|
+
const m = /^(\d+)\.json$/.exec(fileName);
|
|
22752
|
+
return m === null ? void 0 : Number(m[1]);
|
|
22753
|
+
}
|
|
22754
|
+
function parseViewerReport(raw) {
|
|
22755
|
+
let parsed;
|
|
22756
|
+
try {
|
|
22757
|
+
parsed = JSON.parse(stripBom(raw));
|
|
22758
|
+
} catch {
|
|
22759
|
+
return void 0;
|
|
22760
|
+
}
|
|
22761
|
+
if (!isRecord2(parsed)) return void 0;
|
|
22762
|
+
if (parsed["version"] !== VIEWER_FILE_VERSION) return void 0;
|
|
22763
|
+
const follow = parsed["follow"];
|
|
22764
|
+
if (typeof follow !== "boolean") return void 0;
|
|
22765
|
+
const page2 = parsed["page"];
|
|
22766
|
+
if (page2 === null || page2 === void 0) return { page: void 0, follow };
|
|
22767
|
+
if (typeof page2 !== "string") return void 0;
|
|
22768
|
+
const id2 = makePageId(page2);
|
|
22769
|
+
return id2.ok ? { page: id2.value, follow } : void 0;
|
|
22770
|
+
}
|
|
22771
|
+
function readLiveViewers(defaultFile, nowMs) {
|
|
22772
|
+
const dir = viewersDirPath(defaultFile);
|
|
22773
|
+
let names;
|
|
22774
|
+
try {
|
|
22775
|
+
names = readdirSync(dir);
|
|
22776
|
+
} catch {
|
|
22777
|
+
return [];
|
|
22778
|
+
}
|
|
22779
|
+
const live = [];
|
|
22780
|
+
for (const name of names) {
|
|
22781
|
+
const pid = viewerPidOf(name);
|
|
22782
|
+
if (pid === void 0) continue;
|
|
22783
|
+
const path = join(dir, name);
|
|
22784
|
+
let raw;
|
|
22785
|
+
let ageMs;
|
|
22786
|
+
try {
|
|
22787
|
+
ageMs = Math.max(0, nowMs - statSync(path).mtimeMs);
|
|
22788
|
+
if (ageMs > VIEWER_SWEEP_MS) {
|
|
22789
|
+
rmSync(path, { force: true });
|
|
22790
|
+
continue;
|
|
22791
|
+
}
|
|
22792
|
+
if (ageMs > VIEWER_STALE_MS) continue;
|
|
22793
|
+
raw = readFileSync(path, "utf8");
|
|
22794
|
+
} catch {
|
|
22795
|
+
continue;
|
|
22796
|
+
}
|
|
22797
|
+
const report = parseViewerReport(raw);
|
|
22798
|
+
if (report !== void 0) live.push({ ...report, pid, ageMs });
|
|
22799
|
+
}
|
|
22800
|
+
return live.sort((a, b) => a.ageMs - b.ageMs || a.pid - b.pid);
|
|
22801
|
+
}
|
|
22802
|
+
var CONFIG_FILE_NAME = "config.json";
|
|
22803
|
+
var CONFIG_FILE_VERSION = 1;
|
|
22804
|
+
function configFilePath(defaultFile) {
|
|
22805
|
+
return join(dirname(defaultFile), CONFIG_FILE_NAME);
|
|
22806
|
+
}
|
|
22807
|
+
function userConfigFilePath(userBase) {
|
|
22808
|
+
return join(userBase, STORE_DIR_NAME, CONFIG_FILE_NAME);
|
|
22809
|
+
}
|
|
22810
|
+
var MAPPING_POLICIES = ["always", "complex", "on-request"];
|
|
22811
|
+
function makeMappingPolicy(raw) {
|
|
22812
|
+
return MAPPING_POLICIES.includes(raw) ? ok(raw) : err({ kind: "invalid-policy", raw, allowed: MAPPING_POLICIES });
|
|
22813
|
+
}
|
|
22814
|
+
function describeMappingPolicy(policy) {
|
|
22815
|
+
switch (policy) {
|
|
22816
|
+
case "always":
|
|
22817
|
+
return "map every structured task \u2014 workflows, designs, architecture, technical dependencies";
|
|
22818
|
+
case "complex":
|
|
22819
|
+
return "map only medium or complex tasks \u2014 several modules, a new subsystem, roughly an hour or more";
|
|
22820
|
+
case "on-request":
|
|
22821
|
+
return "map only when the user explicitly asks";
|
|
22822
|
+
}
|
|
22823
|
+
}
|
|
22824
|
+
function loadMappingPolicy(path) {
|
|
22825
|
+
let text2;
|
|
22826
|
+
try {
|
|
22827
|
+
text2 = readFileSync(path, "utf8");
|
|
22828
|
+
} catch (e) {
|
|
22829
|
+
if (e.code === "ENOENT") return ok(void 0);
|
|
22830
|
+
throw e;
|
|
22831
|
+
}
|
|
22832
|
+
let raw;
|
|
22833
|
+
try {
|
|
22834
|
+
raw = JSON.parse(stripBom(text2));
|
|
22835
|
+
} catch (e) {
|
|
22836
|
+
return err({ kind: "malformed-json", path, detail: e.message });
|
|
22837
|
+
}
|
|
22838
|
+
if (!isRecord2(raw)) return err({ kind: "bad-shape", path, detail: "root is not an object" });
|
|
22839
|
+
if (raw["version"] !== CONFIG_FILE_VERSION) {
|
|
22840
|
+
return err({ kind: "bad-shape", path, detail: `version is ${String(raw["version"])}, expected ${CONFIG_FILE_VERSION}` });
|
|
22841
|
+
}
|
|
22842
|
+
const rawPolicy = raw["policy"];
|
|
22843
|
+
if (rawPolicy === void 0) return ok(void 0);
|
|
22844
|
+
if (typeof rawPolicy !== "string") return err({ kind: "bad-shape", path, detail: "policy is not a string" });
|
|
22845
|
+
const policy = makeMappingPolicy(rawPolicy);
|
|
22846
|
+
return policy.ok ? ok(policy.value) : err({ kind: "bad-shape", path, detail: `policy is "${rawPolicy}", expected one of: ${MAPPING_POLICIES.join(" | ")}` });
|
|
22847
|
+
}
|
|
22848
|
+
function saveMappingPolicy(path, policy) {
|
|
22849
|
+
const body = JSON.stringify({ version: CONFIG_FILE_VERSION, policy }, null, 2) + "\n";
|
|
22850
|
+
return writeFileAtomic(path, body);
|
|
22851
|
+
}
|
|
22852
|
+
var POLICY_SCOPES = ["user", "project"];
|
|
22853
|
+
function effectiveMappingPolicy(projectConfigFile, userConfigFile) {
|
|
22854
|
+
const project = loadMappingPolicy(projectConfigFile);
|
|
22855
|
+
if (!project.ok) return project;
|
|
22856
|
+
const user = loadMappingPolicy(userConfigFile);
|
|
22857
|
+
if (!user.ok) return user;
|
|
22858
|
+
const effective = project.value ?? user.value;
|
|
22859
|
+
const source = project.value !== void 0 ? "project" : user.value !== void 0 ? "user" : void 0;
|
|
22860
|
+
return ok({ project: project.value, user: user.value, effective, source });
|
|
22861
|
+
}
|
|
22862
|
+
var LEGACY_STATE_FILE_RELATIVE_PATH = join(".claude", "mellos-mapping.json");
|
|
22863
|
+
var LEGACY_PAGES_DIR_NAME = "mellos-mapping.pages";
|
|
22864
|
+
var LEGACY_CONFIG_FILE_NAME = "mellos-mapping.config.json";
|
|
22865
|
+
function migrateLegacyStore(defaultFile) {
|
|
22866
|
+
const projectRoot = dirname(dirname(defaultFile));
|
|
22867
|
+
const legacyDefault = join(projectRoot, LEGACY_STATE_FILE_RELATIVE_PATH);
|
|
22868
|
+
const legacyPages = join(dirname(legacyDefault), LEGACY_PAGES_DIR_NAME);
|
|
22869
|
+
const legacyConfig = join(dirname(legacyDefault), LEGACY_CONFIG_FILE_NAME);
|
|
22870
|
+
const hasLegacy = existsSync(legacyDefault) || existsSync(legacyPages) || existsSync(legacyConfig);
|
|
22871
|
+
const hasCurrent = existsSync(defaultFile) || existsSync(join(dirname(defaultFile), PAGES_DIR_NAME)) || existsSync(configFilePath(defaultFile));
|
|
22872
|
+
if (!hasLegacy || hasCurrent) return false;
|
|
22873
|
+
mkdirSync(dirname(defaultFile), { recursive: true });
|
|
22874
|
+
if (existsSync(legacyDefault)) renameSync(legacyDefault, defaultFile);
|
|
22875
|
+
if (existsSync(legacyPages)) renameSync(legacyPages, join(dirname(defaultFile), PAGES_DIR_NAME));
|
|
22876
|
+
if (existsSync(legacyConfig)) renameSync(legacyConfig, configFilePath(defaultFile));
|
|
22877
|
+
return true;
|
|
22878
|
+
}
|
|
22349
22879
|
function loadMapFile(path) {
|
|
22350
22880
|
let text2;
|
|
22351
22881
|
try {
|
|
@@ -22357,20 +22887,20 @@ function loadMapFile(path) {
|
|
|
22357
22887
|
}
|
|
22358
22888
|
let raw;
|
|
22359
22889
|
try {
|
|
22360
|
-
raw = JSON.parse(text2);
|
|
22890
|
+
raw = JSON.parse(stripBom(text2));
|
|
22361
22891
|
} catch (e) {
|
|
22362
22892
|
return err({ kind: "malformed-json", path, detail: e.message });
|
|
22363
22893
|
}
|
|
22364
22894
|
return parseMap(raw, path);
|
|
22365
22895
|
}
|
|
22366
22896
|
function saveMapFile(path, map) {
|
|
22367
|
-
|
|
22368
|
-
const tmp = path + ".tmp";
|
|
22369
|
-
writeFileSync(tmp, serializeMap(map), "utf8");
|
|
22370
|
-
renameSync(tmp, path);
|
|
22897
|
+
return writeFileAtomic(path, serializeMap(map));
|
|
22371
22898
|
}
|
|
22372
22899
|
|
|
22373
22900
|
// src/server/apply.ts
|
|
22901
|
+
function refuseSelfDive(where, submap, page2) {
|
|
22902
|
+
return submap === page2 ? `${where}: a node cannot dive into its own page ("${submap}"); a submap links a CHILD page` : void 0;
|
|
22903
|
+
}
|
|
22374
22904
|
function applyDeclare(map, input) {
|
|
22375
22905
|
let next = input.title !== void 0 ? setTitle(map, input.title) : map;
|
|
22376
22906
|
if (input.kind !== void 0) {
|
|
@@ -22379,38 +22909,40 @@ function applyDeclare(map, input) {
|
|
|
22379
22909
|
next = setKind(next, kind.value);
|
|
22380
22910
|
}
|
|
22381
22911
|
for (const [i, l] of (input.layers ?? []).entries()) {
|
|
22382
|
-
const
|
|
22383
|
-
if (!
|
|
22384
|
-
const
|
|
22912
|
+
const id2 = makeLayerId(l.id);
|
|
22913
|
+
if (!id2.ok) return err(`layers[${i}]: ${describeMapError(id2.error)}`);
|
|
22914
|
+
const rank2 = makeRank(l.rank);
|
|
22915
|
+
if (!rank2.ok) return err(`layers[${i}]: ${describeMapError(rank2.error)}`);
|
|
22916
|
+
const declared = declareLayer(next, { id: id2.value, name: l.name, rank: rank2.value });
|
|
22385
22917
|
if (!declared.ok) return err(`layers[${i}]: ${describeMapError(declared.error)}`);
|
|
22386
22918
|
next = declared.value;
|
|
22387
22919
|
}
|
|
22388
22920
|
for (const [i, l] of (input.lanes ?? []).entries()) {
|
|
22389
|
-
const
|
|
22390
|
-
if (!
|
|
22391
|
-
const declared = declareLane(next, { id:
|
|
22921
|
+
const id2 = makeLaneId(l.id);
|
|
22922
|
+
if (!id2.ok) return err(`lanes[${i}]: ${describeMapError(id2.error)}`);
|
|
22923
|
+
const declared = declareLane(next, { id: id2.value, label: l.label });
|
|
22392
22924
|
if (!declared.ok) return err(`lanes[${i}]: ${describeMapError(declared.error)}`);
|
|
22393
22925
|
next = declared.value;
|
|
22394
22926
|
}
|
|
22395
22927
|
for (const [i, g] of (input.groups ?? []).entries()) {
|
|
22396
|
-
const
|
|
22397
|
-
if (!
|
|
22928
|
+
const id2 = makeGroupId(g.id);
|
|
22929
|
+
if (!id2.ok) return err(`groups[${i}]: ${describeMapError(id2.error)}`);
|
|
22398
22930
|
const layer = makeLayerId(g.layer);
|
|
22399
22931
|
if (!layer.ok) return err(`groups[${i}]: ${describeMapError(layer.error)}`);
|
|
22400
|
-
const declared = declareGroup(next, { id:
|
|
22932
|
+
const declared = declareGroup(next, { id: id2.value, label: g.label, layer: layer.value });
|
|
22401
22933
|
if (!declared.ok) return err(`groups[${i}]: ${describeMapError(declared.error)}`);
|
|
22402
22934
|
next = declared.value;
|
|
22403
22935
|
}
|
|
22404
22936
|
for (const [i, n] of (input.nodes ?? []).entries()) {
|
|
22405
|
-
const
|
|
22406
|
-
if (!
|
|
22937
|
+
const id2 = makeNodeId(n.id);
|
|
22938
|
+
if (!id2.ok) return err(`nodes[${i}]: ${describeMapError(id2.error)}`);
|
|
22407
22939
|
const layer = makeLayerId(n.layer);
|
|
22408
22940
|
if (!layer.ok) return err(`nodes[${i}]: ${describeMapError(layer.error)}`);
|
|
22409
|
-
let
|
|
22941
|
+
let status2;
|
|
22410
22942
|
if (n.status !== void 0) {
|
|
22411
22943
|
const parsed = makeNodeStatus(n.status);
|
|
22412
22944
|
if (!parsed.ok) return err(`nodes[${i}]: ${describeMapError(parsed.error)}`);
|
|
22413
|
-
|
|
22945
|
+
status2 = parsed.value;
|
|
22414
22946
|
}
|
|
22415
22947
|
let group;
|
|
22416
22948
|
if (n.group !== void 0) {
|
|
@@ -22434,13 +22966,16 @@ function applyDeclare(map, input) {
|
|
|
22434
22966
|
if (n.submap !== void 0) {
|
|
22435
22967
|
const parsed = makeSubmapRef(n.submap);
|
|
22436
22968
|
if (!parsed.ok) return err(`nodes[${i}]: ${describeMapError(parsed.error)}`);
|
|
22969
|
+
const selfDive = refuseSelfDive(`nodes[${i}]`, n.submap, input.page);
|
|
22970
|
+
if (selfDive !== void 0) return err(selfDive);
|
|
22437
22971
|
submap = parsed.value;
|
|
22438
22972
|
}
|
|
22439
22973
|
const declared = declareNode(next, {
|
|
22440
|
-
id:
|
|
22974
|
+
id: id2.value,
|
|
22441
22975
|
label: n.label,
|
|
22442
22976
|
layer: layer.value,
|
|
22443
|
-
...
|
|
22977
|
+
...status2 !== void 0 ? { status: status2 } : {},
|
|
22978
|
+
...n.evidence !== void 0 ? { evidence: n.evidence } : {},
|
|
22444
22979
|
...n.detail !== void 0 ? { detail: n.detail } : {},
|
|
22445
22980
|
...group !== void 0 ? { group } : {},
|
|
22446
22981
|
...kind !== void 0 ? { kind } : {},
|
|
@@ -22463,14 +22998,56 @@ function applyDeclare(map, input) {
|
|
|
22463
22998
|
}
|
|
22464
22999
|
function applyUpdate(map, input) {
|
|
22465
23000
|
let next = map;
|
|
22466
|
-
|
|
22467
|
-
|
|
22468
|
-
|
|
22469
|
-
|
|
23001
|
+
const items = (input.updates?.length ?? 0) + (input.layers?.length ?? 0) + (input.groups?.length ?? 0) + (input.lanes?.length ?? 0);
|
|
23002
|
+
if (items === 0) return err("nothing to revise: pass updates, layers, groups or lanes");
|
|
23003
|
+
for (const [i, l] of (input.layers ?? []).entries()) {
|
|
23004
|
+
const id2 = makeLayerId(l.id);
|
|
23005
|
+
if (!id2.ok) return err(`layers[${i}]: ${describeMapError(id2.error)}`);
|
|
23006
|
+
if (l.name === void 0 && l.rank === void 0) {
|
|
23007
|
+
return err(`layers[${i}]: nothing to change; give a name, a rank, or both`);
|
|
23008
|
+
}
|
|
23009
|
+
let rank2;
|
|
23010
|
+
if (l.rank !== void 0) {
|
|
23011
|
+
const parsed = makeRank(l.rank);
|
|
23012
|
+
if (!parsed.ok) return err(`layers[${i}]: ${describeMapError(parsed.error)}`);
|
|
23013
|
+
rank2 = parsed.value;
|
|
23014
|
+
}
|
|
23015
|
+
const updated = updateLayer(next, id2.value, {
|
|
23016
|
+
...l.name !== void 0 ? { name: l.name } : {},
|
|
23017
|
+
...rank2 !== void 0 ? { rank: rank2 } : {}
|
|
23018
|
+
});
|
|
23019
|
+
if (!updated.ok) return err(`layers[${i}]: ${describeMapError(updated.error)}`);
|
|
23020
|
+
next = updated.value;
|
|
23021
|
+
}
|
|
23022
|
+
for (const [i, g] of (input.groups ?? []).entries()) {
|
|
23023
|
+
const id2 = makeGroupId(g.id);
|
|
23024
|
+
if (!id2.ok) return err(`groups[${i}]: ${describeMapError(id2.error)}`);
|
|
23025
|
+
const updated = updateGroup(next, id2.value, g.label);
|
|
23026
|
+
if (!updated.ok) return err(`groups[${i}]: ${describeMapError(updated.error)}`);
|
|
23027
|
+
next = updated.value;
|
|
23028
|
+
}
|
|
23029
|
+
for (const [i, l] of (input.lanes ?? []).entries()) {
|
|
23030
|
+
const id2 = makeLaneId(l.id);
|
|
23031
|
+
if (!id2.ok) return err(`lanes[${i}]: ${describeMapError(id2.error)}`);
|
|
23032
|
+
const updated = updateLane(next, id2.value, l.label);
|
|
23033
|
+
if (!updated.ok) return err(`lanes[${i}]: ${describeMapError(updated.error)}`);
|
|
23034
|
+
next = updated.value;
|
|
23035
|
+
}
|
|
23036
|
+
for (const [i, u] of (input.updates ?? []).entries()) {
|
|
23037
|
+
const id2 = makeNodeId(u.id);
|
|
23038
|
+
if (!id2.ok) return err(`updates[${i}]: ${describeMapError(id2.error)}`);
|
|
23039
|
+
if (u.layer !== void 0) {
|
|
23040
|
+
const layer = makeLayerId(u.layer);
|
|
23041
|
+
if (!layer.ok) return err(`updates[${i}]: ${describeMapError(layer.error)}`);
|
|
23042
|
+
const moved = moveNode(next, id2.value, layer.value);
|
|
23043
|
+
if (!moved.ok) return err(`updates[${i}]: ${describeMapError(moved.error)}`);
|
|
23044
|
+
next = moved.value;
|
|
23045
|
+
}
|
|
23046
|
+
let status2;
|
|
22470
23047
|
if (u.status !== void 0) {
|
|
22471
23048
|
const parsed = makeNodeStatus(u.status);
|
|
22472
23049
|
if (!parsed.ok) return err(`updates[${i}]: ${describeMapError(parsed.error)}`);
|
|
22473
|
-
|
|
23050
|
+
status2 = parsed.value;
|
|
22474
23051
|
}
|
|
22475
23052
|
let group;
|
|
22476
23053
|
if (u.group === null) group = null;
|
|
@@ -22498,11 +23075,13 @@ function applyUpdate(map, input) {
|
|
|
22498
23075
|
else if (u.submap !== void 0) {
|
|
22499
23076
|
const parsed = makeSubmapRef(u.submap);
|
|
22500
23077
|
if (!parsed.ok) return err(`updates[${i}]: ${describeMapError(parsed.error)}`);
|
|
23078
|
+
const selfDive = refuseSelfDive(`updates[${i}]`, u.submap, input.page);
|
|
23079
|
+
if (selfDive !== void 0) return err(selfDive);
|
|
22501
23080
|
submap = parsed.value;
|
|
22502
23081
|
}
|
|
22503
23082
|
const updated = updateNode(next, {
|
|
22504
|
-
id:
|
|
22505
|
-
...
|
|
23083
|
+
id: id2.value,
|
|
23084
|
+
...status2 !== void 0 ? { status: status2 } : {},
|
|
22506
23085
|
...u.label !== void 0 ? { label: u.label } : {},
|
|
22507
23086
|
...u.evidence !== void 0 ? { evidence: u.evidence } : {},
|
|
22508
23087
|
...u.detail !== void 0 ? { detail: u.detail } : {},
|
|
@@ -22528,30 +23107,30 @@ function applyRemove(map, input) {
|
|
|
22528
23107
|
next = removed.value;
|
|
22529
23108
|
}
|
|
22530
23109
|
for (const [i, rawId] of (input.nodes ?? []).entries()) {
|
|
22531
|
-
const
|
|
22532
|
-
if (!
|
|
22533
|
-
const removed = removeNode(next,
|
|
23110
|
+
const id2 = makeNodeId(rawId);
|
|
23111
|
+
if (!id2.ok) return err(`nodes[${i}]: ${describeMapError(id2.error)}`);
|
|
23112
|
+
const removed = removeNode(next, id2.value);
|
|
22534
23113
|
if (!removed.ok) return err(`nodes[${i}]: ${describeMapError(removed.error)}`);
|
|
22535
23114
|
next = removed.value;
|
|
22536
23115
|
}
|
|
22537
23116
|
for (const [i, rawId] of (input.groups ?? []).entries()) {
|
|
22538
|
-
const
|
|
22539
|
-
if (!
|
|
22540
|
-
const removed = removeGroup(next,
|
|
23117
|
+
const id2 = makeGroupId(rawId);
|
|
23118
|
+
if (!id2.ok) return err(`groups[${i}]: ${describeMapError(id2.error)}`);
|
|
23119
|
+
const removed = removeGroup(next, id2.value);
|
|
22541
23120
|
if (!removed.ok) return err(`groups[${i}]: ${describeMapError(removed.error)}`);
|
|
22542
23121
|
next = removed.value;
|
|
22543
23122
|
}
|
|
22544
23123
|
for (const [i, rawId] of (input.lanes ?? []).entries()) {
|
|
22545
|
-
const
|
|
22546
|
-
if (!
|
|
22547
|
-
const removed = removeLane(next,
|
|
23124
|
+
const id2 = makeLaneId(rawId);
|
|
23125
|
+
if (!id2.ok) return err(`lanes[${i}]: ${describeMapError(id2.error)}`);
|
|
23126
|
+
const removed = removeLane(next, id2.value);
|
|
22548
23127
|
if (!removed.ok) return err(`lanes[${i}]: ${describeMapError(removed.error)}`);
|
|
22549
23128
|
next = removed.value;
|
|
22550
23129
|
}
|
|
22551
23130
|
for (const [i, rawId] of (input.layers ?? []).entries()) {
|
|
22552
|
-
const
|
|
22553
|
-
if (!
|
|
22554
|
-
const removed = removeLayer(next,
|
|
23131
|
+
const id2 = makeLayerId(rawId);
|
|
23132
|
+
if (!id2.ok) return err(`layers[${i}]: ${describeMapError(id2.error)}`);
|
|
23133
|
+
const removed = removeLayer(next, id2.value);
|
|
22555
23134
|
if (!removed.ok) return err(`layers[${i}]: ${describeMapError(removed.error)}`);
|
|
22556
23135
|
next = removed.value;
|
|
22557
23136
|
}
|
|
@@ -22560,105 +23139,280 @@ function applyRemove(map, input) {
|
|
|
22560
23139
|
function summarize(map) {
|
|
22561
23140
|
const byStatus = { planned: 0, "in-progress": 0, done: 0, regressed: 0 };
|
|
22562
23141
|
for (const n of map.nodes) byStatus[n.status]++;
|
|
22563
|
-
const statusPart = Object.entries(byStatus).filter(([, count]) => count > 0).map(([
|
|
23142
|
+
const statusPart = Object.entries(byStatus).filter(([, count]) => count > 0).map(([status2, count]) => `${count} ${status2}`).join(", ");
|
|
22564
23143
|
return `map now: ${map.layers.length} layer(s), ${map.nodes.length} node(s)` + (statusPart ? ` [${statusPart}]` : "") + (map.groups.length > 0 ? `, ${map.groups.length} group(s)` : "") + (map.lanes.length > 0 ? `, ${map.lanes.length} lane(s)` : "") + `, ${map.edges.length} edge(s)` + (map.kind !== void 0 && map.kind !== "dev" ? ` (${map.kind})` : "");
|
|
22565
23144
|
}
|
|
22566
23145
|
|
|
22567
23146
|
// src/server/server.ts
|
|
22568
23147
|
var SERVER_NAME = "mellos-mapping";
|
|
22569
|
-
var SERVER_VERSION = "0.
|
|
22570
|
-
var
|
|
22571
|
-
var
|
|
22572
|
-
|
|
22573
|
-
|
|
22574
|
-
var
|
|
22575
|
-
|
|
22576
|
-
|
|
22577
|
-
|
|
22578
|
-
|
|
22579
|
-
|
|
22580
|
-
|
|
22581
|
-
|
|
22582
|
-
var
|
|
22583
|
-
|
|
22584
|
-
);
|
|
23148
|
+
var SERVER_VERSION = "0.20.2";
|
|
23149
|
+
var TITLE_MAX = 120;
|
|
23150
|
+
var LABEL_MAX = 60;
|
|
23151
|
+
var DETAIL_MAX = 600;
|
|
23152
|
+
var EVIDENCE_MAX = 200;
|
|
23153
|
+
var EDGE_LABEL_MAX = 80;
|
|
23154
|
+
function id(description) {
|
|
23155
|
+
return external_exports.string().regex(ID_RULE, ID_RULE_TEXT).describe(description);
|
|
23156
|
+
}
|
|
23157
|
+
var PAGE_DESCRIPTION = "page (parallel map) this call targets; omit for the default page. One effort = one page: start a NEW effort on its own page named after the effort, so concurrent sessions never write over each other and the pane can switch between pages.";
|
|
23158
|
+
function page() {
|
|
23159
|
+
return id(PAGE_DESCRIPTION).optional();
|
|
23160
|
+
}
|
|
23161
|
+
var STATUS_VOCABULARY = "planned = ghost on the map; in-progress = spinner; done = verified; regressed = was done, now broken";
|
|
23162
|
+
function status(note2) {
|
|
23163
|
+
return external_exports.enum(NODE_STATUSES).describe(`${note2}. ${STATUS_VOCABULARY}`);
|
|
23164
|
+
}
|
|
23165
|
+
function nodeKind() {
|
|
23166
|
+
return id(
|
|
23167
|
+
"node kind rendered as a glyph prefix. Known: selector | sequence | parallel | decorator | condition | action (behavior trees); source | transform | sink (dataflow); service | db | queue | ui (architecture). Unknown kinds are kept and shown in the detail panel."
|
|
23168
|
+
);
|
|
23169
|
+
}
|
|
23170
|
+
function mapKind() {
|
|
23171
|
+
return external_exports.enum(MAP_KINDS).describe(
|
|
23172
|
+
"diagram kind. dev (default) = the live progress ledger with status skins. The rest are documentation diagrams rendered neutrally: architecture (layered components; also fits call graphs and module dependencies), dataflow (source\u2192transform\u2192sink, stages as layers), behavior-tree (root on top, leaves at the bottom; also fits mind maps and WBS), sequence (classic call/return: rank = time step with rank 0 = EARLIEST, drawn top-down; declare lanes as participants and make every call AND every return its own event node in the acting participant's lane, edges labeled with the message). State machines are unsupported: cycles cannot enter a Mellos map."
|
|
23173
|
+
);
|
|
23174
|
+
}
|
|
23175
|
+
function rank() {
|
|
23176
|
+
return external_exports.number().int().min(RANK_MIN).max(RANK_MAX).describe(`${RANK_RULE_TEXT}; must be unique among the map's bands`);
|
|
23177
|
+
}
|
|
23178
|
+
function edgeEnds() {
|
|
23179
|
+
return {
|
|
23180
|
+
from: id("the node that USES the other (must live on a higher layer)"),
|
|
23181
|
+
to: id("the node being used (must live on a strictly lower layer)")
|
|
23182
|
+
};
|
|
23183
|
+
}
|
|
23184
|
+
var NO_CONTROLS = /^[^\u0000-\u001f\u007f-\u009f]*$/;
|
|
23185
|
+
var NO_CONTROLS_TEXT = "one line of text; control characters (ESC, newline, tab) are not allowed";
|
|
23186
|
+
var NO_CONTROLS_BUT_BREAKS = /^[^\u0000-\u0008\u000b-\u001f\u007f-\u009f]*$/;
|
|
23187
|
+
var NO_CONTROLS_BUT_BREAKS_TEXT = "text with optional newlines (\\n) and tabs; other control characters (ESC, BEL, CR) are not allowed";
|
|
23188
|
+
function line(max, description) {
|
|
23189
|
+
return external_exports.string().min(1).max(max).regex(NO_CONTROLS, NO_CONTROLS_TEXT).describe(description);
|
|
23190
|
+
}
|
|
23191
|
+
function note(max, description) {
|
|
23192
|
+
return external_exports.string().min(1).max(max).regex(NO_CONTROLS_BUT_BREAKS, NO_CONTROLS_BUT_BREAKS_TEXT).describe(description);
|
|
23193
|
+
}
|
|
23194
|
+
function closed(shape) {
|
|
23195
|
+
return external_exports.object(shape).strict();
|
|
23196
|
+
}
|
|
23197
|
+
var DEFAULT_PAGE_NAME = "(default)";
|
|
23198
|
+
var DEFAULT_PAGE_ABSENT = "(default: absent)";
|
|
23199
|
+
function pagesLine(stateFile, shown) {
|
|
23200
|
+
const files = listPageFiles(stateFile);
|
|
23201
|
+
const named = files.map((f) => pageIdOfFile(stateFile, f)).filter((p) => p !== void 0);
|
|
23202
|
+
const hasDefault = files.length > named.length;
|
|
23203
|
+
const known = [hasDefault ? DEFAULT_PAGE_NAME : DEFAULT_PAGE_ABSENT, ...named];
|
|
23204
|
+
return `pages: ${known.join(", ")} \u2014 this view: ${shown ?? DEFAULT_PAGE_NAME}`;
|
|
23205
|
+
}
|
|
23206
|
+
function pageName(page2) {
|
|
23207
|
+
return page2 ?? DEFAULT_PAGE_NAME;
|
|
23208
|
+
}
|
|
23209
|
+
function paneLine(stateFile, touched) {
|
|
23210
|
+
const viewers = readLiveViewers(stateFile, Date.now());
|
|
23211
|
+
if (viewers.length === 0) {
|
|
23212
|
+
return `pane: CLOSED \u2014 nobody is seeing this map. Open it with mmap_open {page: ${touched === void 0 ? "(omit for the default page)" : `"${touched}"`}} and do not ask first: a user with a mapping policy has already said they want the picture.`;
|
|
23213
|
+
}
|
|
23214
|
+
if (viewers.some((v) => v.page === touched)) return "pane: open on this page \u2014 the user is seeing this.";
|
|
23215
|
+
const elsewhere = [...new Set(viewers.map((v) => pageName(v.page)))].join(", ");
|
|
23216
|
+
if (viewers.some((v) => v.follow)) {
|
|
23217
|
+
return `pane: open on ${elsewhere}, auto-follow on \u2014 it lands on this page within a second.`;
|
|
23218
|
+
}
|
|
23219
|
+
return `pane: open on ${elsewhere}, auto-follow OFF \u2014 the user pinned that page, so this change is NOT on their screen. Tell them rather than switching it behind them; mmap_open {page} retargets the pane if they want it moved.`;
|
|
23220
|
+
}
|
|
23221
|
+
var LAUNCH_TIMEOUT_MS = 45e3;
|
|
23222
|
+
var PANE_REPORT_TIMEOUT_MS = 8e3;
|
|
23223
|
+
var PANE_REPORT_POLL_MS = 250;
|
|
23224
|
+
function launcherPath(moduleUrl) {
|
|
23225
|
+
return join2(dirname2(dirname2(fileURLToPath(moduleUrl))), "scripts", "open-pane.mjs");
|
|
23226
|
+
}
|
|
23227
|
+
function projectDirOf(stateFile) {
|
|
23228
|
+
return dirname2(dirname2(stateFile));
|
|
23229
|
+
}
|
|
23230
|
+
function launcherArgs(projectDir, page2, window) {
|
|
23231
|
+
const args = [projectDir];
|
|
23232
|
+
if (page2 !== void 0) args.push("--page", page2);
|
|
23233
|
+
if (window) args.push("--window");
|
|
23234
|
+
return args;
|
|
23235
|
+
}
|
|
23236
|
+
function runLauncher(script, args) {
|
|
23237
|
+
return new Promise((resolve) => {
|
|
23238
|
+
const child = spawn(process.execPath, [script, ...args], {
|
|
23239
|
+
windowsHide: true,
|
|
23240
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
23241
|
+
});
|
|
23242
|
+
let output = "";
|
|
23243
|
+
const collect = (chunk) => {
|
|
23244
|
+
output += chunk.toString("utf8");
|
|
23245
|
+
};
|
|
23246
|
+
child.stdout.on("data", collect);
|
|
23247
|
+
child.stderr.on("data", collect);
|
|
23248
|
+
const abandon = setTimeout(() => child.kill(), LAUNCH_TIMEOUT_MS);
|
|
23249
|
+
child.on("error", (e) => {
|
|
23250
|
+
clearTimeout(abandon);
|
|
23251
|
+
resolve({ ok: false, output: e.message });
|
|
23252
|
+
});
|
|
23253
|
+
child.on("close", (code) => {
|
|
23254
|
+
clearTimeout(abandon);
|
|
23255
|
+
resolve({ ok: code === 0, output: output.trim() });
|
|
23256
|
+
});
|
|
23257
|
+
});
|
|
23258
|
+
}
|
|
23259
|
+
function paneShows(viewers, page2) {
|
|
23260
|
+
return page2 === void 0 ? viewers.length > 0 : viewers.some((v) => v.page === page2);
|
|
23261
|
+
}
|
|
23262
|
+
async function awaitPane(stateFile, page2, deadlineMs) {
|
|
23263
|
+
for (; ; ) {
|
|
23264
|
+
const viewers = readLiveViewers(stateFile, Date.now());
|
|
23265
|
+
if (paneShows(viewers, page2)) return viewers;
|
|
23266
|
+
if (Date.now() >= deadlineMs) return viewers;
|
|
23267
|
+
await new Promise((r) => setTimeout(r, PANE_REPORT_POLL_MS));
|
|
23268
|
+
}
|
|
23269
|
+
}
|
|
23270
|
+
function openOutcome(run, viewers, page2) {
|
|
23271
|
+
if (!run.ok) {
|
|
23272
|
+
return `could not open the pane: ${run.output === "" ? "the launcher failed without saying why" : run.output}
|
|
23273
|
+
Relay this to the user \u2014 on a machine without Windows Terminal the map is opened by running the watcher in any second terminal or tmux split (see the plugin README).`;
|
|
23274
|
+
}
|
|
23275
|
+
if (paneShows(viewers, page2)) {
|
|
23276
|
+
return `pane: open and showing ${pageName(page2)} \u2014 the user can see the map now.
|
|
23277
|
+
${run.output}`;
|
|
23278
|
+
}
|
|
23279
|
+
if (viewers.length > 0) {
|
|
23280
|
+
const elsewhere = [...new Set(viewers.map((v) => pageName(v.page)))].join(", ");
|
|
23281
|
+
return `pane: open, but it reports ${elsewhere} rather than ${pageName(page2)}. With auto-follow on it lands there on your next write; with follow off the user is holding that page on purpose.
|
|
23282
|
+
` + run.output;
|
|
23283
|
+
}
|
|
23284
|
+
return `the launcher succeeded but no pane has reported in within ${PANE_REPORT_TIMEOUT_MS / 1e3}s. It may still be starting; the \`pane:\` line on your next write says whether it made it.
|
|
23285
|
+
` + run.output;
|
|
23286
|
+
}
|
|
22585
23287
|
function text(s, isError = false) {
|
|
22586
23288
|
return { content: [{ type: "text", text: s }], ...isError ? { isError: true } : {} };
|
|
22587
23289
|
}
|
|
23290
|
+
function saveFailed(error2) {
|
|
23291
|
+
return text(`save failed, nothing changed (retry): ${describeStoreError(error2)}`, true);
|
|
23292
|
+
}
|
|
22588
23293
|
function loadOrEmpty(stateFile) {
|
|
22589
23294
|
const loaded = loadMapFile(stateFile);
|
|
22590
23295
|
if (loaded.ok) return { ok: true, value: loaded.value };
|
|
22591
23296
|
if (loaded.error.kind === "not-found") return { ok: true, value: EMPTY_MAP };
|
|
22592
23297
|
return { ok: false, error: describeStoreError(loaded.error) };
|
|
22593
23298
|
}
|
|
22594
|
-
function buildServer(stateFile) {
|
|
23299
|
+
function buildServer(stateFile, userConfigFile) {
|
|
22595
23300
|
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
|
|
22596
|
-
const
|
|
22597
|
-
const
|
|
22598
|
-
|
|
23301
|
+
const projectConfigFile = configFilePath(stateFile);
|
|
23302
|
+
const fileOf = (page2) => pageFilePath(stateFile, page2);
|
|
23303
|
+
const mutate = (page2, apply) => {
|
|
23304
|
+
const file = fileOf(page2);
|
|
22599
23305
|
const current = loadOrEmpty(file);
|
|
22600
23306
|
if (!current.ok) return text(current.error, true);
|
|
22601
23307
|
const applied = apply(current.value);
|
|
22602
23308
|
if (!applied.ok) return text(`refused (nothing changed): ${applied.error}`, true);
|
|
22603
|
-
saveMapFile(file, applied.value);
|
|
22604
|
-
|
|
23309
|
+
const saved = saveMapFile(file, applied.value);
|
|
23310
|
+
if (!saved.ok) return saveFailed(saved.error);
|
|
23311
|
+
return text(summarize(applied.value) + (page2 !== void 0 ? ` [page: ${page2}]` : ""));
|
|
23312
|
+
};
|
|
23313
|
+
const withPane = (result, page2) => result.isError === true ? result : text(`${result.content[0]?.text ?? ""}
|
|
23314
|
+
${paneLine(stateFile, page2)}`);
|
|
23315
|
+
const knownPages = () => listPageFiles(stateFile).map((f) => pageIdOfFile(stateFile, f)).filter((p) => p !== void 0);
|
|
23316
|
+
const refusePageDeletion = (pages, target) => {
|
|
23317
|
+
if (target !== void 0 && pages.includes(target)) {
|
|
23318
|
+
return text(
|
|
23319
|
+
`refused (nothing changed): pages includes "${target}", the page this call targets \u2014 one call must not edit a map it is deleting. Delete it from a call that does not target it.`,
|
|
23320
|
+
true
|
|
23321
|
+
);
|
|
23322
|
+
}
|
|
23323
|
+
const known = knownPages();
|
|
23324
|
+
const unknown2 = pages.filter((p) => !known.includes(p));
|
|
23325
|
+
if (unknown2.length > 0) {
|
|
23326
|
+
return text(
|
|
23327
|
+
`refused (nothing changed): no page named ${unknown2.map((p) => `"${p}"`).join(", ")}. This project's named pages: ${known.length > 0 ? known.join(", ") : "(none)"}.`,
|
|
23328
|
+
true
|
|
23329
|
+
);
|
|
23330
|
+
}
|
|
23331
|
+
return void 0;
|
|
23332
|
+
};
|
|
23333
|
+
const deletePages = (pages, summary) => {
|
|
23334
|
+
const deleted = [];
|
|
23335
|
+
const failed = [];
|
|
23336
|
+
for (const p of pages) {
|
|
23337
|
+
const removed = deletePageFile(pageFilePath(stateFile, p));
|
|
23338
|
+
if (removed.ok) deleted.push(p);
|
|
23339
|
+
else failed.push(`${p} (${describeStoreError(removed.error)})`);
|
|
23340
|
+
}
|
|
23341
|
+
const gone = `deleted page(s): ${deleted.length > 0 ? deleted.join(", ") : "(none)"}`;
|
|
23342
|
+
if (failed.length === 0) return text(`${summary}${gone}`);
|
|
23343
|
+
return text(
|
|
23344
|
+
`${summary}${gone}; could NOT delete: ${failed.join("; ")}. Deleting files is not a transaction: what is named deleted above is gone for good, and only the failures are worth retrying.`,
|
|
23345
|
+
true
|
|
23346
|
+
);
|
|
22605
23347
|
};
|
|
22606
23348
|
const setupNudge = () => {
|
|
22607
|
-
const
|
|
22608
|
-
if (!
|
|
22609
|
-
note: ${describeStoreError(
|
|
22610
|
-
if (
|
|
22611
|
-
return "\nnote: mapping policy
|
|
23349
|
+
const scopes = effectiveMappingPolicy(projectConfigFile, userConfigFile);
|
|
23350
|
+
if (!scopes.ok) return `
|
|
23351
|
+
note: ${describeStoreError(scopes.error)} \u2014 fix it or rerun setup (mmap_setup).`;
|
|
23352
|
+
if (scopes.value.effective !== void 0) return "";
|
|
23353
|
+
return "\nnote: no mapping policy has been chosen yet. Ask the user when maps should open \u2014 " + MAPPING_POLICIES.map((p) => `${p} (${describeMappingPolicy(p)})`).join("; ") + " \u2014 then record the answer with mmap_setup. It is asked once ever, not once per project.";
|
|
22612
23354
|
};
|
|
22613
23355
|
server.registerTool(
|
|
22614
23356
|
"mmap_declare",
|
|
22615
23357
|
{
|
|
22616
23358
|
title: "Declare map structure",
|
|
22617
|
-
description: "Grow the Mellos map: set the title and diagram kind, add layer bands, lanes and groups (labeled subsystems within ONE band \u2014 declare them when a single band grows crowded, roughly five or more nodes in that band; a group must be a strict subset of its band, and a map spread thin across many bands needs none), add nodes, add dependency edges. Declare the whole ghost design up front, then grow it as understanding deepens. Edges must point strictly downward (a node may only use nodes on lower layers); the batch is all-or-nothing.",
|
|
22618
|
-
inputSchema: {
|
|
22619
|
-
page:
|
|
22620
|
-
title:
|
|
22621
|
-
kind:
|
|
23359
|
+
description: "Grow the Mellos map: set the title and diagram kind, add layer bands, lanes and groups (labeled subsystems within ONE band \u2014 declare them when a single band grows crowded, roughly five or more nodes in that band; a group must be a strict subset of its band, and a map spread thin across many bands needs none), add nodes, add dependency edges. Declare the whole ghost design up front, then grow it as understanding deepens. Edges must point strictly downward (a node may only use nodes on lower layers); the batch is all-or-nothing. The title lives here and only here: pass it again to replace it, or null to remove it. Revising what already exists (moving, renaming, relabeling, clearing) is mmap_update.",
|
|
23360
|
+
inputSchema: closed({
|
|
23361
|
+
page: page(),
|
|
23362
|
+
title: line(TITLE_MAX, "map title, e.g. the feature being built; null removes it").nullable().optional(),
|
|
23363
|
+
kind: mapKind().optional(),
|
|
22622
23364
|
lanes: external_exports.array(
|
|
22623
|
-
|
|
22624
|
-
id:
|
|
22625
|
-
label:
|
|
23365
|
+
closed({
|
|
23366
|
+
id: id("stable kebab-case identifier of the lane"),
|
|
23367
|
+
label: line(LABEL_MAX, "column name, e.g. a sequence participant")
|
|
22626
23368
|
})
|
|
22627
23369
|
).optional().describe("vertical columns crossing all bands; declaration order = left-to-right"),
|
|
22628
23370
|
layers: external_exports.array(
|
|
22629
|
-
|
|
22630
|
-
id:
|
|
22631
|
-
name:
|
|
22632
|
-
rank:
|
|
23371
|
+
closed({
|
|
23372
|
+
id: id("stable kebab-case identifier of the band"),
|
|
23373
|
+
name: line(LABEL_MAX, "display name of the band"),
|
|
23374
|
+
rank: rank()
|
|
22633
23375
|
})
|
|
22634
23376
|
).optional(),
|
|
22635
23377
|
groups: external_exports.array(
|
|
22636
|
-
|
|
22637
|
-
id:
|
|
22638
|
-
label:
|
|
22639
|
-
layer:
|
|
23378
|
+
closed({
|
|
23379
|
+
id: id("stable kebab-case identifier of the group"),
|
|
23380
|
+
label: line(LABEL_MAX, "subsystem name shown at the far zoom"),
|
|
23381
|
+
layer: id("band this group clusters; members must live on the same band")
|
|
22640
23382
|
})
|
|
22641
23383
|
).optional(),
|
|
22642
23384
|
nodes: external_exports.array(
|
|
22643
|
-
|
|
22644
|
-
id:
|
|
22645
|
-
label:
|
|
22646
|
-
layer:
|
|
22647
|
-
status:
|
|
22648
|
-
|
|
22649
|
-
|
|
22650
|
-
|
|
22651
|
-
|
|
22652
|
-
|
|
23385
|
+
closed({
|
|
23386
|
+
id: id("stable kebab-case identifier of the node"),
|
|
23387
|
+
label: line(LABEL_MAX, "display label inside the box"),
|
|
23388
|
+
layer: id("id of the band this node lives in"),
|
|
23389
|
+
status: status("defaults to planned").optional(),
|
|
23390
|
+
evidence: line(
|
|
23391
|
+
EVIDENCE_MAX,
|
|
23392
|
+
"how an already-verified node was verified; for regressed: what broke. Declaring a node straight to done needs it as much as updating one does."
|
|
23393
|
+
).optional(),
|
|
23394
|
+
detail: note(
|
|
23395
|
+
DETAIL_MAX,
|
|
23396
|
+
"design notes shown in the pane detail panel: responsibility, contract, key decisions"
|
|
23397
|
+
).optional(),
|
|
23398
|
+
group: id("same-band group this node belongs to").optional(),
|
|
23399
|
+
kind: nodeKind().optional(),
|
|
23400
|
+
lane: id("lane (column) this node belongs to").optional(),
|
|
23401
|
+
submap: id(
|
|
22653
23402
|
"page slug of this node's child map \u2014 the pane badges the node \u229E and double-click dives in. Declare the child page separately. Create a sub-map only when the node's internals genuinely deserve their own picture; most nodes need none."
|
|
22654
|
-
)
|
|
23403
|
+
).optional()
|
|
22655
23404
|
})
|
|
22656
23405
|
).optional(),
|
|
22657
|
-
edges: external_exports.array(
|
|
22658
|
-
|
|
23406
|
+
edges: external_exports.array(
|
|
23407
|
+
closed({
|
|
23408
|
+
...edgeEnds(),
|
|
23409
|
+
label: line(EDGE_LABEL_MAX, "what flows along the edge").optional()
|
|
23410
|
+
})
|
|
23411
|
+
).optional()
|
|
23412
|
+
})
|
|
22659
23413
|
},
|
|
22660
23414
|
(input) => {
|
|
22661
|
-
const result = mutate(input.page, (map) => applyDeclare(map, input));
|
|
23415
|
+
const result = withPane(mutate(input.page, (map) => applyDeclare(map, input)), input.page);
|
|
22662
23416
|
if (result.isError === true) return result;
|
|
22663
23417
|
const nudge = setupNudge();
|
|
22664
23418
|
return nudge === "" ? result : text((result.content[0]?.text ?? "") + nudge);
|
|
@@ -22667,83 +23421,158 @@ note: ${describeStoreError(policy.error)} \u2014 fix it or rerun setup (mmap_set
|
|
|
22667
23421
|
server.registerTool(
|
|
22668
23422
|
"mmap_update",
|
|
22669
23423
|
{
|
|
22670
|
-
title: "Record progress
|
|
22671
|
-
description: "
|
|
22672
|
-
inputSchema: {
|
|
22673
|
-
page:
|
|
23424
|
+
title: "Record progress and revise the map",
|
|
23425
|
+
description: "The revision tool, all-or-nothing. Record progress on nodes: in-progress when starting a node (the pane spins), done with evidence when its verification passes, regressed with evidence when a done node breaks. Revise what the ghost design got wrong: move a node to another band, join or leave a group or lane, rename a band (or re-rank it, which reorders the whole map), relabel a group or a lane. Every clearable field takes null to empty it \u2014 that is how a field is cleared, never an empty string. Bands, groups and lanes are applied before the node updates, and within one node update `layer` moves the node before its other fields. The map is a ledger: report honestly, it never blocks you.",
|
|
23426
|
+
inputSchema: closed({
|
|
23427
|
+
page: page(),
|
|
22674
23428
|
updates: external_exports.array(
|
|
22675
|
-
|
|
22676
|
-
id:
|
|
22677
|
-
status:
|
|
22678
|
-
label:
|
|
22679
|
-
evidence:
|
|
22680
|
-
detail:
|
|
22681
|
-
|
|
22682
|
-
|
|
22683
|
-
|
|
22684
|
-
|
|
23429
|
+
closed({
|
|
23430
|
+
id: id("id of the node to update"),
|
|
23431
|
+
status: status("the status to record").optional(),
|
|
23432
|
+
label: line(LABEL_MAX, "new display label inside the box").optional(),
|
|
23433
|
+
evidence: line(EVIDENCE_MAX, "for done: how it was verified; for regressed: what broke; null clears it").nullable().optional(),
|
|
23434
|
+
detail: note(
|
|
23435
|
+
DETAIL_MAX,
|
|
23436
|
+
"design notes shown in the pane detail panel: responsibility, contract, key decisions; null clears them"
|
|
23437
|
+
).nullable().optional(),
|
|
23438
|
+
layer: id(
|
|
23439
|
+
"move the node to this band; applied before this item's other fields, so a node can move and join a group on the new band in one item. Every edge touching it must still point strictly downward, and a grouped node may only move to its group's band."
|
|
23440
|
+
).optional(),
|
|
23441
|
+
group: id("join this same-band group; null leaves the current group").nullable().optional(),
|
|
23442
|
+
kind: nodeKind().nullable().optional(),
|
|
23443
|
+
lane: id("join this lane; null leaves the current lane").nullable().optional(),
|
|
23444
|
+
submap: id("link a child map page by slug; null unlinks it").nullable().optional()
|
|
22685
23445
|
})
|
|
22686
|
-
).min(1)
|
|
22687
|
-
|
|
23446
|
+
).min(1).optional(),
|
|
23447
|
+
layers: external_exports.array(
|
|
23448
|
+
closed({
|
|
23449
|
+
id: id("id of the band to revise"),
|
|
23450
|
+
name: line(LABEL_MAX, "new display name of the band").optional(),
|
|
23451
|
+
rank: rank().optional()
|
|
23452
|
+
})
|
|
23453
|
+
).min(1).optional().describe("rename and/or re-rank existing bands; an item must carry a name, a rank, or both"),
|
|
23454
|
+
groups: external_exports.array(closed({ id: id("id of the group to relabel"), label: line(LABEL_MAX, "new subsystem name") })).min(1).optional().describe("relabel existing groups; membership and band are untouched"),
|
|
23455
|
+
lanes: external_exports.array(closed({ id: id("id of the lane to relabel"), label: line(LABEL_MAX, "new column name") })).min(1).optional().describe("relabel existing lanes; order and membership are untouched")
|
|
23456
|
+
})
|
|
22688
23457
|
},
|
|
22689
|
-
(input) => mutate(input.page, (map) => applyUpdate(map, input))
|
|
23458
|
+
(input) => withPane(mutate(input.page, (map) => applyUpdate(map, input)), input.page)
|
|
22690
23459
|
);
|
|
22691
23460
|
server.registerTool(
|
|
22692
23461
|
"mmap_remove",
|
|
22693
23462
|
{
|
|
22694
23463
|
title: "Revise the map",
|
|
22695
|
-
description:
|
|
22696
|
-
inputSchema: {
|
|
22697
|
-
page:
|
|
22698
|
-
edges: external_exports.array(
|
|
22699
|
-
nodes: external_exports.array(
|
|
22700
|
-
groups: external_exports.array(
|
|
22701
|
-
lanes: external_exports.array(
|
|
22702
|
-
layers: external_exports.array(
|
|
22703
|
-
|
|
23464
|
+
description: 'Remove edges, nodes, groups and empty layer bands (in that order, all-or-nothing). Removing a node also removes every edge touching it; removing a group merely ungroups its members. Use when the ghost design turns out wrong \u2014 the map is a hypothesis, revising it is honest work. `pages` is the other scale: it DELETES whole page files, so a finished effort can be cleaned up instead of accumulating tabs forever. A bare `{pages: ["slug"]}` with no other field is the normal form; combined with map edits, the edits are applied first and the pages are deleted after. The deletion is permanent and cannot be undone, so delete only pages whose effort is over \u2014 and only ever with the user behind it. An unknown slug is refused with the project\'s real page list (naming a page that does not exist is a typo, not a request). The default page has no slug and is not deletable here. A node elsewhere still pointing at a deleted page with `submap` stays legal \u2014 a submap reference has no existence invariant \u2014 but it has nowhere to dive until the page comes back.',
|
|
23465
|
+
inputSchema: closed({
|
|
23466
|
+
page: page(),
|
|
23467
|
+
edges: external_exports.array(closed(edgeEnds())).optional(),
|
|
23468
|
+
nodes: external_exports.array(id("id of the node to remove, with every edge touching it")).optional(),
|
|
23469
|
+
groups: external_exports.array(id("id of the group to remove; members stay, merely ungrouped")).optional(),
|
|
23470
|
+
lanes: external_exports.array(id("id of the lane to remove; members stay, merely off-lane")).optional(),
|
|
23471
|
+
layers: external_exports.array(id("id of the band to remove; it must hold no nodes and no groups")).optional(),
|
|
23472
|
+
pages: external_exports.array(
|
|
23473
|
+
id(
|
|
23474
|
+
"slug of a page whose WHOLE map file is deleted \u2014 the page and everything drawn on it. Not the page this same call targets with `page`."
|
|
23475
|
+
)
|
|
23476
|
+
).optional().describe("pages to delete entirely, after this call's map edits; permanent")
|
|
23477
|
+
})
|
|
22704
23478
|
},
|
|
22705
|
-
(input) =>
|
|
23479
|
+
(input) => {
|
|
23480
|
+
if (input.pages === void 0) return withPane(mutate(input.page, (map) => applyRemove(map, input)), input.page);
|
|
23481
|
+
const refusal = refusePageDeletion(input.pages, input.page);
|
|
23482
|
+
if (refusal !== void 0) return refusal;
|
|
23483
|
+
const editsAnything = (input.edges?.length ?? 0) + (input.nodes?.length ?? 0) + (input.groups?.length ?? 0) + (input.lanes?.length ?? 0) + (input.layers?.length ?? 0) > 0;
|
|
23484
|
+
let summary = "";
|
|
23485
|
+
if (editsAnything) {
|
|
23486
|
+
const edited = mutate(input.page, (map) => applyRemove(map, input));
|
|
23487
|
+
if (edited.isError === true) return edited;
|
|
23488
|
+
summary = `${edited.content[0]?.text ?? ""}
|
|
23489
|
+
`;
|
|
23490
|
+
}
|
|
23491
|
+
return withPane(deletePages(input.pages, summary), input.page);
|
|
23492
|
+
}
|
|
22706
23493
|
);
|
|
22707
23494
|
server.registerTool(
|
|
22708
23495
|
"mmap_setup",
|
|
22709
23496
|
{
|
|
22710
23497
|
title: "Configure when maps open",
|
|
22711
|
-
description:
|
|
22712
|
-
inputSchema: {
|
|
22713
|
-
policy: external_exports.enum(MAPPING_POLICIES).optional().describe("the user's choice to persist; omit to read the current policy")
|
|
22714
|
-
|
|
23498
|
+
description: 'Get or set the mapping policy \u2014 WHEN the assistant opens a Mellos map. Call with no arguments to read it: the reply names the policy chosen for the USER (every project), the one this PROJECT overrides it with if any, and which of them is in effect. If it reports "not set", ask the USER to choose (never pick for them): always = ' + describeMappingPolicy("always") + "; complex = " + describeMappingPolicy("complex") + "; on-request = " + describeMappingPolicy("on-request") + '. Then call again with their choice to persist it. It defaults to user scope, which is the normal one \u2014 the question is about how someone works, so it is asked once ever, not once per repository. Pass scope: "project" only when the user wants THIS project to differ from that habit. The policy guides you; it never blocks the tools, and an explicit user request for a map always wins.',
|
|
23499
|
+
inputSchema: closed({
|
|
23500
|
+
policy: external_exports.enum(MAPPING_POLICIES).optional().describe("the user's choice to persist; omit to read the current policy"),
|
|
23501
|
+
scope: external_exports.enum(POLICY_SCOPES).optional().describe(
|
|
23502
|
+
'where to record the choice: "user" (default) applies to every project this user opens; "project" overrides that for this project alone. Ignored when reading.'
|
|
23503
|
+
)
|
|
23504
|
+
})
|
|
22715
23505
|
},
|
|
22716
23506
|
(input) => {
|
|
23507
|
+
const scope = input.scope ?? "user";
|
|
23508
|
+
const configFile = scope === "project" ? projectConfigFile : userConfigFile;
|
|
22717
23509
|
if (input.policy !== void 0) {
|
|
22718
23510
|
const policy = input.policy;
|
|
22719
|
-
saveMappingPolicy(
|
|
22720
|
-
|
|
23511
|
+
const saved = saveMappingPolicy(configFile, policy);
|
|
23512
|
+
if (!saved.ok) return saveFailed(saved.error);
|
|
23513
|
+
const reach = scope === "user" ? 'applies to EVERY project this user opens; a single project can still override it with mmap_setup {policy, scope: "project"}' : "applies to THIS project only, overriding the user-level choice";
|
|
23514
|
+
return text(
|
|
23515
|
+
`mapping policy set (${scope} scope): ${policy} \u2014 ${describeMappingPolicy(policy)}. ${reach}. [${configFile}]`
|
|
23516
|
+
);
|
|
22721
23517
|
}
|
|
22722
|
-
const
|
|
22723
|
-
if (!
|
|
22724
|
-
|
|
23518
|
+
const scopes = effectiveMappingPolicy(projectConfigFile, userConfigFile);
|
|
23519
|
+
if (!scopes.ok) return text(describeStoreError(scopes.error), true);
|
|
23520
|
+
const { user, project, effective, source } = scopes.value;
|
|
23521
|
+
const said = (p) => p === void 0 ? "not set" : p;
|
|
23522
|
+
const heading = `mapping policy \u2014 user: ${said(user)}; project: ${said(project)}`;
|
|
23523
|
+
if (effective === void 0) {
|
|
22725
23524
|
return text(
|
|
22726
|
-
|
|
23525
|
+
`${heading}. Nobody has chosen yet. Ask the user to choose one of: ` + MAPPING_POLICIES.map((p) => `${p} (${describeMappingPolicy(p)})`).join("; ") + " \u2014 then call mmap_setup with their choice (scope defaults to user, which is what you want: the question is asked once ever). Until then act as complex."
|
|
22727
23526
|
);
|
|
22728
23527
|
}
|
|
22729
|
-
return text(
|
|
23528
|
+
return text(`${heading}. In effect: ${effective} (${source} scope) \u2014 ${describeMappingPolicy(effective)}`);
|
|
22730
23529
|
}
|
|
22731
23530
|
);
|
|
22732
23531
|
server.registerTool(
|
|
22733
23532
|
"mmap_view",
|
|
22734
23533
|
{
|
|
22735
23534
|
title: "View the current map",
|
|
22736
|
-
description: "Render the current Mellos map as monochrome text \u2014 the same picture the split-pane watcher shows live. Use it to check the map state or to show it inline in conversation.",
|
|
22737
|
-
inputSchema: {
|
|
22738
|
-
page:
|
|
23535
|
+
description: "Render the current Mellos map as monochrome text \u2014 the same picture the split-pane watcher shows live. Use it to check the map state or to show it inline in conversation. Every response ends with a `pages:` line naming the pages this project actually has and which one you are looking at, so this is also how you discover whether a map exists at all and under which slugs \u2014 never probe the files.",
|
|
23536
|
+
inputSchema: closed({
|
|
23537
|
+
page: page(),
|
|
22739
23538
|
zoom: external_exports.number().int().min(ZOOM_MIN).max(ZOOM_MAX).optional().describe("zoom ladder: 1 = detail (notes unfold), 0 = standard (default), -1..-3 = scaled down, -4 = overview glyphs")
|
|
22740
|
-
}
|
|
23539
|
+
})
|
|
22741
23540
|
},
|
|
22742
23541
|
(input) => {
|
|
22743
23542
|
const current = loadOrEmpty(fileOf(input.page));
|
|
22744
23543
|
if (!current.ok) return text(current.error, true);
|
|
22745
23544
|
const zoom = clampZoom(input.zoom ?? 0);
|
|
22746
|
-
|
|
23545
|
+
const picture = renderMap(current.value, { color: false, unicode: true, spinnerFrame: 0, zoom }).join("\n");
|
|
23546
|
+
return text(`${picture}
|
|
23547
|
+
${pagesLine(stateFile, input.page)}
|
|
23548
|
+
${paneLine(stateFile, input.page)}`);
|
|
23549
|
+
}
|
|
23550
|
+
);
|
|
23551
|
+
server.registerTool(
|
|
23552
|
+
"mmap_open",
|
|
23553
|
+
{
|
|
23554
|
+
title: "Open the map pane",
|
|
23555
|
+
description: "Put the live map on the user's screen: a terminal pane beside this conversation that redraws on every write. Call it whenever a result says `pane: CLOSED` \u2014 and do NOT ask permission first, because a user who has set a mapping policy has already said they want to see the map. With a pane already open this RETARGETS it to `page` instead of opening a second one, so it is also how you show the user a particular page when they ask for one. It never closes a pane: taking the map off the screen belongs to the user (the `q` key in the pane, or typing `mmap` in a terminal). The reply says whether a pane actually reported itself in afterwards, not merely that a command was run. Windows Terminal is the supported route; anywhere else it says so and you relay the manual command from the README.",
|
|
23556
|
+
inputSchema: closed({
|
|
23557
|
+
page: id(
|
|
23558
|
+
"page to show first \u2014 the page THIS effort lives on, the same slug you pass to the other tools. Omit only for the default page: without it a fresh pane opens on whichever page was written last, which after a gap is rarely the one under discussion."
|
|
23559
|
+
).optional(),
|
|
23560
|
+
window: external_exports.boolean().optional().describe(
|
|
23561
|
+
`open the map in its own "mellos-mapping" window instead of splitting this conversation's window. Pass it only when the user asked for the map separate (a second monitor, a small screen); the split is the default because the map is meant to sit beside what it describes.`
|
|
23562
|
+
)
|
|
23563
|
+
})
|
|
23564
|
+
},
|
|
23565
|
+
async (input) => {
|
|
23566
|
+
const script = launcherPath(import.meta.url);
|
|
23567
|
+
if (!existsSync2(script)) {
|
|
23568
|
+
return text(
|
|
23569
|
+
`cannot open the pane: the launcher is missing at ${script}. This install is incomplete \u2014 tell the user to reinstall the plugin (a source checkout needs "npm run build").`,
|
|
23570
|
+
true
|
|
23571
|
+
);
|
|
23572
|
+
}
|
|
23573
|
+
const run = await runLauncher(script, launcherArgs(projectDirOf(stateFile), input.page, input.window === true));
|
|
23574
|
+
const viewers = run.ok ? await awaitPane(stateFile, input.page, Date.now() + PANE_REPORT_TIMEOUT_MS) : [];
|
|
23575
|
+
return text(openOutcome(run, viewers, input.page), !run.ok);
|
|
22747
23576
|
}
|
|
22748
23577
|
);
|
|
22749
23578
|
return server;
|
|
@@ -22752,8 +23581,14 @@ function resolveStateFile(env, cwd) {
|
|
|
22752
23581
|
const projectDir = env["MELLOS_MAPPING_CWD"] ?? env["CLAUDE_PROJECT_DIR"] ?? cwd;
|
|
22753
23582
|
return join2(projectDir, STATE_FILE_RELATIVE_PATH);
|
|
22754
23583
|
}
|
|
23584
|
+
function resolveUserConfigFile(home) {
|
|
23585
|
+
return userConfigFilePath(home);
|
|
23586
|
+
}
|
|
22755
23587
|
async function main() {
|
|
22756
|
-
const
|
|
23588
|
+
const stateFile = resolveStateFile(process.env, process.cwd());
|
|
23589
|
+
const userConfigFile = resolveUserConfigFile(homedir());
|
|
23590
|
+
if (migrateLegacyStore(stateFile)) console.error("mellos-mapping: moved the legacy .claude map store to .mellos/ \u2014 commit the move.");
|
|
23591
|
+
const server = buildServer(stateFile, userConfigFile);
|
|
22757
23592
|
await server.connect(new StdioServerTransport());
|
|
22758
23593
|
}
|
|
22759
23594
|
function launchedAsEntry(argv1, moduleUrl) {
|
|
@@ -22775,5 +23610,10 @@ export {
|
|
|
22775
23610
|
SERVER_VERSION,
|
|
22776
23611
|
buildServer,
|
|
22777
23612
|
launchedAsEntry,
|
|
22778
|
-
|
|
23613
|
+
launcherArgs,
|
|
23614
|
+
launcherPath,
|
|
23615
|
+
openOutcome,
|
|
23616
|
+
projectDirOf,
|
|
23617
|
+
resolveStateFile,
|
|
23618
|
+
resolveUserConfigFile
|
|
22779
23619
|
};
|