@sayue_ltr/fleq 1.50.0 → 1.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +47 -5
  3. package/dist/config.js +35 -2
  4. package/dist/dmdata/rest-client.js +58 -3
  5. package/dist/dmdata/telegram-parser.js +37 -59
  6. package/dist/dmdata/ws-client.js +49 -18
  7. package/dist/engine/cli/cli-run.js +71 -1
  8. package/dist/engine/cli/cli.js +12 -0
  9. package/dist/engine/filter/compile-filter.js +21 -0
  10. package/dist/engine/filter/compiler.js +188 -0
  11. package/dist/engine/filter/errors.js +41 -0
  12. package/dist/engine/filter/field-registry.js +78 -0
  13. package/dist/engine/filter/index.js +15 -0
  14. package/dist/engine/filter/parser.js +137 -0
  15. package/dist/engine/filter/rank-maps.js +34 -0
  16. package/dist/engine/filter/tokenizer.js +121 -0
  17. package/dist/engine/filter/type-checker.js +104 -0
  18. package/dist/engine/filter/types.js +2 -0
  19. package/dist/engine/filter-template/pipeline-controller.js +73 -0
  20. package/dist/engine/filter-template/pipeline.js +16 -0
  21. package/dist/engine/messages/display-callbacks.js +7 -0
  22. package/dist/engine/messages/message-router.js +114 -182
  23. package/dist/engine/messages/summary-tracker.js +106 -0
  24. package/dist/engine/messages/telegram-stats.js +103 -0
  25. package/dist/engine/messages/volcano-route-handler.js +122 -0
  26. package/dist/engine/monitor/monitor.js +51 -3
  27. package/dist/engine/monitor/shutdown.js +1 -0
  28. package/dist/engine/notification/notifier.js +16 -1
  29. package/dist/engine/notification/sound-player.js +193 -28
  30. package/dist/engine/presentation/diff-store.js +158 -0
  31. package/dist/engine/presentation/diff-types.js +2 -0
  32. package/dist/engine/presentation/events/from-earthquake.js +53 -0
  33. package/dist/engine/presentation/events/from-eew.js +72 -0
  34. package/dist/engine/presentation/events/from-lg-observation.js +58 -0
  35. package/dist/engine/presentation/events/from-nankai-trough.js +39 -0
  36. package/dist/engine/presentation/events/from-raw.js +35 -0
  37. package/dist/engine/presentation/events/from-seismic-text.js +37 -0
  38. package/dist/engine/presentation/events/from-tsunami.js +51 -0
  39. package/dist/engine/presentation/events/from-volcano.js +88 -0
  40. package/dist/engine/presentation/events/to-presentation-event.js +32 -0
  41. package/dist/engine/presentation/level-helpers.js +118 -0
  42. package/dist/engine/presentation/processors/process-earthquake.js +36 -0
  43. package/dist/engine/presentation/processors/process-eew.js +90 -0
  44. package/dist/engine/presentation/processors/process-lg-observation.js +30 -0
  45. package/dist/engine/presentation/processors/process-message.js +53 -0
  46. package/dist/engine/presentation/processors/process-nankai-trough.js +30 -0
  47. package/dist/engine/presentation/processors/process-raw.js +22 -0
  48. package/dist/engine/presentation/processors/process-seismic-text.js +30 -0
  49. package/dist/engine/presentation/processors/process-tsunami.js +42 -0
  50. package/dist/engine/presentation/processors/process-volcano.js +41 -0
  51. package/dist/engine/presentation/types.js +2 -0
  52. package/dist/engine/startup/config-resolver.js +2 -0
  53. package/dist/engine/template/compile-template.js +18 -0
  54. package/dist/engine/template/compiler.js +102 -0
  55. package/dist/engine/template/field-accessor.js +25 -0
  56. package/dist/engine/template/filters.js +94 -0
  57. package/dist/engine/template/index.js +5 -0
  58. package/dist/engine/template/parser.js +190 -0
  59. package/dist/engine/template/tokenizer.js +96 -0
  60. package/dist/engine/template/types.js +2 -0
  61. package/dist/types.js +2 -1
  62. package/dist/ui/display-adapter.js +60 -0
  63. package/dist/ui/earthquake-formatter.js +17 -5
  64. package/dist/ui/eew-formatter.js +25 -10
  65. package/dist/ui/formatter.js +67 -32
  66. package/dist/ui/minimap/grid-layout.js +91 -0
  67. package/dist/ui/minimap/index.js +16 -0
  68. package/dist/ui/minimap/minimap-renderer.js +277 -0
  69. package/dist/ui/minimap/pref-mapping.js +82 -0
  70. package/dist/ui/minimap/types.js +2 -0
  71. package/dist/ui/night-overlay.js +56 -0
  72. package/dist/ui/repl-handlers/command-definitions.js +320 -0
  73. package/dist/ui/repl-handlers/index.js +11 -0
  74. package/dist/ui/repl-handlers/info-handlers.js +577 -0
  75. package/dist/ui/repl-handlers/operation-handlers.js +233 -0
  76. package/dist/ui/repl-handlers/settings-handlers.js +923 -0
  77. package/dist/ui/repl-handlers/types.js +10 -0
  78. package/dist/ui/repl.js +81 -1752
  79. package/dist/ui/statistics-formatter.js +208 -0
  80. package/dist/ui/status-line.js +69 -0
  81. package/dist/ui/summary/index.js +5 -0
  82. package/dist/ui/summary/summary-line.js +18 -0
  83. package/dist/ui/summary/summary-model.js +31 -0
  84. package/dist/ui/summary/token-builders.js +317 -0
  85. package/dist/ui/summary/types.js +2 -0
  86. package/dist/ui/summary/width-fit.js +41 -0
  87. package/dist/ui/summary-interval-formatter.js +72 -0
  88. package/dist/ui/theme.js +34 -5
  89. package/dist/ui/tip-shuffler.js +81 -0
  90. package/dist/ui/volcano-formatter.js +15 -13
  91. package/dist/ui/waiting-tips.js +289 -249
  92. package/package.json +1 -1
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.processEew = processEew;
37
+ const telegram_parser_1 = require("../../../dmdata/telegram-parser");
38
+ const level_helpers_1 = require("../level-helpers");
39
+ const log = __importStar(require("../../../logger"));
40
+ /**
41
+ * EEW 電文を処理し結果を返す。
42
+ * - パース失敗: { kind: "parse-failed" }
43
+ * - 重複報: { kind: "duplicate" }
44
+ * - 正常: { kind: "ok", outcome }
45
+ */
46
+ function processEew(msg, eewTracker, eewLogger) {
47
+ const eewInfo = (0, telegram_parser_1.parseEewTelegram)(msg);
48
+ if (!eewInfo)
49
+ return { kind: "parse-failed" };
50
+ const result = eewTracker.update(eewInfo);
51
+ if (result.isDuplicate) {
52
+ log.debug(`EEW 重複報スキップ: EventID=${eewInfo.eventId} 第${eewInfo.serial}報`);
53
+ return { kind: "duplicate" };
54
+ }
55
+ // ログ記録
56
+ eewLogger.logReport(eewInfo, result);
57
+ if (result.isCancelled && eewInfo.eventId) {
58
+ eewLogger.closeEvent(eewInfo.eventId, "取消");
59
+ }
60
+ if (eewInfo.nextAdvisory && eewInfo.eventId && !result.isCancelled) {
61
+ eewLogger.closeEvent(eewInfo.eventId, "最終報");
62
+ eewTracker.finalizeEvent(eewInfo.eventId);
63
+ }
64
+ return {
65
+ kind: "ok",
66
+ outcome: {
67
+ domain: "eew",
68
+ msg,
69
+ headType: msg.head.type,
70
+ statsCategory: "eew",
71
+ parsed: eewInfo,
72
+ state: {
73
+ activeCount: result.activeCount,
74
+ colorIndex: result.colorIndex,
75
+ isCancelled: result.isCancelled,
76
+ diff: result.diff,
77
+ },
78
+ eewResult: result,
79
+ stats: {
80
+ shouldRecord: true,
81
+ eventId: eewInfo.eventId,
82
+ },
83
+ presentation: {
84
+ frameLevel: (0, level_helpers_1.eewFrameLevel)(eewInfo),
85
+ soundLevel: (0, level_helpers_1.eewSoundLevel)(eewInfo),
86
+ notifyCategory: "eew",
87
+ },
88
+ },
89
+ };
90
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processLgObservation = processLgObservation;
4
+ const telegram_parser_1 = require("../../../dmdata/telegram-parser");
5
+ const level_helpers_1 = require("../level-helpers");
6
+ /**
7
+ * 長周期地震動観測情報 (VXSE62) を処理し LgObservationOutcome を返す。
8
+ * パース失敗の場合は null を返す。
9
+ */
10
+ function processLgObservation(msg) {
11
+ const lgInfo = (0, telegram_parser_1.parseLgObservationTelegram)(msg);
12
+ if (!lgInfo)
13
+ return null;
14
+ return {
15
+ domain: "lgObservation",
16
+ msg,
17
+ headType: msg.head.type,
18
+ statsCategory: "earthquake", // routeToCategory("lgObservation") = "earthquake"
19
+ parsed: lgInfo,
20
+ stats: {
21
+ shouldRecord: true,
22
+ eventId: msg.xmlReport?.head.eventId ?? null,
23
+ },
24
+ presentation: {
25
+ frameLevel: (0, level_helpers_1.lgObservationFrameLevel)(lgInfo),
26
+ soundLevel: (0, level_helpers_1.lgObservationSoundLevel)(lgInfo),
27
+ notifyCategory: "lgObservation",
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processMessage = processMessage;
4
+ const telegram_stats_1 = require("../../messages/telegram-stats");
5
+ const process_eew_1 = require("./process-eew");
6
+ const process_earthquake_1 = require("./process-earthquake");
7
+ const process_seismic_text_1 = require("./process-seismic-text");
8
+ const process_lg_observation_1 = require("./process-lg-observation");
9
+ const process_tsunami_1 = require("./process-tsunami");
10
+ const process_nankai_trough_1 = require("./process-nankai-trough");
11
+ const process_raw_1 = require("./process-raw");
12
+ /**
13
+ * ルートに応じた processXxx を呼び出し ProcessOutcome を返す。
14
+ * パース失敗の場合は RawOutcome にフォールバックする(元カテゴリを statsCategory に保持)。
15
+ *
16
+ * EEW の場合: パース失敗/重複は null を返す(表示も統計記録もしない)。
17
+ * 火山の場合: VFVO53 aggregator との連携は呼び出し側の責務。
18
+ */
19
+ function processMessage(msg, route, deps) {
20
+ const category = (0, telegram_stats_1.routeToCategory)(route);
21
+ switch (route) {
22
+ case "eew": {
23
+ const eewResult = (0, process_eew_1.processEew)(msg, deps.eewTracker, deps.eewLogger);
24
+ if (eewResult.kind === "ok")
25
+ return eewResult.outcome;
26
+ if (eewResult.kind === "duplicate")
27
+ return null; // 重複 → 表示・統計なし
28
+ // parse-failed → raw 表示するが統計には含めない(旧 router と同じ動作)
29
+ const raw = (0, process_raw_1.processRaw)(msg, category);
30
+ raw.stats.shouldRecord = false;
31
+ return raw;
32
+ }
33
+ case "earthquake": {
34
+ return (0, process_earthquake_1.processEarthquake)(msg) ?? (0, process_raw_1.processRaw)(msg, category);
35
+ }
36
+ case "seismicText": {
37
+ return (0, process_seismic_text_1.processSeismicText)(msg) ?? (0, process_raw_1.processRaw)(msg, category);
38
+ }
39
+ case "lgObservation": {
40
+ return (0, process_lg_observation_1.processLgObservation)(msg) ?? (0, process_raw_1.processRaw)(msg, category);
41
+ }
42
+ case "tsunami": {
43
+ return (0, process_tsunami_1.processTsunami)(msg, deps.tsunamiState) ?? (0, process_raw_1.processRaw)(msg, category);
44
+ }
45
+ case "nankaiTrough": {
46
+ return (0, process_nankai_trough_1.processNankaiTrough)(msg) ?? (0, process_raw_1.processRaw)(msg, category);
47
+ }
48
+ // volcano: VolcanoRouteHandler が処理する (VFVO53 バッチ集約のため線形フローでは処理不可)
49
+ default: {
50
+ return (0, process_raw_1.processRaw)(msg, category);
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processNankaiTrough = processNankaiTrough;
4
+ const telegram_parser_1 = require("../../../dmdata/telegram-parser");
5
+ const level_helpers_1 = require("../level-helpers");
6
+ /**
7
+ * 南海トラフ電文 (VYSE50/51/52/60) を処理し NankaiTroughOutcome を返す。
8
+ * パース失敗の場合は null を返す。
9
+ */
10
+ function processNankaiTrough(msg) {
11
+ const nankaiInfo = (0, telegram_parser_1.parseNankaiTroughTelegram)(msg);
12
+ if (!nankaiInfo)
13
+ return null;
14
+ return {
15
+ domain: "nankaiTrough",
16
+ msg,
17
+ headType: msg.head.type,
18
+ statsCategory: "nankaiTrough",
19
+ parsed: nankaiInfo,
20
+ stats: {
21
+ shouldRecord: true,
22
+ eventId: msg.xmlReport?.head.eventId ?? null,
23
+ },
24
+ presentation: {
25
+ frameLevel: (0, level_helpers_1.nankaiTroughFrameLevel)(nankaiInfo),
26
+ soundLevel: (0, level_helpers_1.nankaiTroughSoundLevel)(nankaiInfo),
27
+ notifyCategory: "nankaiTrough",
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processRaw = processRaw;
4
+ /**
5
+ * フォールバック: パース失敗等で認識できない電文の ProcessOutcome。
6
+ * statsCategory は呼び出し元から渡され、元ルートのカテゴリを保持する。
7
+ */
8
+ function processRaw(msg, statsCategory = "other") {
9
+ return {
10
+ domain: "raw",
11
+ msg,
12
+ headType: msg.head.type,
13
+ statsCategory,
14
+ parsed: null,
15
+ stats: {
16
+ shouldRecord: true,
17
+ },
18
+ presentation: {
19
+ frameLevel: "info",
20
+ },
21
+ };
22
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processSeismicText = processSeismicText;
4
+ const telegram_parser_1 = require("../../../dmdata/telegram-parser");
5
+ const level_helpers_1 = require("../level-helpers");
6
+ /**
7
+ * テキスト系地震電文 (VXSE56/VXSE60/VZSE40) を処理し SeismicTextOutcome を返す。
8
+ * パース失敗の場合は null を返す。
9
+ */
10
+ function processSeismicText(msg) {
11
+ const textInfo = (0, telegram_parser_1.parseSeismicTextTelegram)(msg);
12
+ if (!textInfo)
13
+ return null;
14
+ return {
15
+ domain: "seismicText",
16
+ msg,
17
+ headType: msg.head.type,
18
+ statsCategory: "earthquake", // routeToCategory("seismicText") = "earthquake"
19
+ parsed: textInfo,
20
+ stats: {
21
+ shouldRecord: true,
22
+ eventId: msg.xmlReport?.head.eventId ?? null,
23
+ },
24
+ presentation: {
25
+ frameLevel: (0, level_helpers_1.seismicTextFrameLevel)(textInfo),
26
+ soundLevel: (0, level_helpers_1.seismicTextSoundLevel)(textInfo),
27
+ notifyCategory: "seismicText",
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processTsunami = processTsunami;
4
+ const telegram_parser_1 = require("../../../dmdata/telegram-parser");
5
+ const level_helpers_1 = require("../level-helpers");
6
+ /**
7
+ * 津波電文 (VTSE41/51/52) を処理し TsunamiOutcome を返す。
8
+ * VTSE41 のみ TsunamiStateHolder の状態更新を行い、更新前後のレベルを記録する。
9
+ * パース失敗の場合は null を返す。
10
+ */
11
+ function processTsunami(msg, tsunamiState) {
12
+ const tsunamiInfo = (0, telegram_parser_1.parseTsunamiTelegram)(msg);
13
+ if (!tsunamiInfo)
14
+ return null;
15
+ const levelBefore = tsunamiState.getLevel();
16
+ // VTSE41 のみ状態更新
17
+ if (msg.head.type === "VTSE41") {
18
+ tsunamiState.update(tsunamiInfo);
19
+ }
20
+ const levelAfter = tsunamiState.getLevel();
21
+ return {
22
+ domain: "tsunami",
23
+ msg,
24
+ headType: msg.head.type,
25
+ statsCategory: "tsunami",
26
+ parsed: tsunamiInfo,
27
+ state: {
28
+ levelBefore,
29
+ levelAfter,
30
+ changed: levelBefore !== levelAfter,
31
+ },
32
+ stats: {
33
+ shouldRecord: true,
34
+ eventId: msg.xmlReport?.head.eventId ?? null,
35
+ },
36
+ presentation: {
37
+ frameLevel: (0, level_helpers_1.tsunamiFrameLevel)(tsunamiInfo),
38
+ soundLevel: (0, level_helpers_1.tsunamiSoundLevel)(tsunamiInfo),
39
+ notifyCategory: "tsunami",
40
+ },
41
+ };
42
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildVolcanoOutcome = buildVolcanoOutcome;
4
+ const volcano_presentation_1 = require("../../notification/volcano-presentation");
5
+ /**
6
+ * パース済み火山情報から VolcanoOutcome を構築する。
7
+ * VolcanoRouteHandler から使用される。
8
+ */
9
+ function buildVolcanoOutcome(msg, volcanoInfo, volcanoState) {
10
+ const presentation = (0, volcano_presentation_1.resolveVolcanoPresentation)(volcanoInfo, volcanoState);
11
+ const isRenotification = volcanoInfo.kind === "alert"
12
+ ? volcanoState.isRenotification(volcanoInfo)
13
+ : false;
14
+ // Track state before/after for alert types
15
+ const trackedBefore = volcanoInfo.volcanoCode
16
+ ? (volcanoState.getEntry(volcanoInfo.volcanoCode)?.alertLevel?.toString() ?? null)
17
+ : null;
18
+ return {
19
+ domain: "volcano",
20
+ msg,
21
+ headType: msg.head.type,
22
+ statsCategory: "volcano",
23
+ parsed: volcanoInfo,
24
+ volcanoPresentation: presentation,
25
+ state: {
26
+ isRenotification,
27
+ trackedBefore,
28
+ // trackedAfter will be set by router after volcanoState.update()
29
+ trackedAfter: undefined,
30
+ },
31
+ stats: {
32
+ shouldRecord: true,
33
+ eventId: msg.xmlReport?.head.eventId ?? null,
34
+ },
35
+ presentation: {
36
+ frameLevel: presentation.frameLevel,
37
+ soundLevel: presentation.soundLevel,
38
+ notifyCategory: "volcano",
39
+ },
40
+ };
41
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -133,6 +133,8 @@ function resolveConfig(opts) {
133
133
  eewLog: fileConfig.eewLog ?? types_1.DEFAULT_CONFIG.eewLog,
134
134
  eewLogFields: { ...types_1.DEFAULT_CONFIG.eewLogFields, ...fileConfig.eewLogFields },
135
135
  maxObservations: fileConfig.maxObservations ?? types_1.DEFAULT_CONFIG.maxObservations,
136
+ nightMode: opts.night ?? fileConfig.nightMode ?? types_1.DEFAULT_CONFIG.nightMode,
137
+ summaryInterval: fileConfig.summaryInterval ?? types_1.DEFAULT_CONFIG.summaryInterval,
136
138
  backup: fileConfig.backup ?? types_1.DEFAULT_CONFIG.backup,
137
139
  truncation: { ...types_1.DEFAULT_CONFIG.truncation, ...fileConfig.truncation },
138
140
  };
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compileTemplate = compileTemplate;
4
+ const parser_1 = require("./parser");
5
+ const compiler_1 = require("./compiler");
6
+ /**
7
+ * テンプレート文字列をコンパイルし、TemplateRenderer を返す。
8
+ *
9
+ * 使用例:
10
+ * ```ts
11
+ * const render = compileTemplate("{{title}} M{{magnitude|default:\"-\"}}");
12
+ * const text = render(event);
13
+ * ```
14
+ */
15
+ function compileTemplate(template) {
16
+ const nodes = (0, parser_1.parseTemplate)(template);
17
+ return (0, compiler_1.compileTemplateNodes)(nodes);
18
+ }
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compileTemplateNodes = compileTemplateNodes;
4
+ const field_accessor_1 = require("./field-accessor");
5
+ const filters_1 = require("./filters");
6
+ /**
7
+ * TemplateNode[] を TemplateRenderer にコンパイルする。
8
+ */
9
+ function compileTemplateNodes(nodes) {
10
+ return (event) => {
11
+ return renderNodes(nodes, event);
12
+ };
13
+ }
14
+ function renderNodes(nodes, event) {
15
+ let result = "";
16
+ for (const node of nodes) {
17
+ switch (node.kind) {
18
+ case "text":
19
+ result += node.value;
20
+ break;
21
+ case "interpolation":
22
+ result += renderInterpolation(node.expr, node.filters, event);
23
+ break;
24
+ case "if":
25
+ result += renderIfBlock(node, event);
26
+ break;
27
+ }
28
+ }
29
+ return result;
30
+ }
31
+ function renderInterpolation(expr, filters, event) {
32
+ let value = resolveExpr(expr, event);
33
+ for (const filter of filters) {
34
+ const args = filter.args.map((a) => resolveExprLiteral(a, event));
35
+ value = (0, filters_1.applyFilter)(filter.name, value, args);
36
+ }
37
+ return stringify(value);
38
+ }
39
+ function renderIfBlock(node, event) {
40
+ const result = evaluatePredicate(node.test, event);
41
+ if (result) {
42
+ return renderNodes(node.body, event);
43
+ }
44
+ if (node.elseBody) {
45
+ return renderNodes(node.elseBody, event);
46
+ }
47
+ return "";
48
+ }
49
+ function resolveExpr(expr, event) {
50
+ if (expr.kind === "literal")
51
+ return expr.value;
52
+ return (0, field_accessor_1.getFieldValue)(event, expr.segments);
53
+ }
54
+ function resolveExprLiteral(expr, event) {
55
+ const val = resolveExpr(expr, event);
56
+ if (val == null)
57
+ return null;
58
+ if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
59
+ return val;
60
+ }
61
+ return String(val);
62
+ }
63
+ function evaluatePredicate(pred, event) {
64
+ if (pred.kind === "truthy") {
65
+ const val = resolveExpr(pred.expr, event);
66
+ return isTruthy(val);
67
+ }
68
+ const left = resolveExpr(pred.left, event);
69
+ const right = resolveExpr(pred.right, event);
70
+ switch (pred.op) {
71
+ case "eq":
72
+ return left === right;
73
+ case "ne":
74
+ return left !== right;
75
+ case "gt":
76
+ return Number(left) > Number(right);
77
+ case "ge":
78
+ return Number(left) >= Number(right);
79
+ case "lt":
80
+ return Number(left) < Number(right);
81
+ case "le":
82
+ return Number(left) <= Number(right);
83
+ }
84
+ }
85
+ function isTruthy(val) {
86
+ if (val == null)
87
+ return false;
88
+ if (val === false)
89
+ return false;
90
+ if (val === "")
91
+ return false;
92
+ if (val === 0)
93
+ return false;
94
+ return true;
95
+ }
96
+ function stringify(value) {
97
+ if (value == null)
98
+ return "";
99
+ if (Array.isArray(value))
100
+ return value.join(", ");
101
+ return String(value);
102
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFieldValue = getFieldValue;
4
+ /**
5
+ * PresentationEvent からドットパス + index access で値を取得する。
6
+ *
7
+ * segments 例:
8
+ * - ["title"] → event.title
9
+ * - ["raw", "xxx"] → event.raw.xxx
10
+ * - ["areaItems", 0, "name"] → event.areaItems[0].name
11
+ */
12
+ function getFieldValue(event, segments) {
13
+ let current = event;
14
+ for (const seg of segments) {
15
+ if (current == null)
16
+ return undefined;
17
+ if (typeof current === "object") {
18
+ current = current[seg];
19
+ }
20
+ else {
21
+ return undefined;
22
+ }
23
+ }
24
+ return current;
25
+ }
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ // ── Template filter functions ──
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.applyFilter = applyFilter;
5
+ function toString(value) {
6
+ if (value == null)
7
+ return "";
8
+ return String(value);
9
+ }
10
+ function filterDefault(value, args) {
11
+ if (value == null || value === "") {
12
+ return args[0] ?? "";
13
+ }
14
+ return value;
15
+ }
16
+ function filterTruncate(value, args) {
17
+ const str = toString(value);
18
+ const limit = typeof args[0] === "number" ? args[0] : Number(args[0]);
19
+ if (!Number.isFinite(limit) || str.length <= limit)
20
+ return str;
21
+ return str.slice(0, limit);
22
+ }
23
+ function filterPad(value, args) {
24
+ const str = toString(value);
25
+ const width = typeof args[0] === "number" ? args[0] : Number(args[0]);
26
+ if (!Number.isFinite(width))
27
+ return str;
28
+ return str.padEnd(width);
29
+ }
30
+ function filterJoin(value, args) {
31
+ if (!Array.isArray(value))
32
+ return toString(value);
33
+ const separator = args[0] != null ? String(args[0]) : ",";
34
+ return value.join(separator);
35
+ }
36
+ function filterDate(value, args) {
37
+ const format = args[0] != null ? String(args[0]) : "HH:mm";
38
+ const date = value instanceof Date ? value : new Date(String(value));
39
+ if (isNaN(date.getTime()))
40
+ return toString(value);
41
+ const HH = String(date.getHours()).padStart(2, "0");
42
+ const mm = String(date.getMinutes()).padStart(2, "0");
43
+ const ss = String(date.getSeconds()).padStart(2, "0");
44
+ const MM = String(date.getMonth() + 1).padStart(2, "0");
45
+ const DD = String(date.getDate()).padStart(2, "0");
46
+ switch (format) {
47
+ case "HH:mm":
48
+ return `${HH}:${mm}`;
49
+ case "HH:mm:ss":
50
+ return `${HH}:${mm}:${ss}`;
51
+ case "MM/DD HH:mm":
52
+ return `${MM}/${DD} ${HH}:${mm}`;
53
+ default:
54
+ return `${HH}:${mm}`;
55
+ }
56
+ }
57
+ function filterReplace(value, args) {
58
+ const str = toString(value);
59
+ const search = args[0] != null ? String(args[0]) : "";
60
+ const replacement = args[1] != null ? String(args[1]) : "";
61
+ return str.split(search).join(replacement);
62
+ }
63
+ function filterUpper(value) {
64
+ return toString(value).toUpperCase();
65
+ }
66
+ function filterLower(value) {
67
+ return toString(value).toLowerCase();
68
+ }
69
+ /**
70
+ * テンプレートフィルタを適用する。
71
+ * 未知のフィルタ名の場合は値をそのまま返す。
72
+ */
73
+ function applyFilter(name, value, args) {
74
+ switch (name) {
75
+ case "default":
76
+ return filterDefault(value, args);
77
+ case "truncate":
78
+ return filterTruncate(value, args);
79
+ case "pad":
80
+ return filterPad(value, args);
81
+ case "join":
82
+ return filterJoin(value, args);
83
+ case "date":
84
+ return filterDate(value, args);
85
+ case "replace":
86
+ return filterReplace(value, args);
87
+ case "upper":
88
+ return filterUpper(value);
89
+ case "lower":
90
+ return filterLower(value);
91
+ default:
92
+ return value;
93
+ }
94
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compileTemplate = void 0;
4
+ var compile_template_1 = require("./compile-template");
5
+ Object.defineProperty(exports, "compileTemplate", { enumerable: true, get: function () { return compile_template_1.compileTemplate; } });