@gobing-ai/ts-infra 0.5.22 → 0.5.24
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 +4 -2
- package/dist/application-node.d.ts.map +1 -1
- package/dist/application-node.js +32 -7
- package/package.json +6 -6
- package/src/application-node.ts +33 -7
package/README.md
CHANGED
|
@@ -621,7 +621,7 @@ bootstrap:
|
|
|
621
621
|
logging:
|
|
622
622
|
level: info
|
|
623
623
|
console: true
|
|
624
|
-
filePath: ./logs/app.jsonl
|
|
624
|
+
filePath: ./logs/app.jsonl # a directory (e.g. ./logs/) writes daily spur{YYYYMMDD}.log files
|
|
625
625
|
telemetry:
|
|
626
626
|
enabled: true
|
|
627
627
|
serviceName: billing-api
|
|
@@ -775,7 +775,9 @@ never opens files: it calls `attachFileObserver(lifecycleBus, filePath, writer)`
|
|
|
775
775
|
only when `services.fileObserverWriter` is provided; its portable default path is
|
|
776
776
|
`.spur/logs/system-events.jsonl`. The Node subpath `runNodeApplication` constructs a
|
|
777
777
|
Node writer and uses `createNodeFileSystem()` to resolve the path to
|
|
778
|
-
`<projectRoot>/.spur/logs/system-events.jsonl
|
|
778
|
+
`<projectRoot>/.spur/logs/system-events{YYYYMM}.jsonl` (one file per month). The Node
|
|
779
|
+
writer expands `{YYYYMM}` / `{YYYYMMDD}` (local date) in any `filePath` on each write, so a
|
|
780
|
+
long-running process rolls over without a restart. Set `config.events.fileObserver: false`
|
|
779
781
|
to disable; supply a custom `filePath` (or a test-stub writer via
|
|
780
782
|
`services.fileObserverWriter`) to override.
|
|
781
783
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application-node.d.ts","sourceRoot":"","sources":["../src/application-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAWH,OAAO,KAAK,EACR,2BAA2B,EAC3B,uBAAuB,EAEvB,kBAAkB,EAClB,qBAAqB,EAGrB,QAAQ,EACR,aAAa,EACb,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EACnB,MAAM,qBAAqB,CAAC;AAS7B,kGAAkG;AAClG,qBAAa,qBAAsB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI9B;
|
|
1
|
+
{"version":3,"file":"application-node.d.ts","sourceRoot":"","sources":["../src/application-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAWH,OAAO,KAAK,EACR,2BAA2B,EAC3B,uBAAuB,EAEvB,kBAAkB,EAClB,qBAAqB,EAGrB,QAAQ,EACR,aAAa,EACb,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EACnB,MAAM,qBAAqB,CAAC;AAS7B,kGAAkG;AAClG,qBAAa,qBAAsB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI9B;AA8PD;;;;GAIG;AACH,MAAM,WAAW,6BAA8B,SAAQ,gBAAgB;IACnE,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,uEAAuE;AACvE,MAAM,WAAW,sBAAsB,CAAC,UAAU,GAAG,OAAO,EAAE,OAAO,SAAS,QAAQ,GAAG,WAAW;IAChG,gEAAgE;IAChE,QAAQ,CAAC,YAAY,CAAC,EAAE,uBAAuB,CAAC,UAAU,CAAC,CAAC;IAC5D,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE;QACd,OAAO,CAAC,EAAE,cAAc,CAAC;QACzB,MAAM,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;QAChC,SAAS,CAAC,EAAE,6BAA6B,CAAC;QAC1C,SAAS,CAAC,EAAE,gBAAgB,CAAC;KAChC,CAAC;IACF,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,2BAA2B,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC;IACjF,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,kBAAkB,CAAC,UAAU,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvF,wDAAwD;IACxD,QAAQ,CAAC,IAAI,CAAC,EAAE,CACZ,GAAG,EAAE,kBAAkB,CAAC,UAAU,EAAE,OAAO,CAAC,EAC5C,MAAM,EAAE,qBAAqB,KAC5B,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7B;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,GAAG,OAAO,EAAE,OAAO,SAAS,QAAQ,GAAG,WAAW,EACjG,OAAO,EAAE,sBAAsB,CAAC,UAAU,EAAE,OAAO,CAAC,GACrD,OAAO,CAAC,kBAAkB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CA6JlD"}
|
package/dist/application-node.js
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
*
|
|
15
15
|
* @module application-node
|
|
16
16
|
*/
|
|
17
|
-
import { appendFileSync, mkdirSync, readFileSync } from 'node:fs';
|
|
18
|
-
import { dirname } from 'node:path';
|
|
17
|
+
import { appendFileSync, mkdirSync, readFileSync, statSync } from 'node:fs';
|
|
18
|
+
import { dirname, join } from 'node:path';
|
|
19
19
|
import { createDbAdapter } from '@gobing-ai/ts-db';
|
|
20
20
|
import { createNodeFileSystem, interpolateTree, parseYamlObject } from '@gobing-ai/ts-runtime';
|
|
21
21
|
import { runApplication } from './application/index.js';
|
|
@@ -159,11 +159,33 @@ function normalizeSchedulerJobs(raw) {
|
|
|
159
159
|
return jobs;
|
|
160
160
|
}
|
|
161
161
|
// ── File sink helper ──────────────────────────────────────────────────────
|
|
162
|
+
/**
|
|
163
|
+
* Expand `{YYYYMMDD}` / `{YYYYMM}` (local date) in a file path. Resolved per write,
|
|
164
|
+
* so a long-running process rolls over to the next file without a restart.
|
|
165
|
+
*/
|
|
166
|
+
function expandDateTokens(path, now = new Date()) {
|
|
167
|
+
const yyyymm = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}`;
|
|
168
|
+
const dd = String(now.getDate()).padStart(2, '0');
|
|
169
|
+
return path.replaceAll('{YYYYMMDD}', `${yyyymm}${dd}`).replaceAll('{YYYYMM}', yyyymm);
|
|
170
|
+
}
|
|
171
|
+
function isDirectoryPath(path) {
|
|
172
|
+
if (path.endsWith('/') || path.endsWith('\\'))
|
|
173
|
+
return true;
|
|
174
|
+
try {
|
|
175
|
+
return statSync(path).isDirectory();
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/** A directory `filePath` gets the daily-rotated `spur{YYYYMMDD}.log` inside it; a file path is used as-is. */
|
|
162
182
|
function createFileSink(filePath) {
|
|
183
|
+
const pattern = isDirectoryPath(filePath) ? join(filePath, 'spur{YYYYMMDD}.log') : filePath;
|
|
163
184
|
return (line) => {
|
|
164
185
|
try {
|
|
165
|
-
|
|
166
|
-
|
|
186
|
+
const target = expandDateTokens(pattern);
|
|
187
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
188
|
+
appendFileSync(target, line);
|
|
167
189
|
}
|
|
168
190
|
catch {
|
|
169
191
|
// Best-effort — don't crash on log write failure
|
|
@@ -347,7 +369,7 @@ export async function runNodeApplication(options) {
|
|
|
347
369
|
const fileObserverWriter = options.services?.fileObserverWriter ?? {
|
|
348
370
|
ensureDir: (dir) => {
|
|
349
371
|
try {
|
|
350
|
-
mkdirSync(dir, { recursive: true });
|
|
372
|
+
mkdirSync(expandDateTokens(dir), { recursive: true });
|
|
351
373
|
}
|
|
352
374
|
catch (error) {
|
|
353
375
|
// ensureDir contract: an existing directory is success. Node
|
|
@@ -359,10 +381,13 @@ export async function runNodeApplication(options) {
|
|
|
359
381
|
}
|
|
360
382
|
},
|
|
361
383
|
appendFile: (path, content) => {
|
|
362
|
-
|
|
384
|
+
const target = expandDateTokens(path);
|
|
385
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
386
|
+
appendFileSync(target, content);
|
|
363
387
|
},
|
|
364
388
|
};
|
|
365
|
-
|
|
389
|
+
// Monthly file by default so the raw bus trace never grows unbounded in one file.
|
|
390
|
+
const eventsFilePath = eventsOpts.filePath ?? injectedFileSystem.resolve('.spur', 'logs', 'system-events{YYYYMM}.jsonl');
|
|
366
391
|
const eventsConfig = {
|
|
367
392
|
...eventsOpts,
|
|
368
393
|
filePath: eventsFilePath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/ts-infra",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.24",
|
|
4
4
|
"description": "@gobing-ai/ts-infra — Infrastructure backbone: event bus, job queue, scheduler, telemetry, API client, and logging.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -78,12 +78,12 @@
|
|
|
78
78
|
"release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-infra-v<version> && git push --tags' && exit 1"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@gobing-ai/ts-utils": "^0.5.
|
|
81
|
+
"@gobing-ai/ts-utils": "^0.5.24",
|
|
82
82
|
"@logtape/logtape": "^2.0.0"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
|
-
"@gobing-ai/ts-db": "^0.5.
|
|
86
|
-
"@gobing-ai/ts-runtime": "^0.5.
|
|
85
|
+
"@gobing-ai/ts-db": "^0.5.24",
|
|
86
|
+
"@gobing-ai/ts-runtime": "^0.5.24",
|
|
87
87
|
"@opentelemetry/api": "^1.9.0",
|
|
88
88
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
|
89
89
|
"@opentelemetry/sdk-metrics": "^2.0.0",
|
|
@@ -116,8 +116,8 @@
|
|
|
116
116
|
}
|
|
117
117
|
},
|
|
118
118
|
"devDependencies": {
|
|
119
|
-
"@gobing-ai/ts-db": "^0.5.
|
|
120
|
-
"@gobing-ai/ts-runtime": "^0.5.
|
|
119
|
+
"@gobing-ai/ts-db": "^0.5.24",
|
|
120
|
+
"@gobing-ai/ts-runtime": "^0.5.24",
|
|
121
121
|
"@types/bun": "1.3.14",
|
|
122
122
|
"@opentelemetry/api": "^1.9.0",
|
|
123
123
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
package/src/application-node.ts
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* @module application-node
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { appendFileSync, mkdirSync, readFileSync } from 'node:fs';
|
|
19
|
-
import { dirname } from 'node:path';
|
|
18
|
+
import { appendFileSync, mkdirSync, readFileSync, statSync } from 'node:fs';
|
|
19
|
+
import { dirname, join } from 'node:path';
|
|
20
20
|
|
|
21
21
|
import { createDbAdapter, type DbAdapter } from '@gobing-ai/ts-db';
|
|
22
22
|
import { createNodeFileSystem, interpolateTree, parseYamlObject } from '@gobing-ai/ts-runtime';
|
|
@@ -217,11 +217,33 @@ function normalizeSchedulerJobs(raw: unknown): readonly SchedulerJobConfig[] {
|
|
|
217
217
|
|
|
218
218
|
// ── File sink helper ──────────────────────────────────────────────────────
|
|
219
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Expand `{YYYYMMDD}` / `{YYYYMM}` (local date) in a file path. Resolved per write,
|
|
222
|
+
* so a long-running process rolls over to the next file without a restart.
|
|
223
|
+
*/
|
|
224
|
+
function expandDateTokens(path: string, now = new Date()): string {
|
|
225
|
+
const yyyymm = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}`;
|
|
226
|
+
const dd = String(now.getDate()).padStart(2, '0');
|
|
227
|
+
return path.replaceAll('{YYYYMMDD}', `${yyyymm}${dd}`).replaceAll('{YYYYMM}', yyyymm);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function isDirectoryPath(path: string): boolean {
|
|
231
|
+
if (path.endsWith('/') || path.endsWith('\\')) return true;
|
|
232
|
+
try {
|
|
233
|
+
return statSync(path).isDirectory();
|
|
234
|
+
} catch {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** A directory `filePath` gets the daily-rotated `spur{YYYYMMDD}.log` inside it; a file path is used as-is. */
|
|
220
240
|
function createFileSink(filePath: string): (line: string) => void {
|
|
241
|
+
const pattern = isDirectoryPath(filePath) ? join(filePath, 'spur{YYYYMMDD}.log') : filePath;
|
|
221
242
|
return (line: string) => {
|
|
222
243
|
try {
|
|
223
|
-
|
|
224
|
-
|
|
244
|
+
const target = expandDateTokens(pattern);
|
|
245
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
246
|
+
appendFileSync(target, line);
|
|
225
247
|
} catch {
|
|
226
248
|
// Best-effort — don't crash on log write failure
|
|
227
249
|
}
|
|
@@ -470,7 +492,7 @@ export async function runNodeApplication<TAppConfig = unknown, TEvents extends E
|
|
|
470
492
|
const fileObserverWriter = options.services?.fileObserverWriter ?? {
|
|
471
493
|
ensureDir: (dir: string) => {
|
|
472
494
|
try {
|
|
473
|
-
mkdirSync(dir, { recursive: true });
|
|
495
|
+
mkdirSync(expandDateTokens(dir), { recursive: true });
|
|
474
496
|
} catch (error) {
|
|
475
497
|
// ensureDir contract: an existing directory is success. Node
|
|
476
498
|
// semantics already suppress EEXIST with `recursive: true`, but
|
|
@@ -480,10 +502,14 @@ export async function runNodeApplication<TAppConfig = unknown, TEvents extends E
|
|
|
480
502
|
}
|
|
481
503
|
},
|
|
482
504
|
appendFile: (path: string, content: string) => {
|
|
483
|
-
|
|
505
|
+
const target = expandDateTokens(path);
|
|
506
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
507
|
+
appendFileSync(target, content);
|
|
484
508
|
},
|
|
485
509
|
};
|
|
486
|
-
|
|
510
|
+
// Monthly file by default so the raw bus trace never grows unbounded in one file.
|
|
511
|
+
const eventsFilePath =
|
|
512
|
+
eventsOpts.filePath ?? injectedFileSystem.resolve('.spur', 'logs', 'system-events{YYYYMM}.jsonl');
|
|
487
513
|
const eventsConfig: EventsOptions<TEvents> = {
|
|
488
514
|
...eventsOpts,
|
|
489
515
|
filePath: eventsFilePath,
|