@tradejs/infra 2.0.19 → 2.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ai.js CHANGED
@@ -134,21 +134,32 @@ var getRunIdFromAiChunkFileName = (strategyName, fileName) => {
134
134
  var appendAiDatasetRow = async (params) => {
135
135
  const { strategyName, chunkId, row, outDir = DEFAULT_DIR } = params;
136
136
  const filePath = getAiChunkFilePath(strategyName, chunkId, outDir);
137
- let state = writerByPath.get(filePath);
138
- if (!state) {
139
- await import_promises2.default.mkdir(outDir, { recursive: true });
140
- const stream = (0, import_fs2.createWriteStream)(filePath, {
141
- encoding: "utf8",
142
- flags: "a"
143
- });
144
- state = {
145
- filePath,
146
- stream,
147
- buffer: [],
148
- writeQueue: Promise.resolve(),
149
- closed: false
150
- };
151
- writerByPath.set(filePath, state);
137
+ let statePromise = writerByPath.get(filePath);
138
+ if (!statePromise) {
139
+ statePromise = (async () => {
140
+ await import_promises2.default.mkdir(outDir, { recursive: true });
141
+ const stream = (0, import_fs2.createWriteStream)(filePath, {
142
+ encoding: "utf8",
143
+ flags: "a"
144
+ });
145
+ return {
146
+ filePath,
147
+ stream,
148
+ buffer: [],
149
+ writeQueue: Promise.resolve(),
150
+ closed: false
151
+ };
152
+ })();
153
+ writerByPath.set(filePath, statePromise);
154
+ }
155
+ let state;
156
+ try {
157
+ state = await statePromise;
158
+ } catch (error) {
159
+ if (writerByPath.get(filePath) === statePromise) {
160
+ writerByPath.delete(filePath);
161
+ }
162
+ throw error;
152
163
  }
153
164
  if (state.closed) {
154
165
  throw new Error(`AI dataset writer is closed: ${filePath}`);
@@ -171,10 +182,12 @@ var flushState = async (state) => {
171
182
  }
172
183
  };
173
184
  var flushAiDatasetWriter = async (filePath) => {
174
- const state = writerByPath.get(filePath);
175
- if (!state || state.closed) {
185
+ const statePromise = writerByPath.get(filePath);
186
+ if (!statePromise) {
176
187
  return;
177
188
  }
189
+ const state = await statePromise;
190
+ if (state.closed) return;
178
191
  state.writeQueue = state.writeQueue.then(() => flushState(state));
179
192
  await state.writeQueue;
180
193
  };
@@ -191,13 +204,16 @@ var closeState = async (state) => {
191
204
  ]);
192
205
  };
193
206
  var closeAiDatasetWriter = async (filePath) => {
194
- const state = writerByPath.get(filePath);
195
- if (!state) {
207
+ const statePromise = writerByPath.get(filePath);
208
+ if (!statePromise) {
196
209
  return;
197
210
  }
211
+ const state = await statePromise;
198
212
  state.writeQueue = state.writeQueue.then(() => closeState(state));
199
213
  await state.writeQueue;
200
- writerByPath.delete(filePath);
214
+ if (writerByPath.get(filePath) === statePromise) {
215
+ writerByPath.delete(filePath);
216
+ }
201
217
  };
202
218
  var closeAllAiDatasetWriters = async () => {
203
219
  const filePaths = [...writerByPath.keys()];
package/dist/ai.mjs CHANGED
@@ -35,21 +35,32 @@ var getRunIdFromAiChunkFileName = (strategyName, fileName) => {
35
35
  var appendAiDatasetRow = async (params) => {
36
36
  const { strategyName, chunkId, row, outDir = DEFAULT_DIR } = params;
37
37
  const filePath = getAiChunkFilePath(strategyName, chunkId, outDir);
38
- let state = writerByPath.get(filePath);
39
- if (!state) {
40
- await fs.mkdir(outDir, { recursive: true });
41
- const stream = createWriteStream(filePath, {
42
- encoding: "utf8",
43
- flags: "a"
44
- });
45
- state = {
46
- filePath,
47
- stream,
48
- buffer: [],
49
- writeQueue: Promise.resolve(),
50
- closed: false
51
- };
52
- writerByPath.set(filePath, state);
38
+ let statePromise = writerByPath.get(filePath);
39
+ if (!statePromise) {
40
+ statePromise = (async () => {
41
+ await fs.mkdir(outDir, { recursive: true });
42
+ const stream = createWriteStream(filePath, {
43
+ encoding: "utf8",
44
+ flags: "a"
45
+ });
46
+ return {
47
+ filePath,
48
+ stream,
49
+ buffer: [],
50
+ writeQueue: Promise.resolve(),
51
+ closed: false
52
+ };
53
+ })();
54
+ writerByPath.set(filePath, statePromise);
55
+ }
56
+ let state;
57
+ try {
58
+ state = await statePromise;
59
+ } catch (error) {
60
+ if (writerByPath.get(filePath) === statePromise) {
61
+ writerByPath.delete(filePath);
62
+ }
63
+ throw error;
53
64
  }
54
65
  if (state.closed) {
55
66
  throw new Error(`AI dataset writer is closed: ${filePath}`);
@@ -72,10 +83,12 @@ var flushState = async (state) => {
72
83
  }
73
84
  };
74
85
  var flushAiDatasetWriter = async (filePath) => {
75
- const state = writerByPath.get(filePath);
76
- if (!state || state.closed) {
86
+ const statePromise = writerByPath.get(filePath);
87
+ if (!statePromise) {
77
88
  return;
78
89
  }
90
+ const state = await statePromise;
91
+ if (state.closed) return;
79
92
  state.writeQueue = state.writeQueue.then(() => flushState(state));
80
93
  await state.writeQueue;
81
94
  };
@@ -92,13 +105,16 @@ var closeState = async (state) => {
92
105
  ]);
93
106
  };
94
107
  var closeAiDatasetWriter = async (filePath) => {
95
- const state = writerByPath.get(filePath);
96
- if (!state) {
108
+ const statePromise = writerByPath.get(filePath);
109
+ if (!statePromise) {
97
110
  return;
98
111
  }
112
+ const state = await statePromise;
99
113
  state.writeQueue = state.writeQueue.then(() => closeState(state));
100
114
  await state.writeQueue;
101
- writerByPath.delete(filePath);
115
+ if (writerByPath.get(filePath) === statePromise) {
116
+ writerByPath.delete(filePath);
117
+ }
102
118
  };
103
119
  var closeAllAiDatasetWriters = async () => {
104
120
  const filePaths = [...writerByPath.keys()];
@@ -0,0 +1,20 @@
1
+ import { CoreResearchTraceEvent } from '@tradejs/types';
2
+
3
+ declare const getCoreResearchTraceFilePath: (params: {
4
+ strategyName: string;
5
+ chunkId: string;
6
+ outDir?: string;
7
+ }) => string;
8
+ declare const listCoreResearchTraceFiles: (params: {
9
+ strategyName: string;
10
+ runId: string;
11
+ outDir?: string;
12
+ }) => Promise<string[]>;
13
+ declare const appendCoreResearchTraceEvent: (params: {
14
+ strategyName: string;
15
+ chunkId: string;
16
+ event: CoreResearchTraceEvent;
17
+ outDir?: string;
18
+ }) => Promise<string>;
19
+
20
+ export { appendCoreResearchTraceEvent, getCoreResearchTraceFilePath, listCoreResearchTraceFiles };
@@ -0,0 +1,20 @@
1
+ import { CoreResearchTraceEvent } from '@tradejs/types';
2
+
3
+ declare const getCoreResearchTraceFilePath: (params: {
4
+ strategyName: string;
5
+ chunkId: string;
6
+ outDir?: string;
7
+ }) => string;
8
+ declare const listCoreResearchTraceFiles: (params: {
9
+ strategyName: string;
10
+ runId: string;
11
+ outDir?: string;
12
+ }) => Promise<string[]>;
13
+ declare const appendCoreResearchTraceEvent: (params: {
14
+ strategyName: string;
15
+ chunkId: string;
16
+ event: CoreResearchTraceEvent;
17
+ outDir?: string;
18
+ }) => Promise<string>;
19
+
20
+ export { appendCoreResearchTraceEvent, getCoreResearchTraceFilePath, listCoreResearchTraceFiles };
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/coreResearch.ts
31
+ var coreResearch_exports = {};
32
+ __export(coreResearch_exports, {
33
+ appendCoreResearchTraceEvent: () => appendCoreResearchTraceEvent,
34
+ getCoreResearchTraceFilePath: () => getCoreResearchTraceFilePath,
35
+ listCoreResearchTraceFiles: () => listCoreResearchTraceFiles
36
+ });
37
+ module.exports = __toCommonJS(coreResearch_exports);
38
+
39
+ // src/coreResearchTraceFile.ts
40
+ var import_promises = __toESM(require("fs/promises"));
41
+ var import_node_path = __toESM(require("path"));
42
+
43
+ // src/mlDatasetFile.ts
44
+ var import_node_readline = __toESM(require("readline"));
45
+ var toFileToken = (value) => value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "any";
46
+
47
+ // src/coreResearchTraceFile.ts
48
+ var DEFAULT_DIR = "data/research/core/trace";
49
+ var queueByPath = /* @__PURE__ */ new Map();
50
+ var getCoreResearchTraceFilePath = (params) => import_node_path.default.join(
51
+ params.outDir ?? DEFAULT_DIR,
52
+ `core-research-trace-${toFileToken(params.strategyName)}-chunk-${toFileToken(params.chunkId)}.jsonl`
53
+ );
54
+ var listCoreResearchTraceFiles = async (params) => {
55
+ const outDir = import_node_path.default.resolve(params.outDir ?? DEFAULT_DIR);
56
+ let names = [];
57
+ try {
58
+ names = await import_promises.default.readdir(outDir);
59
+ } catch (error) {
60
+ if (error.code === "ENOENT") return [];
61
+ throw error;
62
+ }
63
+ const prefix = `core-research-trace-${toFileToken(params.strategyName)}-chunk-`;
64
+ return names.filter(
65
+ (name) => name.startsWith(prefix) && name.includes(toFileToken(params.runId)) && name.endsWith(".jsonl")
66
+ ).sort((left, right) => left < right ? -1 : left > right ? 1 : 0).map((name) => import_node_path.default.join(outDir, name));
67
+ };
68
+ var appendCoreResearchTraceEvent = async (params) => {
69
+ const filePath = getCoreResearchTraceFilePath(params);
70
+ const previous = queueByPath.get(filePath) ?? Promise.resolve();
71
+ const next = previous.then(async () => {
72
+ await import_promises.default.mkdir(import_node_path.default.dirname(filePath), { recursive: true });
73
+ await import_promises.default.appendFile(filePath, `${JSON.stringify(params.event)}
74
+ `, "utf8");
75
+ });
76
+ queueByPath.set(filePath, next);
77
+ try {
78
+ await next;
79
+ } finally {
80
+ if (queueByPath.get(filePath) === next) queueByPath.delete(filePath);
81
+ }
82
+ return filePath;
83
+ };
84
+ // Annotate the CommonJS export names for ESM import in node:
85
+ 0 && (module.exports = {
86
+ appendCoreResearchTraceEvent,
87
+ getCoreResearchTraceFilePath,
88
+ listCoreResearchTraceFiles
89
+ });
@@ -0,0 +1,48 @@
1
+ import {
2
+ toFileToken
3
+ } from "./chunk-RQP5VSTH.mjs";
4
+
5
+ // src/coreResearchTraceFile.ts
6
+ import fs from "fs/promises";
7
+ import path from "path";
8
+ var DEFAULT_DIR = "data/research/core/trace";
9
+ var queueByPath = /* @__PURE__ */ new Map();
10
+ var getCoreResearchTraceFilePath = (params) => path.join(
11
+ params.outDir ?? DEFAULT_DIR,
12
+ `core-research-trace-${toFileToken(params.strategyName)}-chunk-${toFileToken(params.chunkId)}.jsonl`
13
+ );
14
+ var listCoreResearchTraceFiles = async (params) => {
15
+ const outDir = path.resolve(params.outDir ?? DEFAULT_DIR);
16
+ let names = [];
17
+ try {
18
+ names = await fs.readdir(outDir);
19
+ } catch (error) {
20
+ if (error.code === "ENOENT") return [];
21
+ throw error;
22
+ }
23
+ const prefix = `core-research-trace-${toFileToken(params.strategyName)}-chunk-`;
24
+ return names.filter(
25
+ (name) => name.startsWith(prefix) && name.includes(toFileToken(params.runId)) && name.endsWith(".jsonl")
26
+ ).sort((left, right) => left < right ? -1 : left > right ? 1 : 0).map((name) => path.join(outDir, name));
27
+ };
28
+ var appendCoreResearchTraceEvent = async (params) => {
29
+ const filePath = getCoreResearchTraceFilePath(params);
30
+ const previous = queueByPath.get(filePath) ?? Promise.resolve();
31
+ const next = previous.then(async () => {
32
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
33
+ await fs.appendFile(filePath, `${JSON.stringify(params.event)}
34
+ `, "utf8");
35
+ });
36
+ queueByPath.set(filePath, next);
37
+ try {
38
+ await next;
39
+ } finally {
40
+ if (queueByPath.get(filePath) === next) queueByPath.delete(filePath);
41
+ }
42
+ return filePath;
43
+ };
44
+ export {
45
+ appendCoreResearchTraceEvent,
46
+ getCoreResearchTraceFilePath,
47
+ listCoreResearchTraceFiles
48
+ };
package/dist/ml.mjs CHANGED
@@ -1,3 +1,6 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-LNFUOXDW.mjs";
1
4
  import {
2
5
  appendMlDatasetRow,
3
6
  closeAllMlDatasetWriters,
@@ -10,9 +13,6 @@ import {
10
13
  mergeJsonlFiles,
11
14
  toFileToken
12
15
  } from "./chunk-RQP5VSTH.mjs";
13
- import {
14
- logger
15
- } from "./chunk-LNFUOXDW.mjs";
16
16
 
17
17
  // src/mlGrpc.ts
18
18
  import path from "path";
@@ -0,0 +1,17 @@
1
+ import { StrategyEvidenceMarkerPayload, StrategyEvidenceMarkerEnvelope } from '@tradejs/types';
2
+
3
+ declare const canonicalStrategyEvidenceJson: (value: unknown) => string;
4
+ declare const strategyEvidenceSha256: (value: unknown) => string;
5
+ declare const strategyEvidenceFingerprint: (value: unknown) => string;
6
+ /**
7
+ * Fingerprints strategy decision semantics without local/runtime bindings,
8
+ * credentials, config identity, or position-risk scale.
9
+ */
10
+ declare const strategyLogicConfigFingerprint: (value: unknown) => string;
11
+ declare const strategyEvidenceFileSha256: (filePath: string) => Promise<string>;
12
+ declare const safeStrategyEvidenceSegment: (value: string, fallback?: string) => string;
13
+ declare const compactStrategyEvidenceTimestamp: (timestamp: number) => string;
14
+ declare const createStrategyEvidenceMarkerEnvelope: (payload: StrategyEvidenceMarkerPayload) => StrategyEvidenceMarkerEnvelope;
15
+ declare const verifyStrategyEvidenceMarkerEnvelope: (value: unknown) => StrategyEvidenceMarkerEnvelope;
16
+
17
+ export { canonicalStrategyEvidenceJson, compactStrategyEvidenceTimestamp, createStrategyEvidenceMarkerEnvelope, safeStrategyEvidenceSegment, strategyEvidenceFileSha256, strategyEvidenceFingerprint, strategyEvidenceSha256, strategyLogicConfigFingerprint, verifyStrategyEvidenceMarkerEnvelope };
@@ -0,0 +1,17 @@
1
+ import { StrategyEvidenceMarkerPayload, StrategyEvidenceMarkerEnvelope } from '@tradejs/types';
2
+
3
+ declare const canonicalStrategyEvidenceJson: (value: unknown) => string;
4
+ declare const strategyEvidenceSha256: (value: unknown) => string;
5
+ declare const strategyEvidenceFingerprint: (value: unknown) => string;
6
+ /**
7
+ * Fingerprints strategy decision semantics without local/runtime bindings,
8
+ * credentials, config identity, or position-risk scale.
9
+ */
10
+ declare const strategyLogicConfigFingerprint: (value: unknown) => string;
11
+ declare const strategyEvidenceFileSha256: (filePath: string) => Promise<string>;
12
+ declare const safeStrategyEvidenceSegment: (value: string, fallback?: string) => string;
13
+ declare const compactStrategyEvidenceTimestamp: (timestamp: number) => string;
14
+ declare const createStrategyEvidenceMarkerEnvelope: (payload: StrategyEvidenceMarkerPayload) => StrategyEvidenceMarkerEnvelope;
15
+ declare const verifyStrategyEvidenceMarkerEnvelope: (value: unknown) => StrategyEvidenceMarkerEnvelope;
16
+
17
+ export { canonicalStrategyEvidenceJson, compactStrategyEvidenceTimestamp, createStrategyEvidenceMarkerEnvelope, safeStrategyEvidenceSegment, strategyEvidenceFileSha256, strategyEvidenceFingerprint, strategyEvidenceSha256, strategyLogicConfigFingerprint, verifyStrategyEvidenceMarkerEnvelope };
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/strategyReleaseEvidence.ts
21
+ var strategyReleaseEvidence_exports = {};
22
+ __export(strategyReleaseEvidence_exports, {
23
+ canonicalStrategyEvidenceJson: () => canonicalStrategyEvidenceJson,
24
+ compactStrategyEvidenceTimestamp: () => compactStrategyEvidenceTimestamp,
25
+ createStrategyEvidenceMarkerEnvelope: () => createStrategyEvidenceMarkerEnvelope,
26
+ safeStrategyEvidenceSegment: () => safeStrategyEvidenceSegment,
27
+ strategyEvidenceFileSha256: () => strategyEvidenceFileSha256,
28
+ strategyEvidenceFingerprint: () => strategyEvidenceFingerprint,
29
+ strategyEvidenceSha256: () => strategyEvidenceSha256,
30
+ strategyLogicConfigFingerprint: () => strategyLogicConfigFingerprint,
31
+ verifyStrategyEvidenceMarkerEnvelope: () => verifyStrategyEvidenceMarkerEnvelope
32
+ });
33
+ module.exports = __toCommonJS(strategyReleaseEvidence_exports);
34
+ var import_node_crypto = require("crypto");
35
+ var import_node_fs = require("fs");
36
+ var import_types = require("@tradejs/types");
37
+ var SHA256_RE = /^[a-f0-9]{64}$/;
38
+ var LINEAGE_FINGERPRINT_RE = /^[a-f0-9]{16}$/;
39
+ var MARKER_TYPES = /* @__PURE__ */ new Set([
40
+ "G",
41
+ "L",
42
+ "E",
43
+ "D",
44
+ "P",
45
+ "R"
46
+ ]);
47
+ var STRATEGY_LOGIC_BINDING_KEYS = /* @__PURE__ */ new Set([
48
+ "ACCOUNT_ID",
49
+ "DEPLOYMENT_ID",
50
+ "MAX_LOSS_VALUE",
51
+ "configId"
52
+ ]);
53
+ var SECRET_CONFIG_KEY_RE = /(?:^|_)(?:API_KEY|API_SECRET|TOKEN|PASSWORD|PRIVATE_KEY)$/i;
54
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
55
+ var isString = (value) => typeof value === "string" && value.trim().length > 0;
56
+ var isNumber = (value) => typeof value === "number" && Number.isFinite(value);
57
+ var canonicalStrategyEvidenceJson = (value) => {
58
+ const normalize = (current) => {
59
+ if (Array.isArray(current)) return current.map(normalize);
60
+ const record = asRecord(current);
61
+ if (!record) return current;
62
+ return Object.fromEntries(
63
+ Object.entries(record).filter(([, entry]) => entry !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalize(entry)])
64
+ );
65
+ };
66
+ return JSON.stringify(normalize(value));
67
+ };
68
+ var strategyEvidenceSha256 = (value) => (0, import_node_crypto.createHash)("sha256").update(canonicalStrategyEvidenceJson(value)).digest("hex");
69
+ var strategyEvidenceFingerprint = (value) => strategyEvidenceSha256(value).slice(0, 16);
70
+ var normalizeStrategyLogicConfig = (value) => {
71
+ if (Array.isArray(value)) return value.map(normalizeStrategyLogicConfig);
72
+ const record = asRecord(value);
73
+ if (!record) return value;
74
+ return Object.fromEntries(
75
+ Object.entries(record).filter(
76
+ ([key]) => !STRATEGY_LOGIC_BINDING_KEYS.has(key) && !SECRET_CONFIG_KEY_RE.test(key)
77
+ ).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalizeStrategyLogicConfig(entry)])
78
+ );
79
+ };
80
+ var strategyLogicConfigFingerprint = (value) => strategyEvidenceFingerprint(normalizeStrategyLogicConfig(value));
81
+ var strategyEvidenceFileSha256 = async (filePath) => {
82
+ const hash = (0, import_node_crypto.createHash)("sha256");
83
+ for await (const chunk of (0, import_node_fs.createReadStream)(filePath)) hash.update(chunk);
84
+ return hash.digest("hex");
85
+ };
86
+ var safeStrategyEvidenceSegment = (value, fallback = "strategy") => {
87
+ const safe = value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-");
88
+ return safe && safe !== "." && safe !== ".." ? safe : fallback;
89
+ };
90
+ var compactStrategyEvidenceTimestamp = (timestamp) => new Date(timestamp).toISOString().replace(/[-:]/g, "").replace(/\.000Z$/, "Z");
91
+ var createStrategyEvidenceMarkerEnvelope = (payload) => {
92
+ const payloadSha256 = strategyEvidenceSha256(payload);
93
+ return {
94
+ schema: import_types.STRATEGY_EVIDENCE_MARKERS_SCHEMA,
95
+ artifactId: `${safeStrategyEvidenceSegment(payload.strategy)}_${compactStrategyEvidenceTimestamp(payload.createdAt)}_${payloadSha256.slice(0, 16)}`,
96
+ payloadSha256,
97
+ payload
98
+ };
99
+ };
100
+ var verifyStrategyEvidenceMarkerEnvelope = (value) => {
101
+ const envelope = asRecord(value);
102
+ const payload = asRecord(envelope?.payload);
103
+ if (envelope?.schema !== import_types.STRATEGY_EVIDENCE_MARKERS_SCHEMA || !isString(envelope.artifactId) || !isString(envelope.payloadSha256) || !SHA256_RE.test(envelope.payloadSha256) || !payload || !isString(payload.strategy) || !isNumber(payload.createdAt) || !Array.isArray(payload.markers) || !Array.isArray(payload.sourceArtifacts)) {
104
+ throw new Error("Invalid strategy evidence marker envelope");
105
+ }
106
+ for (const value2 of payload.markers) {
107
+ const marker = asRecord(value2);
108
+ const coverage = asRecord(marker?.coverage);
109
+ if (!marker || !isString(marker.id) || !isString(marker.type) || !MARKER_TYPES.has(marker.type) || !isNumber(marker.timestamp) || !isString(marker.label) || !isString(marker.summary) || !isString(marker.artifactId) || !isString(marker.artifactSha256) || !SHA256_RE.test(marker.artifactSha256) || marker.gitSha !== void 0 && !isString(marker.gitSha) || marker.gateFingerprint !== void 0 && (!isString(marker.gateFingerprint) || !LINEAGE_FINGERPRINT_RE.test(marker.gateFingerprint)) || marker.configFingerprint !== void 0 && (!isString(marker.configFingerprint) || !LINEAGE_FINGERPRINT_RE.test(marker.configFingerprint)) || marker.contextFingerprint !== void 0 && (!isString(marker.contextFingerprint) || !LINEAGE_FINGERPRINT_RE.test(marker.contextFingerprint)) || marker.maxLossValue !== void 0 && !isNumber(marker.maxLossValue) || marker.coverage !== void 0 && (!coverage || !isNumber(coverage.startTime) || !isNumber(coverage.endTime) || coverage.startTime > coverage.endTime)) {
110
+ throw new Error("Invalid strategy evidence marker");
111
+ }
112
+ }
113
+ for (const value2 of payload.sourceArtifacts) {
114
+ const artifact = asRecord(value2);
115
+ if (!artifact || !isString(artifact.artifactId) || !isString(artifact.sha256) || !SHA256_RE.test(artifact.sha256) || artifact.path !== void 0 && typeof artifact.path !== "string") {
116
+ throw new Error("Invalid strategy evidence source artifact");
117
+ }
118
+ }
119
+ const payloadSha256 = strategyEvidenceSha256(payload);
120
+ if (payloadSha256 !== envelope.payloadSha256) {
121
+ throw new Error("Strategy evidence marker checksum mismatch");
122
+ }
123
+ const expectedId = `${safeStrategyEvidenceSegment(payload.strategy)}_${compactStrategyEvidenceTimestamp(payload.createdAt)}_${payloadSha256.slice(0, 16)}`;
124
+ if (expectedId !== envelope.artifactId) {
125
+ throw new Error("Strategy evidence marker artifact identity mismatch");
126
+ }
127
+ return value;
128
+ };
129
+ // Annotate the CommonJS export names for ESM import in node:
130
+ 0 && (module.exports = {
131
+ canonicalStrategyEvidenceJson,
132
+ compactStrategyEvidenceTimestamp,
133
+ createStrategyEvidenceMarkerEnvelope,
134
+ safeStrategyEvidenceSegment,
135
+ strategyEvidenceFileSha256,
136
+ strategyEvidenceFingerprint,
137
+ strategyEvidenceSha256,
138
+ strategyLogicConfigFingerprint,
139
+ verifyStrategyEvidenceMarkerEnvelope
140
+ });
@@ -0,0 +1,109 @@
1
+ // src/strategyReleaseEvidence.ts
2
+ import { createHash } from "crypto";
3
+ import { createReadStream } from "fs";
4
+ import {
5
+ STRATEGY_EVIDENCE_MARKERS_SCHEMA
6
+ } from "@tradejs/types";
7
+ var SHA256_RE = /^[a-f0-9]{64}$/;
8
+ var LINEAGE_FINGERPRINT_RE = /^[a-f0-9]{16}$/;
9
+ var MARKER_TYPES = /* @__PURE__ */ new Set([
10
+ "G",
11
+ "L",
12
+ "E",
13
+ "D",
14
+ "P",
15
+ "R"
16
+ ]);
17
+ var STRATEGY_LOGIC_BINDING_KEYS = /* @__PURE__ */ new Set([
18
+ "ACCOUNT_ID",
19
+ "DEPLOYMENT_ID",
20
+ "MAX_LOSS_VALUE",
21
+ "configId"
22
+ ]);
23
+ var SECRET_CONFIG_KEY_RE = /(?:^|_)(?:API_KEY|API_SECRET|TOKEN|PASSWORD|PRIVATE_KEY)$/i;
24
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
25
+ var isString = (value) => typeof value === "string" && value.trim().length > 0;
26
+ var isNumber = (value) => typeof value === "number" && Number.isFinite(value);
27
+ var canonicalStrategyEvidenceJson = (value) => {
28
+ const normalize = (current) => {
29
+ if (Array.isArray(current)) return current.map(normalize);
30
+ const record = asRecord(current);
31
+ if (!record) return current;
32
+ return Object.fromEntries(
33
+ Object.entries(record).filter(([, entry]) => entry !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalize(entry)])
34
+ );
35
+ };
36
+ return JSON.stringify(normalize(value));
37
+ };
38
+ var strategyEvidenceSha256 = (value) => createHash("sha256").update(canonicalStrategyEvidenceJson(value)).digest("hex");
39
+ var strategyEvidenceFingerprint = (value) => strategyEvidenceSha256(value).slice(0, 16);
40
+ var normalizeStrategyLogicConfig = (value) => {
41
+ if (Array.isArray(value)) return value.map(normalizeStrategyLogicConfig);
42
+ const record = asRecord(value);
43
+ if (!record) return value;
44
+ return Object.fromEntries(
45
+ Object.entries(record).filter(
46
+ ([key]) => !STRATEGY_LOGIC_BINDING_KEYS.has(key) && !SECRET_CONFIG_KEY_RE.test(key)
47
+ ).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalizeStrategyLogicConfig(entry)])
48
+ );
49
+ };
50
+ var strategyLogicConfigFingerprint = (value) => strategyEvidenceFingerprint(normalizeStrategyLogicConfig(value));
51
+ var strategyEvidenceFileSha256 = async (filePath) => {
52
+ const hash = createHash("sha256");
53
+ for await (const chunk of createReadStream(filePath)) hash.update(chunk);
54
+ return hash.digest("hex");
55
+ };
56
+ var safeStrategyEvidenceSegment = (value, fallback = "strategy") => {
57
+ const safe = value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-");
58
+ return safe && safe !== "." && safe !== ".." ? safe : fallback;
59
+ };
60
+ var compactStrategyEvidenceTimestamp = (timestamp) => new Date(timestamp).toISOString().replace(/[-:]/g, "").replace(/\.000Z$/, "Z");
61
+ var createStrategyEvidenceMarkerEnvelope = (payload) => {
62
+ const payloadSha256 = strategyEvidenceSha256(payload);
63
+ return {
64
+ schema: STRATEGY_EVIDENCE_MARKERS_SCHEMA,
65
+ artifactId: `${safeStrategyEvidenceSegment(payload.strategy)}_${compactStrategyEvidenceTimestamp(payload.createdAt)}_${payloadSha256.slice(0, 16)}`,
66
+ payloadSha256,
67
+ payload
68
+ };
69
+ };
70
+ var verifyStrategyEvidenceMarkerEnvelope = (value) => {
71
+ const envelope = asRecord(value);
72
+ const payload = asRecord(envelope?.payload);
73
+ if (envelope?.schema !== STRATEGY_EVIDENCE_MARKERS_SCHEMA || !isString(envelope.artifactId) || !isString(envelope.payloadSha256) || !SHA256_RE.test(envelope.payloadSha256) || !payload || !isString(payload.strategy) || !isNumber(payload.createdAt) || !Array.isArray(payload.markers) || !Array.isArray(payload.sourceArtifacts)) {
74
+ throw new Error("Invalid strategy evidence marker envelope");
75
+ }
76
+ for (const value2 of payload.markers) {
77
+ const marker = asRecord(value2);
78
+ const coverage = asRecord(marker?.coverage);
79
+ if (!marker || !isString(marker.id) || !isString(marker.type) || !MARKER_TYPES.has(marker.type) || !isNumber(marker.timestamp) || !isString(marker.label) || !isString(marker.summary) || !isString(marker.artifactId) || !isString(marker.artifactSha256) || !SHA256_RE.test(marker.artifactSha256) || marker.gitSha !== void 0 && !isString(marker.gitSha) || marker.gateFingerprint !== void 0 && (!isString(marker.gateFingerprint) || !LINEAGE_FINGERPRINT_RE.test(marker.gateFingerprint)) || marker.configFingerprint !== void 0 && (!isString(marker.configFingerprint) || !LINEAGE_FINGERPRINT_RE.test(marker.configFingerprint)) || marker.contextFingerprint !== void 0 && (!isString(marker.contextFingerprint) || !LINEAGE_FINGERPRINT_RE.test(marker.contextFingerprint)) || marker.maxLossValue !== void 0 && !isNumber(marker.maxLossValue) || marker.coverage !== void 0 && (!coverage || !isNumber(coverage.startTime) || !isNumber(coverage.endTime) || coverage.startTime > coverage.endTime)) {
80
+ throw new Error("Invalid strategy evidence marker");
81
+ }
82
+ }
83
+ for (const value2 of payload.sourceArtifacts) {
84
+ const artifact = asRecord(value2);
85
+ if (!artifact || !isString(artifact.artifactId) || !isString(artifact.sha256) || !SHA256_RE.test(artifact.sha256) || artifact.path !== void 0 && typeof artifact.path !== "string") {
86
+ throw new Error("Invalid strategy evidence source artifact");
87
+ }
88
+ }
89
+ const payloadSha256 = strategyEvidenceSha256(payload);
90
+ if (payloadSha256 !== envelope.payloadSha256) {
91
+ throw new Error("Strategy evidence marker checksum mismatch");
92
+ }
93
+ const expectedId = `${safeStrategyEvidenceSegment(payload.strategy)}_${compactStrategyEvidenceTimestamp(payload.createdAt)}_${payloadSha256.slice(0, 16)}`;
94
+ if (expectedId !== envelope.artifactId) {
95
+ throw new Error("Strategy evidence marker artifact identity mismatch");
96
+ }
97
+ return value;
98
+ };
99
+ export {
100
+ canonicalStrategyEvidenceJson,
101
+ compactStrategyEvidenceTimestamp,
102
+ createStrategyEvidenceMarkerEnvelope,
103
+ safeStrategyEvidenceSegment,
104
+ strategyEvidenceFileSha256,
105
+ strategyEvidenceFingerprint,
106
+ strategyEvidenceSha256,
107
+ strategyLogicConfigFingerprint,
108
+ verifyStrategyEvidenceMarkerEnvelope
109
+ };
@@ -1,3 +1,13 @@
1
+ import {
2
+ deleteCandles,
3
+ findContinuityGap,
4
+ getCandlesRange,
5
+ getDataEdges,
6
+ getDataEdgesForSymbols,
7
+ toRows,
8
+ upsertCandles,
9
+ waitForDbReady
10
+ } from "./chunk-YVIHTUV5.mjs";
1
11
  import {
2
12
  applyDerivativesMetricCoverage,
3
13
  getDerivativesBackfillCoverage,
@@ -53,16 +63,6 @@ import {
53
63
  getSpreadSummary,
54
64
  upsertSpreadRows
55
65
  } from "./chunk-2CZREG43.mjs";
56
- import {
57
- deleteCandles,
58
- findContinuityGap,
59
- getCandlesRange,
60
- getDataEdges,
61
- getDataEdgesForSymbols,
62
- toRows,
63
- upsertCandles,
64
- waitForDbReady
65
- } from "./chunk-YVIHTUV5.mjs";
66
66
  import {
67
67
  closeTimescalePool,
68
68
  configureTimescaleMarketContextSchemaMode,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/infra",
3
- "version": "2.0.19",
3
+ "version": "2.0.21",
4
4
  "description": "MIT-licensed server infrastructure adapters for TradeJS: Redis, Timescale, ML, logging, and IO.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -49,6 +49,11 @@
49
49
  "import": "./dist/backtestArtifacts.mjs",
50
50
  "require": "./dist/backtestArtifacts.js"
51
51
  },
52
+ "./coreResearch": {
53
+ "types": "./dist/coreResearch.d.ts",
54
+ "import": "./dist/coreResearch.mjs",
55
+ "require": "./dist/coreResearch.js"
56
+ },
52
57
  "./files": {
53
58
  "types": "./dist/files.d.ts",
54
59
  "import": "./dist/files.mjs",
@@ -84,6 +89,11 @@
84
89
  "import": "./dist/runtimeDeployments.mjs",
85
90
  "require": "./dist/runtimeDeployments.js"
86
91
  },
92
+ "./strategyReleaseEvidence": {
93
+ "types": "./dist/strategyReleaseEvidence.d.ts",
94
+ "import": "./dist/strategyReleaseEvidence.mjs",
95
+ "require": "./dist/strategyReleaseEvidence.js"
96
+ },
87
97
  "./userSettings": {
88
98
  "types": "./dist/userSettings.d.ts",
89
99
  "import": "./dist/userSettings.mjs",
@@ -133,7 +143,7 @@
133
143
  "dependencies": {
134
144
  "@grpc/grpc-js": "^1.14.4",
135
145
  "@grpc/proto-loader": "^0.8.1",
136
- "@tradejs/types": "^2.0.19",
146
+ "@tradejs/types": "^2.0.21",
137
147
  "chalk": "4.1.2",
138
148
  "date-fns": "^3.6.0",
139
149
  "ioredis": "5.11.1",