@gobing-ai/ts-runtime 0.2.8 → 0.3.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.
package/README.md CHANGED
@@ -4,14 +4,17 @@ Runtime abstraction layer — environment detection, file system, process execut
4
4
 
5
5
  ## Overview
6
6
 
7
- `ts-runtime` decouples application code from platform specifics. Instead of importing `node:fs` or `node:child_process` directly, consumers go through interfaces (`FileSystem`, `ProcessExecutor`) that resolve to the correct implementation at startup based on `RuntimeContext`. Node filesystem modules are loaded lazily by `NodeFileSystem`, so importing the package remains safe for Worker bundles that select `CloudflareFileSystem`.
7
+ `ts-runtime` decouples application code from platform specifics. Instead of importing `node:fs` or `node:child_process` directly, consumers go through interfaces (`FileSystem`, `SyncFileSystem`, `ProcessExecutor`, `SyncProcessExecutor`, `PipeProcessSpawner`) that resolve to the correct implementation at startup based on `RuntimeContext`. Node filesystem modules are loaded lazily by `NodeFileSystem`, so importing the package remains safe for Worker bundles that select `CloudflareFileSystem`.
8
8
 
9
9
  **Key abstractions:**
10
10
 
11
11
  | Concept | Interface | Bun/Node impl | Cloudflare impl |
12
12
  |---------|-----------|---------------|-----------------|
13
- | File system | `FileSystem` | `NodeFileSystem` | `CloudflareFileSystem` (unsupported filesystem facade) |
14
- | Process execution | `ProcessExecutor` | `NodeProcessExecutor` | — |
13
+ | Async file system | `FileSystem` | `NodeFileSystem` | `CloudflareFileSystem` (unsupported filesystem facade) |
14
+ | Sync file system | `SyncFileSystem` | `NodeSyncFileSystem` | — |
15
+ | Buffered process execution | `ProcessExecutor` | `NodeProcessExecutor` | — |
16
+ | Sync process execution | `SyncProcessExecutor` | `BunSyncProcessExecutor` | — |
17
+ | Pipe process spawning | `PipeProcessSpawner` | `BunPipeProcessSpawner` | — |
15
18
  | Configuration | `Config` (Zod schema) | YAML + env vars | YAML + env vars |
16
19
  | Context | `RuntimeContext` | service locator | service locator |
17
20
  | Tracing | `SpanContext` | `{ traceId, spanId }` | `{ traceId, spanId }` |
@@ -20,15 +23,6 @@ Runtime abstraction layer — environment detection, file system, process execut
20
23
 
21
24
  ```mermaid
22
25
  classDiagram
23
- class RuntimeFactory {
24
- <<interface>>
25
- +RuntimeName runtimeName
26
- +RuntimeCapabilities capabilities
27
- +createFileSystem() FileSystem
28
- +loadConfig() Promise~Config~
29
- +createContext() RuntimeContext
30
- }
31
-
32
26
  class RuntimeContext {
33
27
  +RuntimeScope scope
34
28
  +RuntimeName runtimeName
@@ -82,6 +76,23 @@ classDiagram
82
76
  +createLogStream(path) LogStream
83
77
  }
84
78
 
79
+ class SyncFileSystem {
80
+ <<interface>>
81
+ +readFile(path) string
82
+ +writeFile(path, content) void
83
+ +mkdir(path) void
84
+ +readDir(path) string[]
85
+ +unlink(path) void
86
+ }
87
+
88
+ class NodeSyncFileSystem {
89
+ +readFile(path) string
90
+ +writeFile(path, content) void
91
+ +mkdir(path) void
92
+ +readDir(path) string[]
93
+ +unlink(path) void
94
+ }
95
+
85
96
  class ProcessExecutor {
86
97
  <<interface>>
87
98
  +run(options) Promise~ProcessResult~
@@ -91,6 +102,35 @@ classDiagram
91
102
  +run(options) Promise~ProcessResult~
92
103
  }
93
104
 
105
+ class SyncProcessExecutor {
106
+ <<interface>>
107
+ +runSync(options) ProcessResult
108
+ }
109
+
110
+ class BunSyncProcessExecutor {
111
+ +runSync(options) ProcessResult
112
+ }
113
+
114
+ class PipeProcessSpawner {
115
+ <<interface>>
116
+ +spawn(options) PipeProcess
117
+ }
118
+
119
+ class BunPipeProcessSpawner {
120
+ +spawn(options) PipeProcess
121
+ }
122
+
123
+ class PipeProcess {
124
+ <<interface>>
125
+ +pid number?
126
+ +stdout ReadableStream?
127
+ +stderr ReadableStream?
128
+ +exited Promise~number?~
129
+ +writeStdin(input) void
130
+ +endStdin() void
131
+ +kill(signal?) void
132
+ }
133
+
94
134
  class Config {
95
135
  +AppConfig app
96
136
  +DatabaseConfig database
@@ -107,20 +147,23 @@ classDiagram
107
147
 
108
148
  FileSystem <|.. NodeFileSystem : implements
109
149
  FileSystem <|.. CloudflareFileSystem : implements
150
+ SyncFileSystem <|.. NodeSyncFileSystem : implements
110
151
  ProcessExecutor <|.. NodeProcessExecutor : implements
152
+ SyncProcessExecutor <|.. BunSyncProcessExecutor : implements
153
+ PipeProcessSpawner <|.. BunPipeProcessSpawner : implements
154
+ BunPipeProcessSpawner --> PipeProcess : creates
111
155
  RuntimeContext --> FileSystem : "fileSystem"
112
156
  RuntimeContext --> Config : "config"
113
157
  RuntimeContext --> ProcessExecutor : "processExecutor"
114
- RuntimeFactory --> RuntimeContext : creates
115
- RuntimeFactory --> FileSystem : creates
116
- RuntimeFactory --> Config : loadConfig()
117
158
  ```
118
159
 
119
160
  ## How It Works
120
161
 
121
- ### 1. Runtime detection
162
+ ### 1. Runtime selection
122
163
 
123
- At startup, a `RuntimeFactory` is selected based on the environment:
164
+ The active `FileSystem` is a process-global swapped via `setFileSystem` (default: `NodeFileSystem`);
165
+ `getFs()` returns it. A `RuntimeContext` is constructed directly with the scope, capabilities, and
166
+ services for the current environment — there is no factory abstraction (see ADR-008):
124
167
 
125
168
  ```ts
126
169
  import { createRuntimeContext } from '@gobing-ai/ts-runtime';
@@ -217,7 +260,7 @@ await loadStructuredConfig('rules.yaml', { fetch: myFetch }); // or in
217
260
 
218
261
  ### 5. File system abstraction
219
262
 
220
- All file operations go through the `FileSystem` interface. Swap implementations for testing:
263
+ Most file operations go through the async `FileSystem` interface. Swap implementations for testing:
221
264
 
222
265
  ```ts
223
266
  import { getFs } from '@gobing-ai/ts-runtime';
@@ -227,6 +270,18 @@ await fs.writeFile('output.json', JSON.stringify(data));
227
270
  const content = await fs.readFile('output.json');
228
271
  ```
229
272
 
273
+ Use `SyncFileSystem` only for APIs that must stay synchronous, such as config discovery at module
274
+ boundaries or compatibility wrappers. The sync seam still keeps direct `node:fs` access inside
275
+ `ts-runtime`.
276
+
277
+ ```ts
278
+ import { NodeSyncFileSystem } from '@gobing-ai/ts-runtime';
279
+
280
+ const fs = new NodeSyncFileSystem();
281
+ fs.writeFile('agents/coder.yaml', 'id: coder\n');
282
+ const files = fs.readDir('agents');
283
+ ```
284
+
230
285
  ### 6. Graceful disposal
231
286
 
232
287
  `RuntimeContext.dispose()` calls `dispose()` on every registered service that implements the pattern:
@@ -302,6 +357,9 @@ const config = buildConfigFromObject({
302
357
 
303
358
  ### Process execution
304
359
 
360
+ `NodeProcessExecutor` is the default buffered async executor. It returns a `ProcessResult` and only
361
+ throws on non-zero exits when `rejectOnError` is set.
362
+
305
363
  ```ts
306
364
  import { NodeProcessExecutor } from '@gobing-ai/ts-runtime';
307
365
 
@@ -318,6 +376,46 @@ console.log(result.stdout);
318
376
  console.log(`Duration: ${result.durationMs}ms`);
319
377
  ```
320
378
 
379
+ Use `BunSyncProcessExecutor` for synchronous command probes where the caller needs an immediate
380
+ result, for example checking git state while building a prompt preamble.
381
+
382
+ ```ts
383
+ import { BunSyncProcessExecutor } from '@gobing-ai/ts-runtime';
384
+
385
+ const exec = new BunSyncProcessExecutor();
386
+
387
+ const branch = exec.runSync({
388
+ command: 'git',
389
+ args: ['branch', '--show-current'],
390
+ cwd: '/path/to/repo',
391
+ rejectOnError: false,
392
+ });
393
+
394
+ if (branch.exitCode === 0) {
395
+ console.log(branch.stdout);
396
+ }
397
+ ```
398
+
399
+ Use `BunPipeProcessSpawner` when the host needs a long-running subprocess with writable stdin and
400
+ readable stdout/stderr streams. This is the primitive used by team-mode agent processes.
401
+
402
+ ```ts
403
+ import { BunPipeProcessSpawner } from '@gobing-ai/ts-runtime';
404
+
405
+ const process = new BunPipeProcessSpawner().spawn({
406
+ command: 'codex',
407
+ args: ['exec', 'Wait for task messages.'],
408
+ cwd: '/path/to/repo',
409
+ });
410
+
411
+ process.writeStdin('[task from=operator id=msg-1] Inspect packages/runtime\n');
412
+ process.endStdin();
413
+
414
+ const exitCode = await process.exited;
415
+ ```
416
+
417
+ Cloudflare Workers do not expose process execution; do not register process executors there.
418
+
321
419
  ### SpanContext (for telemetry)
322
420
 
323
421
  ```ts
package/dist/config.d.ts CHANGED
@@ -29,6 +29,15 @@ export declare const configSchema: z.ZodObject<{
29
29
  }, z.core.$strip>>;
30
30
  }, z.core.$strip>;
31
31
  export type Config = z.output<typeof configSchema>;
32
+ /** Raised when a YAML text cannot be parsed as a plain object. */
33
+ export declare class YamlParseError extends Error {
34
+ readonly innerError?: unknown | undefined;
35
+ constructor(message: string, innerError?: unknown | undefined);
36
+ }
37
+ /** Parse YAML text into a plain object. Returns `{}` for null/undefined/empty input. */
38
+ export declare function parseYamlObject(text: string): Record<string, unknown>;
39
+ /** Serialize a plain object to a YAML string. */
40
+ export declare function stringifyYamlObject(value: Record<string, unknown>): string;
32
41
  export declare class ConfigLoadError extends Error {
33
42
  readonly issues: ZodIssue[];
34
43
  constructor(message: string, issues?: ZodIssue[]);
@@ -37,11 +46,9 @@ export declare function getNodeEnv(): string;
37
46
  export declare function isTestEnv(): boolean;
38
47
  export declare function getProcessEnv(): Record<string, string | undefined>;
39
48
  export declare function getDatabaseUrl(): string | undefined;
49
+ /** Node-bun only: interpolates `${VAR}` from `process.env` (see note above). */
40
50
  export declare function interpolateEnv(value: string): string;
41
51
  export declare function interpolateTree(value: unknown): unknown;
42
- export declare function deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown>;
43
- export declare function flattenKeys(obj: Record<string, unknown>, prefix?: string): Record<string, string>;
44
- export declare function deFlattenKeys(entries: Record<string, string>): Record<string, unknown>;
45
52
  export declare function buildConfigFromObject(raw: Record<string, unknown>, options?: {
46
53
  overrides?: Partial<Config>;
47
54
  }): Config;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAEvC,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsBvB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AAInD,qBAAa,eAAgB,SAAQ,KAAK;IACtC,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,QAAQ,EAAO;CAKvD;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAElE;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAEnD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOvD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUnH;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,SAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAW7F;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAetF;AAED,wBAAgB,qBAAqB,CACjC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;CAAO,GAC9C,MAAM,CAUR;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYzE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;CAAO,GAAG,MAAM,CAE3G"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAEvC,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsBvB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AAInD,kEAAkE;AAClE,qBAAa,cAAe,SAAQ,KAAK;IAGjC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO;gBAD7B,OAAO,EAAE,MAAM,EACN,UAAU,CAAC,EAAE,OAAO,YAAA;CAKpC;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAerE;AAED,iDAAiD;AACjD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAE1E;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACtC,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,QAAQ,EAAO;CAKvD;AAKD,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAElE;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAEnD;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOvD;AAED,wBAAgB,qBAAqB,CACjC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;CAAO,GAC9C,MAAM,CAUR;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;CAAO,GAAG,MAAM,CAE3G"}
package/dist/config.js CHANGED
@@ -1,4 +1,5 @@
1
- import { parse as parseYaml } from 'yaml';
1
+ import { deepMerge } from '@gobing-ai/ts-utils';
2
+ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
2
3
  import { z } from 'zod';
3
4
  export const configSchema = z.object({
4
5
  app: z
@@ -24,6 +25,35 @@ export const configSchema = z.object({
24
25
  .default({ level: 'info', console: true, file: false, json: false }),
25
26
  });
26
27
  const ENV_INTERPOLATION_RE = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
28
+ /** Raised when a YAML text cannot be parsed as a plain object. */
29
+ export class YamlParseError extends Error {
30
+ innerError;
31
+ constructor(message, innerError) {
32
+ super(message);
33
+ this.innerError = innerError;
34
+ this.name = 'YamlParseError';
35
+ }
36
+ }
37
+ /** Parse YAML text into a plain object. Returns `{}` for null/undefined/empty input. */
38
+ export function parseYamlObject(text) {
39
+ let parsed;
40
+ try {
41
+ parsed = parseYaml(text);
42
+ }
43
+ catch (error) {
44
+ throw new YamlParseError(`YAML parsing failed: ${error.message}`, error instanceof Error ? error : undefined);
45
+ }
46
+ if (parsed === null || parsed === undefined)
47
+ return {};
48
+ if (typeof parsed !== 'object' || Array.isArray(parsed)) {
49
+ throw new YamlParseError('YAML must parse to an object');
50
+ }
51
+ return parsed;
52
+ }
53
+ /** Serialize a plain object to a YAML string. */
54
+ export function stringifyYamlObject(value) {
55
+ return stringifyYaml(value);
56
+ }
27
57
  export class ConfigLoadError extends Error {
28
58
  issues;
29
59
  constructor(message, issues = []) {
@@ -32,6 +62,8 @@ export class ConfigLoadError extends Error {
32
62
  this.issues = issues;
33
63
  }
34
64
  }
65
+ // These accessors read `process.env` directly and are node-bun only (ADR-008). On
66
+ // `cloudflare-workers` there is no `process`; inject config explicitly rather than calling these.
35
67
  export function getNodeEnv() {
36
68
  return process.env.NODE_ENV ?? 'development';
37
69
  }
@@ -44,6 +76,7 @@ export function getProcessEnv() {
44
76
  export function getDatabaseUrl() {
45
77
  return process.env.DATABASE_URL;
46
78
  }
79
+ /** Node-bun only: interpolates `${VAR}` from `process.env` (see note above). */
47
80
  export function interpolateEnv(value) {
48
81
  return value.replace(ENV_INTERPOLATION_RE, (_match, name) => process.env[name] ?? `\${${name}}`);
49
82
  }
@@ -57,48 +90,6 @@ export function interpolateTree(value) {
57
90
  }
58
91
  return value;
59
92
  }
60
- export function deepMerge(target, source) {
61
- const result = { ...target };
62
- for (const [key, value] of Object.entries(source)) {
63
- if (isPlainObject(value) && isPlainObject(result[key])) {
64
- result[key] = deepMerge(result[key], value);
65
- }
66
- else {
67
- result[key] = value;
68
- }
69
- }
70
- return result;
71
- }
72
- export function flattenKeys(obj, prefix = '') {
73
- const result = {};
74
- for (const [key, value] of Object.entries(obj)) {
75
- const fullKey = prefix ? `${prefix}.${key}` : key;
76
- if (isPlainObject(value)) {
77
- Object.assign(result, flattenKeys(value, fullKey));
78
- }
79
- else {
80
- result[fullKey] = JSON.stringify(value);
81
- }
82
- }
83
- return result;
84
- }
85
- export function deFlattenKeys(entries) {
86
- const result = {};
87
- for (const [key, rawValue] of Object.entries(entries)) {
88
- const parts = key.split('.');
89
- let current = result;
90
- for (const part of parts.slice(0, -1)) {
91
- if (!isPlainObject(current[part]))
92
- current[part] = {};
93
- current = current[part];
94
- }
95
- const last = parts.at(-1);
96
- if (last === undefined)
97
- continue;
98
- current[last] = parseConfigValue(rawValue);
99
- }
100
- return result;
101
- }
102
93
  export function buildConfigFromObject(raw, options = {}) {
103
94
  const interpolated = interpolateTree(raw);
104
95
  const merged = options.overrides
@@ -112,13 +103,7 @@ export function buildConfigFromObject(raw, options = {}) {
112
103
  }
113
104
  export function parseConfigYaml(yamlText) {
114
105
  try {
115
- const parsed = parseYaml(yamlText);
116
- if (parsed === null || parsed === undefined)
117
- return {};
118
- if (!isPlainObject(parsed)) {
119
- throw new ConfigLoadError('Config YAML must parse to an object');
120
- }
121
- return parsed;
106
+ return parseYamlObject(yamlText);
122
107
  }
123
108
  catch (error) {
124
109
  if (error instanceof ConfigLoadError)
@@ -129,14 +114,6 @@ export function parseConfigYaml(yamlText) {
129
114
  export function buildConfigFromYaml(yamlText, options = {}) {
130
115
  return buildConfigFromObject(parseConfigYaml(yamlText), options);
131
116
  }
132
- function parseConfigValue(value) {
133
- try {
134
- return JSON.parse(value);
135
- }
136
- catch {
137
- return value;
138
- }
139
- }
140
117
  function isPlainObject(value) {
141
118
  return typeof value === 'object' && value !== null && !Array.isArray(value);
142
119
  }
package/dist/context.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { Config } from './config';
2
2
  import type { FileSystem } from './fs';
3
- import type { RuntimeCapabilities, RuntimeFactory, RuntimeName } from './types';
3
+ import type { RuntimeCapabilities, RuntimeName } from './types';
4
4
  export type RuntimeScope = 'process' | 'server-request' | 'scheduled-event' | 'test';
5
5
  export interface RuntimeServiceMap {
6
6
  config: Config;
@@ -12,7 +12,6 @@ export interface RuntimeContextOptions<TServices extends RuntimeServiceMap = Run
12
12
  runtimeName?: RuntimeName;
13
13
  capabilities?: RuntimeCapabilities;
14
14
  services?: Partial<TServices>;
15
- factory?: RuntimeFactory;
16
15
  }
17
16
  export declare class RuntimeContext<TServices extends RuntimeServiceMap = RuntimeServiceMap> {
18
17
  readonly scope: RuntimeScope;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEhF,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,MAAM,CAAC;AAErF,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB,CAAC,SAAS,SAAS,iBAAiB,GAAG,iBAAiB;IAC1F,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,cAAc,CAAC;CAC5B;AAED,qBAAa,cAAc,CAAC,SAAS,SAAS,iBAAiB,GAAG,iBAAiB;IAC/E,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,QAAQ,CAAC,QAAQ,mDAA0D;gBAE/D,OAAO,GAAE,qBAAqB,CAAC,SAAS,CAAM;IAsB1D,QAAQ,CAAC,CAAC,SAAS,MAAM,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI;IAKxE,GAAG,CAAC,CAAC,SAAS,MAAM,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS;IAIhE,OAAO,CAAC,CAAC,SAAS,MAAM,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IAQxD,GAAG,CAAC,CAAC,SAAS,MAAM,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO;IAIzC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAejC;AAMD,wBAAgB,oBAAoB,CAAC,SAAS,SAAS,iBAAiB,GAAG,iBAAiB,EACxF,OAAO,GAAE,qBAAqB,CAAC,SAAS,CAAM,GAC/C,cAAc,CAAC,SAAS,CAAC,CAE3B"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEhE,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,MAAM,CAAC;AAErF,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB,CAAC,SAAS,SAAS,iBAAiB,GAAG,iBAAiB;IAC1F,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;CACjC;AAED,qBAAa,cAAc,CAAC,SAAS,SAAS,iBAAiB,GAAG,iBAAiB;IAC/E,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,QAAQ,CAAC,QAAQ,mDAA0D;gBAE/D,OAAO,GAAE,qBAAqB,CAAC,SAAS,CAAM;IAqB1D,QAAQ,CAAC,CAAC,SAAS,MAAM,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI;IAKxE,GAAG,CAAC,CAAC,SAAS,MAAM,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS;IAIhE,OAAO,CAAC,CAAC,SAAS,MAAM,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IAQxD,GAAG,CAAC,CAAC,SAAS,MAAM,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO;IAIzC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAejC;AAMD,wBAAgB,oBAAoB,CAAC,SAAS,SAAS,iBAAiB,GAAG,iBAAiB,EACxF,OAAO,GAAE,qBAAqB,CAAC,SAAS,CAAM,GAC/C,cAAc,CAAC,SAAS,CAAC,CAE3B"}
package/dist/context.js CHANGED
@@ -7,10 +7,9 @@ export class RuntimeContext {
7
7
  services = new Map();
8
8
  constructor(options = {}) {
9
9
  this.scope = options.scope ?? 'process';
10
- this.runtimeName = options.runtimeName ?? options.factory?.runtimeName ?? 'node-bun';
10
+ this.runtimeName = options.runtimeName ?? 'node-bun';
11
11
  this.capabilities =
12
12
  options.capabilities ??
13
- options.factory?.capabilities ??
14
13
  {
15
14
  hasFilesystem: true,
16
15
  hasProcessExecution: true,
package/dist/fs.d.ts CHANGED
@@ -22,6 +22,15 @@ export interface FileSystem {
22
22
  rename(src: string, dest: string): Promise<void>;
23
23
  createLogStream(path: string): LogStream;
24
24
  }
25
+ export interface SyncFileSystem {
26
+ readFile(path: string): string;
27
+ writeFile(path: string, content: string): void;
28
+ mkdir(path: string): void;
29
+ exists(path: string): boolean;
30
+ readDir(path: string): string[];
31
+ stat(path: string): FileStat | null;
32
+ unlink(path: string): void;
33
+ }
25
34
  export declare class NodeFileSystem implements FileSystem {
26
35
  readFile(path: string): Promise<string>;
27
36
  writeFile(path: string, content: string): Promise<void>;
@@ -36,6 +45,15 @@ export declare class NodeFileSystem implements FileSystem {
36
45
  rename(src: string, dest: string): Promise<void>;
37
46
  createLogStream(path: string): LogStream;
38
47
  }
48
+ export declare class NodeSyncFileSystem implements SyncFileSystem {
49
+ readFile(path: string): string;
50
+ writeFile(path: string, content: string): void;
51
+ mkdir(path: string): void;
52
+ exists(path: string): boolean;
53
+ readDir(path: string): string[];
54
+ stat(path: string): FileStat | null;
55
+ unlink(path: string): void;
56
+ }
39
57
  export declare class CloudflareFileSystem implements FileSystem {
40
58
  readFile(path: string): Promise<string>;
41
59
  writeFile(path: string, _content: string): Promise<void>;
@@ -53,6 +71,7 @@ export declare class CloudflareFileSystem implements FileSystem {
53
71
  export declare function setFileSystem(fileSystem: FileSystem): () => void;
54
72
  export declare function getFs(): FileSystem;
55
73
  export declare function ensureDirForFile(path: string, fs?: FileSystem): Promise<void>;
74
+ export declare function ensureDirForFileSync(path: string, fs: SyncFileSystem): void;
56
75
  export declare function atomicWriteFile(path: string, content: string, fs?: FileSystem): Promise<void>;
57
76
  export declare function atomicWriteJson(path: string, value: unknown, fs?: FileSystem): Promise<void>;
58
77
  export declare function readJsonFile<T = unknown>(path: string, fs?: FileSystem): Promise<T>;
package/dist/fs.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACrB,MAAM,IAAI,OAAO,CAAC;IAClB,WAAW,IAAI,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACtB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,GAAG,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5C;AAED,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAChC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IACpC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAkBD,qBAAa,cAAe,YAAW,UAAU;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKvC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMvD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUtC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAKxC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAe5C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKvC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;CAG3C;AAED,qBAAa,kBAAmB,YAAW,cAAc;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI9B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAK9C,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAQ7B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAI/B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAcnC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAG7B;AAuCD,qBAAa,oBAAqB,YAAW,UAAU;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIvC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIvC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAIxC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAI7C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIvC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;CAG3C;AAQD,wBAAgB,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,IAAI,CAMhE;AAED,wBAAgB,KAAK,IAAI,UAAU,CAElC;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,aAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhF;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,CAE3E;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,aAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhG;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,aAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/F;AAED,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,aAAU,GAAG,OAAO,CAAC,CAAC,CAAC,CAEtF;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,aAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7F;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,aAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAa3E;AAED,wBAAgB,cAAc,CAAC,QAAQ,SAAkB,GAAG,MAAM,CAWjE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAEhE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,aAAU,GAAG,SAAS,CAErE"}
package/dist/fs.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
2
+ import { dirnamePath, getProcessCwd, joinPath, resolvePath } from './path.js';
1
3
  let fsPromisesModule = null;
2
4
  let fsModule = null;
3
5
  function nodeFsPromises() {
@@ -76,37 +78,74 @@ export class NodeFileSystem {
76
78
  return new LazyNodeLogStream(path);
77
79
  }
78
80
  }
81
+ export class NodeSyncFileSystem {
82
+ readFile(path) {
83
+ return readFileSync(path, 'utf-8');
84
+ }
85
+ writeFile(path, content) {
86
+ ensureDirForFileSync(path, this);
87
+ writeFileSync(path, content, 'utf-8');
88
+ }
89
+ mkdir(path) {
90
+ mkdirSync(path, { recursive: true });
91
+ }
92
+ exists(path) {
93
+ try {
94
+ return this.stat(path) !== null;
95
+ }
96
+ catch {
97
+ return false;
98
+ }
99
+ }
100
+ readDir(path) {
101
+ return readdirSync(path);
102
+ }
103
+ stat(path) {
104
+ try {
105
+ const value = statSync(path);
106
+ return {
107
+ isFile: () => value.isFile(),
108
+ isDirectory: () => value.isDirectory(),
109
+ size: value.size,
110
+ mtimeMs: value.mtimeMs,
111
+ };
112
+ }
113
+ catch {
114
+ return null;
115
+ }
116
+ }
117
+ unlink(path) {
118
+ rmSync(path, { recursive: true, force: true });
119
+ }
120
+ }
79
121
  class LazyNodeLogStream {
80
122
  ready;
81
123
  ended = false;
82
- pending = [];
124
+ // Single serialized chain: every write/end is appended here, so the underlying stream observes
125
+ // them in call order regardless of how the resolving microtasks interleave. A per-write `shift()`
126
+ // off a shared buffer (the previous approach) could reorder writes that arrived in the same tick.
127
+ tail;
83
128
  constructor(path) {
84
129
  this.ready = nodeFs().then(({ createWriteStream, mkdirSync }) => {
85
130
  mkdirSync(dirnamePath(path), { recursive: true });
86
131
  const stream = createWriteStream(path, { flags: 'a' });
87
- for (const chunk of this.pending.splice(0))
88
- stream.write(chunk);
89
- if (this.ended)
90
- stream.end();
91
132
  return {
92
133
  write: (chunk) => stream.write(chunk),
93
134
  end: () => stream.end(),
94
135
  };
95
136
  });
137
+ this.tail = this.ready;
96
138
  }
97
139
  write(chunk) {
98
140
  if (this.ended)
99
141
  return;
100
- this.pending.push(chunk);
101
- void this.ready.then((stream) => {
102
- const next = this.pending.shift();
103
- if (next !== undefined)
104
- stream.write(next);
105
- });
142
+ this.tail = this.tail.then(() => this.ready.then((stream) => stream.write(chunk)));
106
143
  }
107
144
  end() {
145
+ if (this.ended)
146
+ return;
108
147
  this.ended = true;
109
- void this.ready.then((stream) => stream.end());
148
+ this.tail = this.tail.then(() => this.ready.then((stream) => stream.end()));
110
149
  }
111
150
  }
112
151
  const CLOUDFLARE_FS_ERROR = 'FileSystem is not available on Cloudflare Workers. Use D1, KV, or R2.';
@@ -165,9 +204,12 @@ export function getFs() {
165
204
  export async function ensureDirForFile(path, fs = getFs()) {
166
205
  await fs.mkdir(dirnamePath(path));
167
206
  }
207
+ export function ensureDirForFileSync(path, fs) {
208
+ fs.mkdir(dirnamePath(path));
209
+ }
168
210
  export async function atomicWriteFile(path, content, fs = getFs()) {
169
211
  await ensureDirForFile(path, fs);
170
- const tempPath = `${path}.${getProcessPid()}.${Date.now()}.tmp`;
212
+ const tempPath = `${path}.${getProcessPid()}.${uniqueToken()}.tmp`;
171
213
  await fs.writeFile(tempPath, content);
172
214
  await fs.rename(tempPath, path);
173
215
  }
@@ -223,55 +265,8 @@ function hasBunFile(path) {
223
265
  function getProcessPid() {
224
266
  return globalThis.process?.pid ?? 0;
225
267
  }
226
- function getProcessCwd() {
227
- return globalThis.process?.cwd?.() ?? '/';
228
- }
229
- function normalizeSeparators(path) {
230
- return path.replaceAll('\\', '/');
231
- }
232
- function isAbsolutePath(path) {
233
- return path.startsWith('/') || /^[A-Za-z]:\//.test(normalizeSeparators(path));
234
- }
235
- function dirnamePath(path) {
236
- const input = normalizeSeparators(path);
237
- if (/^\/+$/.test(input))
238
- return '/';
239
- const normalized = input.replace(/\/+$/, '');
240
- if (normalized === '' || normalized === '/')
241
- return normalized || '.';
242
- const index = normalized.lastIndexOf('/');
243
- if (index < 0)
244
- return '.';
245
- if (index === 0)
246
- return '/';
247
- return normalized.slice(0, index);
248
- }
249
- function joinPath(...segments) {
250
- const filtered = segments.filter((segment) => segment.length > 0).map(normalizeSeparators);
251
- if (filtered.length === 0)
252
- return '.';
253
- const absolute = isAbsolutePath(filtered[0] ?? '');
254
- const joined = filtered.join('/').replace(/\/+/g, '/');
255
- return absolute ? joined : joined.replace(/^\//, '');
256
- }
257
- function resolvePath(...segments) {
258
- const candidates = segments.length === 0 ? [getProcessCwd()] : segments;
259
- let resolved = '';
260
- for (const segment of candidates.map(normalizeSeparators)) {
261
- if (segment.length === 0)
262
- continue;
263
- resolved = isAbsolutePath(segment) ? segment : joinPath(resolved || getProcessCwd(), segment);
264
- }
265
- const parts = [];
266
- const absolute = isAbsolutePath(resolved);
267
- for (const part of resolved.split('/')) {
268
- if (part === '' || part === '.')
269
- continue;
270
- if (part === '..') {
271
- parts.pop();
272
- continue;
273
- }
274
- parts.push(part);
275
- }
276
- return `${absolute ? '/' : ''}${parts.join('/')}` || (absolute ? '/' : '.');
268
+ // Two writers to the same path in the same millisecond must not share a temp name, or one clobbers
269
+ // the other before rename. randomUUID disambiguates; Date.now keeps names sortable for debugging.
270
+ function uniqueToken() {
271
+ return `${Date.now()}.${crypto.randomUUID()}`;
277
272
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './config';
2
2
  export * from './context';
3
3
  export * from './fs';
4
+ export * from './path';
4
5
  export * from './process-executor';
5
6
  export * from './schema-validation';
6
7
  export * from './types';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,MAAM,CAAC;AACrB,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,MAAM,CAAC;AACrB,cAAc,QAAQ,CAAC;AACvB,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,SAAS,CAAC"}