@workflow/world-local 5.0.0-beta.10 → 5.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/config.d.ts +17 -5
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +18 -13
  4. package/dist/config.js.map +1 -1
  5. package/dist/fs.d.ts +58 -2
  6. package/dist/fs.d.ts.map +1 -1
  7. package/dist/fs.js +203 -24
  8. package/dist/fs.js.map +1 -1
  9. package/dist/index.d.ts +15 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +82 -5
  12. package/dist/index.js.map +1 -1
  13. package/dist/init.d.ts +91 -0
  14. package/dist/init.d.ts.map +1 -0
  15. package/dist/init.js +268 -0
  16. package/dist/init.js.map +1 -0
  17. package/dist/instrumentObject.d.ts +8 -0
  18. package/dist/instrumentObject.d.ts.map +1 -0
  19. package/dist/instrumentObject.js +66 -0
  20. package/dist/instrumentObject.js.map +1 -0
  21. package/dist/queue.d.ts +8 -1
  22. package/dist/queue.d.ts.map +1 -1
  23. package/dist/queue.js +164 -53
  24. package/dist/queue.js.map +1 -1
  25. package/dist/storage/events-storage.d.ts +7 -0
  26. package/dist/storage/events-storage.d.ts.map +1 -0
  27. package/dist/storage/events-storage.js +744 -0
  28. package/dist/storage/events-storage.js.map +1 -0
  29. package/dist/storage/filters.d.ts +22 -0
  30. package/dist/storage/filters.d.ts.map +1 -0
  31. package/dist/storage/filters.js +33 -0
  32. package/dist/storage/filters.js.map +1 -0
  33. package/dist/storage/helpers.d.ts +18 -0
  34. package/dist/storage/helpers.d.ts.map +1 -0
  35. package/dist/storage/helpers.js +44 -0
  36. package/dist/storage/helpers.js.map +1 -0
  37. package/dist/storage/hooks-storage.d.ts +12 -0
  38. package/dist/storage/hooks-storage.d.ts.map +1 -0
  39. package/dist/storage/hooks-storage.js +92 -0
  40. package/dist/storage/hooks-storage.js.map +1 -0
  41. package/dist/storage/index.d.ts +12 -0
  42. package/dist/storage/index.d.ts.map +1 -0
  43. package/dist/storage/index.js +32 -0
  44. package/dist/storage/index.js.map +1 -0
  45. package/dist/storage/legacy.d.ts +13 -0
  46. package/dist/storage/legacy.d.ts.map +1 -0
  47. package/dist/storage/legacy.js +73 -0
  48. package/dist/storage/legacy.js.map +1 -0
  49. package/dist/storage/runs-storage.d.ts +7 -0
  50. package/dist/storage/runs-storage.d.ts.map +1 -0
  51. package/dist/storage/runs-storage.js +58 -0
  52. package/dist/storage/runs-storage.js.map +1 -0
  53. package/dist/storage/steps-storage.d.ts +7 -0
  54. package/dist/storage/steps-storage.d.ts.map +1 -0
  55. package/dist/storage/steps-storage.js +49 -0
  56. package/dist/storage/steps-storage.js.map +1 -0
  57. package/dist/storage.d.ts +9 -2
  58. package/dist/storage.d.ts.map +1 -1
  59. package/dist/storage.js +8 -455
  60. package/dist/storage.js.map +1 -1
  61. package/dist/streamer.d.ts +4 -2
  62. package/dist/streamer.d.ts.map +1 -1
  63. package/dist/streamer.js +451 -104
  64. package/dist/streamer.js.map +1 -1
  65. package/dist/telemetry.d.ts +28 -0
  66. package/dist/telemetry.d.ts.map +1 -0
  67. package/dist/telemetry.js +71 -0
  68. package/dist/telemetry.js.map +1 -0
  69. package/dist/test-helpers.d.ts +54 -0
  70. package/dist/test-helpers.d.ts.map +1 -0
  71. package/dist/test-helpers.js +118 -0
  72. package/dist/test-helpers.js.map +1 -0
  73. package/dist/util.js.map +1 -1
  74. package/package.json +12 -11
package/dist/index.js CHANGED
@@ -1,24 +1,101 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { rm } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { reenqueueActiveRuns, SPEC_VERSION_CURRENT } from '@workflow/world';
1
5
  import { config } from './config.js';
6
+ import { clearCreatedFilesCache, deleteJSON, listTaggedFiles, listTaggedFilesByExtension, readJSON, } from './fs.js';
7
+ import { initDataDir } from './init.js';
8
+ import { instrumentObject } from './instrumentObject.js';
2
9
  import { createQueue } from './queue.js';
10
+ import { hashToken } from './storage/helpers.js';
3
11
  import { createStorage } from './storage.js';
4
12
  import { createStreamer } from './streamer.js';
13
+ // Re-export init types and utilities for consumers
14
+ export { DataDirAccessError, DataDirVersionError, ensureDataDir, initDataDir, parseVersion, } from './init.js';
5
15
  /**
6
- * Creates an embedded world instance that combines queue, storage, and streamer functionalities.
16
+ * Creates a local world instance that combines queue, storage, and streamer functionalities.
7
17
  *
8
18
  * @param args - Optional configuration object
9
19
  * @param args.dataDir - Directory for storing workflow data (default: `.workflow-data/`)
10
20
  * @param args.port - Port override for queue transport (default: auto-detected)
11
21
  * @param args.baseUrl - Full base URL override for queue transport (default: `http://localhost:{port}`)
22
+ * @param args.tag - Optional tag to scope files (e.g., `vitest-0`). When set, files are written
23
+ * as `{id}.{tag}.json` and `clear()` only deletes files matching this tag.
24
+ * @throws {DataDirAccessError} If the data directory cannot be created or accessed
25
+ * @throws {DataDirVersionError} If the data directory version is incompatible
12
26
  */
13
- export function createEmbeddedWorld(args) {
27
+ export function createLocalWorld(args) {
14
28
  const definedArgs = args
15
29
  ? Object.fromEntries(Object.entries(args).filter(([, value]) => value !== undefined))
16
30
  : {};
17
31
  const mergedConfig = { ...config.value, ...definedArgs };
32
+ const tag = mergedConfig.tag;
33
+ const queue = createQueue(mergedConfig);
34
+ const storage = createStorage(mergedConfig.dataDir, tag);
18
35
  return {
19
- ...createQueue(mergedConfig),
20
- ...createStorage(mergedConfig.dataDir),
21
- ...createStreamer(mergedConfig.dataDir),
36
+ specVersion: SPEC_VERSION_CURRENT,
37
+ ...queue,
38
+ ...createStorage(mergedConfig.dataDir, tag),
39
+ ...instrumentObject('world.streams', {
40
+ ...createStreamer(mergedConfig.dataDir, tag),
41
+ ...(mergedConfig.streamFlushIntervalMs !== undefined && {
42
+ streamFlushIntervalMs: mergedConfig.streamFlushIntervalMs,
43
+ }),
44
+ }),
45
+ async start() {
46
+ await initDataDir(mergedConfig.dataDir);
47
+ await reenqueueActiveRuns(storage.runs, queue.queue, 'world-local');
48
+ },
49
+ async close() {
50
+ await queue.close();
51
+ },
52
+ async clear() {
53
+ if (tag) {
54
+ // Selectively delete only files matching this tag
55
+ const basedir = mergedConfig.dataDir;
56
+ // Delete hook token constraint files BEFORE deleting the hooks,
57
+ // since we need to read each hook to extract its token hash.
58
+ // Constraint files are untagged ({sha256}.json) so listTaggedFiles
59
+ // won't find them — we must resolve them via the hook data.
60
+ const hooksDir = path.join(basedir, 'hooks');
61
+ const taggedHookFiles = await listTaggedFiles(hooksDir, tag);
62
+ const { HookSchema } = await import('@workflow/world');
63
+ await Promise.all(taggedHookFiles.map(async (hookFile) => {
64
+ const hook = await readJSON(path.join(hooksDir, hookFile), HookSchema);
65
+ if (hook?.token) {
66
+ await deleteJSON(path.join(hooksDir, 'tokens', `${hashToken(hook.token)}.json`));
67
+ }
68
+ }));
69
+ // Delete tagged entity files across all directories
70
+ const entityDirs = [
71
+ 'runs',
72
+ 'steps',
73
+ 'events',
74
+ 'hooks',
75
+ 'waits',
76
+ 'streams/runs',
77
+ ];
78
+ await Promise.all(entityDirs.map(async (dir) => {
79
+ const fullDir = path.join(basedir, dir);
80
+ const files = await listTaggedFiles(fullDir, tag);
81
+ await Promise.all(files.map((f) => deleteJSON(path.join(fullDir, f))));
82
+ }));
83
+ // Clean up lock files used for atomic terminal-state guards
84
+ await fs
85
+ .rm(path.join(basedir, '.locks'), { recursive: true, force: true })
86
+ .catch(() => { });
87
+ // Delete tagged stream chunks (.{tag}.bin files)
88
+ const chunksDir = path.join(basedir, 'streams', 'chunks');
89
+ const taggedBinFiles = await listTaggedFilesByExtension(chunksDir, tag, '.bin');
90
+ await Promise.all(taggedBinFiles.map((f) => fs.unlink(path.join(chunksDir, f)).catch(() => { })));
91
+ // Clear the in-memory write cache so deleted paths are forgotten
92
+ clearCreatedFilesCache();
93
+ }
94
+ else {
95
+ await rm(mergedConfig.dataDir, { recursive: true, force: true });
96
+ await initDataDir(mergedConfig.dataDir);
97
+ }
98
+ },
22
99
  };
23
100
  }
24
101
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAsB;IACxD,MAAM,WAAW,GAAG,IAAI;QACtB,CAAC,CAAC,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAChE;QACH,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,YAAY,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,GAAG,WAAW,EAAE,CAAC;IACzD,OAAO;QACL,GAAG,WAAW,CAAC,YAAY,CAAC;QAC5B,GAAG,aAAa,CAAC,YAAY,CAAC,OAAO,CAAC;QACtC,GAAG,cAAc,CAAC,YAAY,CAAC,OAAO,CAAC;KACxC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport { rm } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { World } from '@workflow/world';\nimport { reenqueueActiveRuns, SPEC_VERSION_CURRENT } from '@workflow/world';\nimport type { Config } from './config.js';\nimport { config } from './config.js';\nimport {\n clearCreatedFilesCache,\n deleteJSON,\n listTaggedFiles,\n listTaggedFilesByExtension,\n readJSON,\n} from './fs.js';\nimport { initDataDir } from './init.js';\nimport { instrumentObject } from './instrumentObject.js';\nimport { createQueue, type DirectHandler } from './queue.js';\nimport { hashToken } from './storage/helpers.js';\nimport { createStorage } from './storage.js';\nimport { createStreamer } from './streamer.js';\n\n// Re-export init types and utilities for consumers\nexport {\n DataDirAccessError,\n DataDirVersionError,\n ensureDataDir,\n initDataDir,\n type ParsedVersion,\n parseVersion,\n} from './init.js';\n\nexport type { DirectHandler } from './queue.js';\n\nexport type LocalWorld = World & {\n /** Register a direct in-process handler for a queue prefix, bypassing HTTP. */\n registerHandler(\n prefix: '__wkf_step_' | '__wkf_workflow_',\n handler: DirectHandler\n ): void;\n /** Clear all workflow data (runs, steps, events, hooks, streams). */\n clear(): Promise<void>;\n};\n\n/**\n * Creates a local world instance that combines queue, storage, and streamer functionalities.\n *\n * @param args - Optional configuration object\n * @param args.dataDir - Directory for storing workflow data (default: `.workflow-data/`)\n * @param args.port - Port override for queue transport (default: auto-detected)\n * @param args.baseUrl - Full base URL override for queue transport (default: `http://localhost:{port}`)\n * @param args.tag - Optional tag to scope files (e.g., `vitest-0`). When set, files are written\n * as `{id}.{tag}.json` and `clear()` only deletes files matching this tag.\n * @throws {DataDirAccessError} If the data directory cannot be created or accessed\n * @throws {DataDirVersionError} If the data directory version is incompatible\n */\nexport function createLocalWorld(args?: Partial<Config>): LocalWorld {\n const definedArgs = args\n ? Object.fromEntries(\n Object.entries(args).filter(([, value]) => value !== undefined)\n )\n : {};\n const mergedConfig = { ...config.value, ...definedArgs };\n const tag = mergedConfig.tag;\n const queue = createQueue(mergedConfig);\n const storage = createStorage(mergedConfig.dataDir, tag);\n return {\n specVersion: SPEC_VERSION_CURRENT,\n ...queue,\n ...createStorage(mergedConfig.dataDir, tag),\n ...instrumentObject('world.streams', {\n ...createStreamer(mergedConfig.dataDir, tag),\n ...(mergedConfig.streamFlushIntervalMs !== undefined && {\n streamFlushIntervalMs: mergedConfig.streamFlushIntervalMs,\n }),\n }),\n async start() {\n await initDataDir(mergedConfig.dataDir);\n await reenqueueActiveRuns(storage.runs, queue.queue, 'world-local');\n },\n async close() {\n await queue.close();\n },\n async clear() {\n if (tag) {\n // Selectively delete only files matching this tag\n const basedir = mergedConfig.dataDir;\n\n // Delete hook token constraint files BEFORE deleting the hooks,\n // since we need to read each hook to extract its token hash.\n // Constraint files are untagged ({sha256}.json) so listTaggedFiles\n // won't find them — we must resolve them via the hook data.\n const hooksDir = path.join(basedir, 'hooks');\n const taggedHookFiles = await listTaggedFiles(hooksDir, tag);\n const { HookSchema } = await import('@workflow/world');\n await Promise.all(\n taggedHookFiles.map(async (hookFile) => {\n const hook = await readJSON(\n path.join(hooksDir, hookFile),\n HookSchema\n );\n if (hook?.token) {\n await deleteJSON(\n path.join(hooksDir, 'tokens', `${hashToken(hook.token)}.json`)\n );\n }\n })\n );\n\n // Delete tagged entity files across all directories\n const entityDirs = [\n 'runs',\n 'steps',\n 'events',\n 'hooks',\n 'waits',\n 'streams/runs',\n ];\n await Promise.all(\n entityDirs.map(async (dir) => {\n const fullDir = path.join(basedir, dir);\n const files = await listTaggedFiles(fullDir, tag);\n await Promise.all(\n files.map((f) => deleteJSON(path.join(fullDir, f)))\n );\n })\n );\n // Clean up lock files used for atomic terminal-state guards\n await fs\n .rm(path.join(basedir, '.locks'), { recursive: true, force: true })\n .catch(() => {});\n // Delete tagged stream chunks (.{tag}.bin files)\n const chunksDir = path.join(basedir, 'streams', 'chunks');\n const taggedBinFiles = await listTaggedFilesByExtension(\n chunksDir,\n tag,\n '.bin'\n );\n await Promise.all(\n taggedBinFiles.map((f) =>\n fs.unlink(path.join(chunksDir, f)).catch(() => {})\n )\n );\n // Clear the in-memory write cache so deleted paths are forgotten\n clearCreatedFilesCache();\n } else {\n await rm(mergedConfig.dataDir, { recursive: true, force: true });\n await initDataDir(mergedConfig.dataDir);\n }\n },\n };\n}\n"]}
package/dist/init.d.ts ADDED
@@ -0,0 +1,91 @@
1
+ interface PackageInfo {
2
+ name: string;
3
+ version: string;
4
+ }
5
+ /**
6
+ * Returns the package name and version from package.json.
7
+ * The result is cached after the first read.
8
+ *
9
+ * In bundled contexts where package.json cannot be read,
10
+ * returns 'bundled' as the version.
11
+ */
12
+ export declare function getPackageInfo(): Promise<PackageInfo>;
13
+ /**
14
+ * Represents a parsed semantic version with optional prerelease tag.
15
+ */
16
+ export interface ParsedVersion {
17
+ major: number;
18
+ minor: number;
19
+ patch: number;
20
+ prerelease?: string;
21
+ raw: string;
22
+ }
23
+ /**
24
+ * Error thrown when the data directory cannot be accessed or created.
25
+ */
26
+ export declare class DataDirAccessError extends Error {
27
+ readonly dataDir: string;
28
+ readonly code?: string;
29
+ constructor(message: string, dataDir: string, code?: string);
30
+ }
31
+ /**
32
+ * Error thrown when data directory version is incompatible.
33
+ */
34
+ export declare class DataDirVersionError extends Error {
35
+ readonly oldVersion: ParsedVersion;
36
+ readonly newVersion: ParsedVersion;
37
+ readonly suggestedVersion?: string;
38
+ constructor(message: string, oldVersion: ParsedVersion, newVersion: ParsedVersion, suggestedVersion?: string);
39
+ }
40
+ /**
41
+ * Parses a version string into its components.
42
+ *
43
+ * @param versionString - Version string like "4.0.1" or "4.0.1-beta.20"
44
+ * @returns Parsed version object with major, minor, patch, and optional prerelease
45
+ */
46
+ export declare function parseVersion(versionString: string): ParsedVersion;
47
+ /**
48
+ * Formats a parsed version back to a string.
49
+ */
50
+ export declare function formatVersion(version: ParsedVersion): string;
51
+ /**
52
+ * Parses the version file content to extract package name and version.
53
+ *
54
+ * @param content - Content like "@workflow/world-local@4.0.1-beta.20"
55
+ * @returns Object with packageName and version
56
+ */
57
+ export declare function parseVersionFile(content: string): {
58
+ packageName: string;
59
+ version: ParsedVersion;
60
+ };
61
+ /**
62
+ * Formats the version file content.
63
+ */
64
+ export declare function formatVersionFile(packageName: string, version: ParsedVersion): string;
65
+ /**
66
+ * Handles version upgrades between old and new versions.
67
+ * This function is called when the data directory was created with a different version.
68
+ *
69
+ * @param oldVersion - The version that created the data directory
70
+ * @param newVersion - The current package version
71
+ * @throws {DataDirVersionError} If the versions are incompatible
72
+ */
73
+ export declare function upgradeVersion(oldVersion: ParsedVersion, newVersion: ParsedVersion): void;
74
+ /**
75
+ * Ensures the data directory exists and is writable.
76
+ * Creates the directory if it doesn't exist.
77
+ *
78
+ * @param dataDir - The path to the data directory
79
+ * @throws {DataDirAccessError} If the directory cannot be created or accessed
80
+ */
81
+ export declare function ensureDataDir(dataDir: string): Promise<void>;
82
+ /**
83
+ * Initializes the data directory, ensuring it exists, is accessible,
84
+ * and handles version compatibility.
85
+ *
86
+ * @param dataDir - The path to the data directory
87
+ * @throws {DataDirAccessError} If the directory cannot be created or accessed
88
+ */
89
+ export declare function initDataDir(dataDir: string): Promise<void>;
90
+ export {};
91
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAcA,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAgBD;;;;;;GAMG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC,CAyB3D;AAKD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAM5D;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;gBAGjC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,aAAa,EACzB,UAAU,EAAE,aAAa,EACzB,gBAAgB,CAAC,EAAE,MAAM;CAQ5B;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,aAAa,CAejE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAG5D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,aAAa,CAAC;CACxB,CAeA;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,aAAa,GACrB,MAAM,CAER;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,aAAa,EACzB,UAAU,EAAE,aAAa,GACxB,IAAI,CAIN;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkDlE;AA4DD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgDhE"}
package/dist/init.js ADDED
@@ -0,0 +1,268 @@
1
+ import { access, constants, mkdir, readFile, unlink, writeFile, } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ /** Package name - hardcoded since it doesn't change */
5
+ const PACKAGE_NAME = '@workflow/world-local';
6
+ let cachedPackageInfo = null;
7
+ /**
8
+ * Get the directory path for this module.
9
+ * Works in ESM and falls back to a constant in CJS contexts (which shouldn't happen)
10
+ */
11
+ function getModuleDir() {
12
+ // In bundled CJS contexts, import.meta.url may be undefined or empty
13
+ if (typeof import.meta.url === 'string' && import.meta.url) {
14
+ return path.dirname(fileURLToPath(import.meta.url));
15
+ }
16
+ return null;
17
+ }
18
+ /**
19
+ * Returns the package name and version from package.json.
20
+ * The result is cached after the first read.
21
+ *
22
+ * In bundled contexts where package.json cannot be read,
23
+ * returns 'bundled' as the version.
24
+ */
25
+ export async function getPackageInfo() {
26
+ if (cachedPackageInfo) {
27
+ return cachedPackageInfo;
28
+ }
29
+ const moduleDir = getModuleDir();
30
+ if (moduleDir) {
31
+ try {
32
+ const content = await readFile(path.join(moduleDir, '../package.json'), 'utf-8');
33
+ cachedPackageInfo = JSON.parse(content);
34
+ return cachedPackageInfo;
35
+ }
36
+ catch {
37
+ // Fall through to bundled fallback
38
+ }
39
+ }
40
+ // Bundled context - package.json not accessible
41
+ cachedPackageInfo = {
42
+ name: PACKAGE_NAME,
43
+ version: 'bundled',
44
+ };
45
+ return cachedPackageInfo;
46
+ }
47
+ /** Filename for storing version information in the data directory */
48
+ const VERSION_FILENAME = 'version.txt';
49
+ /**
50
+ * Error thrown when the data directory cannot be accessed or created.
51
+ */
52
+ export class DataDirAccessError extends Error {
53
+ dataDir;
54
+ code;
55
+ constructor(message, dataDir, code) {
56
+ super(message);
57
+ this.name = 'DataDirAccessError';
58
+ this.dataDir = dataDir;
59
+ this.code = code;
60
+ }
61
+ }
62
+ /**
63
+ * Error thrown when data directory version is incompatible.
64
+ */
65
+ export class DataDirVersionError extends Error {
66
+ oldVersion;
67
+ newVersion;
68
+ suggestedVersion;
69
+ constructor(message, oldVersion, newVersion, suggestedVersion) {
70
+ super(message);
71
+ this.name = 'DataDirVersionError';
72
+ this.oldVersion = oldVersion;
73
+ this.newVersion = newVersion;
74
+ this.suggestedVersion = suggestedVersion;
75
+ }
76
+ }
77
+ /**
78
+ * Parses a version string into its components.
79
+ *
80
+ * @param versionString - Version string like "4.0.1" or "4.0.1-beta.20"
81
+ * @returns Parsed version object with major, minor, patch, and optional prerelease
82
+ */
83
+ export function parseVersion(versionString) {
84
+ // Match: major.minor.patch with optional prerelease
85
+ const match = versionString.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
86
+ if (!match) {
87
+ throw new Error(`Invalid version string: "${versionString}"`);
88
+ }
89
+ return {
90
+ major: parseInt(match[1], 10),
91
+ minor: parseInt(match[2], 10),
92
+ patch: parseInt(match[3], 10),
93
+ prerelease: match[4],
94
+ raw: versionString,
95
+ };
96
+ }
97
+ /**
98
+ * Formats a parsed version back to a string.
99
+ */
100
+ export function formatVersion(version) {
101
+ const base = `${version.major}.${version.minor}.${version.patch}`;
102
+ return version.prerelease ? `${base}-${version.prerelease}` : base;
103
+ }
104
+ /**
105
+ * Parses the version file content to extract package name and version.
106
+ *
107
+ * @param content - Content like "@workflow/world-local@4.0.1-beta.20"
108
+ * @returns Object with packageName and version
109
+ */
110
+ export function parseVersionFile(content) {
111
+ const trimmed = content.trim();
112
+ const lastAtIndex = trimmed.lastIndexOf('@');
113
+ if (lastAtIndex <= 0) {
114
+ throw new Error(`Invalid version file content: "${content}"`);
115
+ }
116
+ const packageName = trimmed.substring(0, lastAtIndex);
117
+ const versionString = trimmed.substring(lastAtIndex + 1);
118
+ return {
119
+ packageName,
120
+ version: parseVersion(versionString),
121
+ };
122
+ }
123
+ /**
124
+ * Formats the version file content.
125
+ */
126
+ export function formatVersionFile(packageName, version) {
127
+ return `${packageName}@${formatVersion(version)}`;
128
+ }
129
+ /**
130
+ * Handles version upgrades between old and new versions.
131
+ * This function is called when the data directory was created with a different version.
132
+ *
133
+ * @param oldVersion - The version that created the data directory
134
+ * @param newVersion - The current package version
135
+ * @throws {DataDirVersionError} If the versions are incompatible
136
+ */
137
+ export function upgradeVersion(oldVersion, newVersion) {
138
+ console.log(`[world-local] Upgrading from version ${formatVersion(oldVersion)} to ${formatVersion(newVersion)}`);
139
+ }
140
+ /**
141
+ * Ensures the data directory exists and is writable.
142
+ * Creates the directory if it doesn't exist.
143
+ *
144
+ * @param dataDir - The path to the data directory
145
+ * @throws {DataDirAccessError} If the directory cannot be created or accessed
146
+ */
147
+ export async function ensureDataDir(dataDir) {
148
+ const absolutePath = path.resolve(dataDir);
149
+ // Try to create the directory if it doesn't exist
150
+ try {
151
+ await mkdir(absolutePath, { recursive: true });
152
+ }
153
+ catch (error) {
154
+ const nodeError = error;
155
+ // EEXIST is fine - directory already exists
156
+ if (nodeError.code !== 'EEXIST') {
157
+ throw new DataDirAccessError(`Failed to create data directory "${absolutePath}": ${nodeError.message}`, absolutePath, nodeError.code);
158
+ }
159
+ }
160
+ // Verify the directory is accessible (readable)
161
+ try {
162
+ await access(absolutePath, constants.R_OK);
163
+ }
164
+ catch (error) {
165
+ const nodeError = error;
166
+ throw new DataDirAccessError(`Data directory "${absolutePath}" is not readable: ${nodeError.message}`, absolutePath, nodeError.code);
167
+ }
168
+ // Verify the directory is writable by attempting to write a temp file
169
+ const testFile = path.join(absolutePath, `.workflow-write-test-${Date.now()}`);
170
+ try {
171
+ await writeFile(testFile, '');
172
+ // Clean up test file — ENOENT during cleanup is harmless (may race
173
+ // with another process performing the same check concurrently)
174
+ await unlink(testFile).catch((e) => {
175
+ if (e.code !== 'ENOENT')
176
+ throw e;
177
+ });
178
+ }
179
+ catch (error) {
180
+ const nodeError = error;
181
+ throw new DataDirAccessError(`Data directory "${absolutePath}" is not writable: ${nodeError.message}`, absolutePath, nodeError.code);
182
+ }
183
+ }
184
+ /**
185
+ * Reads the version from the data directory's version file.
186
+ *
187
+ * @param dataDir - Path to the data directory
188
+ * @returns The parsed version info, or null if the file doesn't exist
189
+ */
190
+ async function readVersionFile(dataDir) {
191
+ const versionFilePath = path.join(path.resolve(dataDir), VERSION_FILENAME);
192
+ try {
193
+ const content = await readFile(versionFilePath, 'utf-8');
194
+ return parseVersionFile(content);
195
+ }
196
+ catch (error) {
197
+ const nodeError = error;
198
+ if (nodeError.code === 'ENOENT') {
199
+ return null;
200
+ }
201
+ throw error;
202
+ }
203
+ }
204
+ /**
205
+ * Writes the current version to the data directory's version file.
206
+ *
207
+ * @param dataDir - Path to the data directory
208
+ * @param version - The version to write
209
+ */
210
+ async function writeVersionFile(dataDir, version) {
211
+ const versionFilePath = path.join(path.resolve(dataDir), VERSION_FILENAME);
212
+ const packageInfo = await getPackageInfo();
213
+ const content = formatVersionFile(packageInfo.name, version);
214
+ await writeFile(versionFilePath, content);
215
+ }
216
+ /**
217
+ * Gets the suggested downgrade version based on the old version.
218
+ * If a specific version is suggested in the error, use that.
219
+ * Otherwise, suggest the previous minor version if patch is 0,
220
+ * or previous major version if minor is also 0.
221
+ */
222
+ function getSuggestedDowngradeVersion(oldVersion, suggestedVersion) {
223
+ if (suggestedVersion) {
224
+ return suggestedVersion;
225
+ }
226
+ // Suggest the old version as the downgrade target
227
+ return formatVersion(oldVersion);
228
+ }
229
+ /**
230
+ * Initializes the data directory, ensuring it exists, is accessible,
231
+ * and handles version compatibility.
232
+ *
233
+ * @param dataDir - The path to the data directory
234
+ * @throws {DataDirAccessError} If the directory cannot be created or accessed
235
+ */
236
+ export async function initDataDir(dataDir) {
237
+ // First ensure the directory exists and is accessible
238
+ await ensureDataDir(dataDir);
239
+ const packageInfo = await getPackageInfo();
240
+ const currentVersion = parseVersion(packageInfo.version);
241
+ // Read existing version file
242
+ const existingVersionInfo = await readVersionFile(dataDir);
243
+ if (existingVersionInfo === null) {
244
+ // New data directory - write the current version
245
+ await writeVersionFile(dataDir, currentVersion);
246
+ return;
247
+ }
248
+ const { version: oldVersion } = existingVersionInfo;
249
+ // Check if versions are the same (no upgrade needed)
250
+ if (formatVersion(oldVersion) === formatVersion(currentVersion)) {
251
+ return;
252
+ }
253
+ // Attempt upgrade
254
+ try {
255
+ upgradeVersion(oldVersion, currentVersion);
256
+ // Upgrade succeeded - write the new version
257
+ await writeVersionFile(dataDir, currentVersion);
258
+ }
259
+ catch (error) {
260
+ const suggestedVersion = error instanceof DataDirVersionError ? error.suggestedVersion : undefined;
261
+ const downgradeTarget = getSuggestedDowngradeVersion(oldVersion, suggestedVersion);
262
+ console.error(`[world-local] Failed to upgrade data directory from version ${formatVersion(oldVersion)} to ${formatVersion(currentVersion)}:`, error instanceof Error ? error.message : error);
263
+ console.error(`[world-local] Data is not compatible with the current version. ` +
264
+ `Please downgrade to ${packageInfo.name}@${downgradeTarget}`);
265
+ throw error;
266
+ }
267
+ }
268
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n access,\n constants,\n mkdir,\n readFile,\n unlink,\n writeFile,\n} from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/** Package name - hardcoded since it doesn't change */\nconst PACKAGE_NAME = '@workflow/world-local';\n\ninterface PackageInfo {\n name: string;\n version: string;\n}\n\nlet cachedPackageInfo: PackageInfo | null = null;\n\n/**\n * Get the directory path for this module.\n * Works in ESM and falls back to a constant in CJS contexts (which shouldn't happen)\n */\nfunction getModuleDir(): string | null {\n // In bundled CJS contexts, import.meta.url may be undefined or empty\n if (typeof import.meta.url === 'string' && import.meta.url) {\n return path.dirname(fileURLToPath(import.meta.url));\n }\n return null;\n}\n\n/**\n * Returns the package name and version from package.json.\n * The result is cached after the first read.\n *\n * In bundled contexts where package.json cannot be read,\n * returns 'bundled' as the version.\n */\nexport async function getPackageInfo(): Promise<PackageInfo> {\n if (cachedPackageInfo) {\n return cachedPackageInfo;\n }\n\n const moduleDir = getModuleDir();\n if (moduleDir) {\n try {\n const content = await readFile(\n path.join(moduleDir, '../package.json'),\n 'utf-8'\n );\n cachedPackageInfo = JSON.parse(content) as PackageInfo;\n return cachedPackageInfo;\n } catch {\n // Fall through to bundled fallback\n }\n }\n\n // Bundled context - package.json not accessible\n cachedPackageInfo = {\n name: PACKAGE_NAME,\n version: 'bundled',\n };\n return cachedPackageInfo;\n}\n\n/** Filename for storing version information in the data directory */\nconst VERSION_FILENAME = 'version.txt';\n\n/**\n * Represents a parsed semantic version with optional prerelease tag.\n */\nexport interface ParsedVersion {\n major: number;\n minor: number;\n patch: number;\n prerelease?: string;\n raw: string;\n}\n\n/**\n * Error thrown when the data directory cannot be accessed or created.\n */\nexport class DataDirAccessError extends Error {\n readonly dataDir: string;\n readonly code?: string;\n\n constructor(message: string, dataDir: string, code?: string) {\n super(message);\n this.name = 'DataDirAccessError';\n this.dataDir = dataDir;\n this.code = code;\n }\n}\n\n/**\n * Error thrown when data directory version is incompatible.\n */\nexport class DataDirVersionError extends Error {\n readonly oldVersion: ParsedVersion;\n readonly newVersion: ParsedVersion;\n readonly suggestedVersion?: string;\n\n constructor(\n message: string,\n oldVersion: ParsedVersion,\n newVersion: ParsedVersion,\n suggestedVersion?: string\n ) {\n super(message);\n this.name = 'DataDirVersionError';\n this.oldVersion = oldVersion;\n this.newVersion = newVersion;\n this.suggestedVersion = suggestedVersion;\n }\n}\n\n/**\n * Parses a version string into its components.\n *\n * @param versionString - Version string like \"4.0.1\" or \"4.0.1-beta.20\"\n * @returns Parsed version object with major, minor, patch, and optional prerelease\n */\nexport function parseVersion(versionString: string): ParsedVersion {\n // Match: major.minor.patch with optional prerelease\n const match = versionString.match(/^(\\d+)\\.(\\d+)\\.(\\d+)(?:-(.+))?$/);\n\n if (!match) {\n throw new Error(`Invalid version string: \"${versionString}\"`);\n }\n\n return {\n major: parseInt(match[1], 10),\n minor: parseInt(match[2], 10),\n patch: parseInt(match[3], 10),\n prerelease: match[4],\n raw: versionString,\n };\n}\n\n/**\n * Formats a parsed version back to a string.\n */\nexport function formatVersion(version: ParsedVersion): string {\n const base = `${version.major}.${version.minor}.${version.patch}`;\n return version.prerelease ? `${base}-${version.prerelease}` : base;\n}\n\n/**\n * Parses the version file content to extract package name and version.\n *\n * @param content - Content like \"@workflow/world-local@4.0.1-beta.20\"\n * @returns Object with packageName and version\n */\nexport function parseVersionFile(content: string): {\n packageName: string;\n version: ParsedVersion;\n} {\n const trimmed = content.trim();\n const lastAtIndex = trimmed.lastIndexOf('@');\n\n if (lastAtIndex <= 0) {\n throw new Error(`Invalid version file content: \"${content}\"`);\n }\n\n const packageName = trimmed.substring(0, lastAtIndex);\n const versionString = trimmed.substring(lastAtIndex + 1);\n\n return {\n packageName,\n version: parseVersion(versionString),\n };\n}\n\n/**\n * Formats the version file content.\n */\nexport function formatVersionFile(\n packageName: string,\n version: ParsedVersion\n): string {\n return `${packageName}@${formatVersion(version)}`;\n}\n\n/**\n * Handles version upgrades between old and new versions.\n * This function is called when the data directory was created with a different version.\n *\n * @param oldVersion - The version that created the data directory\n * @param newVersion - The current package version\n * @throws {DataDirVersionError} If the versions are incompatible\n */\nexport function upgradeVersion(\n oldVersion: ParsedVersion,\n newVersion: ParsedVersion\n): void {\n console.log(\n `[world-local] Upgrading from version ${formatVersion(oldVersion)} to ${formatVersion(newVersion)}`\n );\n}\n\n/**\n * Ensures the data directory exists and is writable.\n * Creates the directory if it doesn't exist.\n *\n * @param dataDir - The path to the data directory\n * @throws {DataDirAccessError} If the directory cannot be created or accessed\n */\nexport async function ensureDataDir(dataDir: string): Promise<void> {\n const absolutePath = path.resolve(dataDir);\n\n // Try to create the directory if it doesn't exist\n try {\n await mkdir(absolutePath, { recursive: true });\n } catch (error: unknown) {\n const nodeError = error as NodeJS.ErrnoException;\n // EEXIST is fine - directory already exists\n if (nodeError.code !== 'EEXIST') {\n throw new DataDirAccessError(\n `Failed to create data directory \"${absolutePath}\": ${nodeError.message}`,\n absolutePath,\n nodeError.code\n );\n }\n }\n\n // Verify the directory is accessible (readable)\n try {\n await access(absolutePath, constants.R_OK);\n } catch (error: unknown) {\n const nodeError = error as NodeJS.ErrnoException;\n throw new DataDirAccessError(\n `Data directory \"${absolutePath}\" is not readable: ${nodeError.message}`,\n absolutePath,\n nodeError.code\n );\n }\n\n // Verify the directory is writable by attempting to write a temp file\n const testFile = path.join(\n absolutePath,\n `.workflow-write-test-${Date.now()}`\n );\n try {\n await writeFile(testFile, '');\n // Clean up test file — ENOENT during cleanup is harmless (may race\n // with another process performing the same check concurrently)\n await unlink(testFile).catch((e: NodeJS.ErrnoException) => {\n if (e.code !== 'ENOENT') throw e;\n });\n } catch (error: unknown) {\n const nodeError = error as NodeJS.ErrnoException;\n throw new DataDirAccessError(\n `Data directory \"${absolutePath}\" is not writable: ${nodeError.message}`,\n absolutePath,\n nodeError.code\n );\n }\n}\n\n/**\n * Reads the version from the data directory's version file.\n *\n * @param dataDir - Path to the data directory\n * @returns The parsed version info, or null if the file doesn't exist\n */\nasync function readVersionFile(dataDir: string): Promise<{\n packageName: string;\n version: ParsedVersion;\n} | null> {\n const versionFilePath = path.join(path.resolve(dataDir), VERSION_FILENAME);\n\n try {\n const content = await readFile(versionFilePath, 'utf-8');\n return parseVersionFile(content);\n } catch (error: unknown) {\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code === 'ENOENT') {\n return null;\n }\n throw error;\n }\n}\n\n/**\n * Writes the current version to the data directory's version file.\n *\n * @param dataDir - Path to the data directory\n * @param version - The version to write\n */\nasync function writeVersionFile(\n dataDir: string,\n version: ParsedVersion\n): Promise<void> {\n const versionFilePath = path.join(path.resolve(dataDir), VERSION_FILENAME);\n const packageInfo = await getPackageInfo();\n const content = formatVersionFile(packageInfo.name, version);\n await writeFile(versionFilePath, content);\n}\n\n/**\n * Gets the suggested downgrade version based on the old version.\n * If a specific version is suggested in the error, use that.\n * Otherwise, suggest the previous minor version if patch is 0,\n * or previous major version if minor is also 0.\n */\nfunction getSuggestedDowngradeVersion(\n oldVersion: ParsedVersion,\n suggestedVersion?: string\n): string {\n if (suggestedVersion) {\n return suggestedVersion;\n }\n\n // Suggest the old version as the downgrade target\n return formatVersion(oldVersion);\n}\n\n/**\n * Initializes the data directory, ensuring it exists, is accessible,\n * and handles version compatibility.\n *\n * @param dataDir - The path to the data directory\n * @throws {DataDirAccessError} If the directory cannot be created or accessed\n */\nexport async function initDataDir(dataDir: string): Promise<void> {\n // First ensure the directory exists and is accessible\n await ensureDataDir(dataDir);\n\n const packageInfo = await getPackageInfo();\n const currentVersion = parseVersion(packageInfo.version);\n\n // Read existing version file\n const existingVersionInfo = await readVersionFile(dataDir);\n\n if (existingVersionInfo === null) {\n // New data directory - write the current version\n await writeVersionFile(dataDir, currentVersion);\n return;\n }\n\n const { version: oldVersion } = existingVersionInfo;\n\n // Check if versions are the same (no upgrade needed)\n if (formatVersion(oldVersion) === formatVersion(currentVersion)) {\n return;\n }\n\n // Attempt upgrade\n try {\n upgradeVersion(oldVersion, currentVersion);\n // Upgrade succeeded - write the new version\n await writeVersionFile(dataDir, currentVersion);\n } catch (error: unknown) {\n const suggestedVersion =\n error instanceof DataDirVersionError ? error.suggestedVersion : undefined;\n\n const downgradeTarget = getSuggestedDowngradeVersion(\n oldVersion,\n suggestedVersion\n );\n\n console.error(\n `[world-local] Failed to upgrade data directory from version ${formatVersion(oldVersion)} to ${formatVersion(currentVersion)}:`,\n error instanceof Error ? error.message : error\n );\n console.error(\n `[world-local] Data is not compatible with the current version. ` +\n `Please downgrade to ${packageInfo.name}@${downgradeTarget}`\n );\n\n throw error;\n }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Wraps all methods of an object with tracing spans.
3
+ * @param prefix - Prefix for span names (e.g., "world.runs")
4
+ * @param o - Object with methods to instrument
5
+ * @returns Instrumented object with same interface
6
+ */
7
+ export declare function instrumentObject<T extends object>(prefix: string, o: T): T;
8
+ //# sourceMappingURL=instrumentObject.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentObject.d.ts","sourceRoot":"","sources":["../src/instrumentObject.ts"],"names":[],"mappings":"AAkCA;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAsC1E"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Utility to instrument object methods with tracing.
3
+ * This mirrors world-vercel's implementation for consistent observability.
4
+ */
5
+ import { trace, getSpanKind, PeerService, RpcSystem, RpcService, RpcMethod, } from './telemetry.js';
6
+ /** Configuration for peer service attribution */
7
+ const WORLD_LOCAL_SERVICE = {
8
+ peerService: 'world-local',
9
+ rpcSystem: 'local',
10
+ rpcService: 'world-local',
11
+ };
12
+ /**
13
+ * Extracts the event type from arguments for events.create calls.
14
+ * The event data is the second argument and contains eventType.
15
+ */
16
+ function extractEventType(args) {
17
+ if (args.length >= 2 && typeof args[1] === 'object' && args[1] !== null) {
18
+ const data = args[1];
19
+ if (typeof data.eventType === 'string') {
20
+ return data.eventType;
21
+ }
22
+ }
23
+ return undefined;
24
+ }
25
+ /**
26
+ * Wraps all methods of an object with tracing spans.
27
+ * @param prefix - Prefix for span names (e.g., "world.runs")
28
+ * @param o - Object with methods to instrument
29
+ * @returns Instrumented object with same interface
30
+ */
31
+ export function instrumentObject(prefix, o) {
32
+ const handlers = {};
33
+ for (const key of Object.keys(o)) {
34
+ if (typeof o[key] !== 'function') {
35
+ handlers[key] = o[key];
36
+ }
37
+ else {
38
+ const f = o[key];
39
+ const methodName = String(key);
40
+ // @ts-expect-error - dynamic function wrapping
41
+ handlers[key] = async (...args) => {
42
+ // Build span name - for events.create, include the event type
43
+ let spanName = `${prefix}.${methodName}`;
44
+ if (prefix === 'world.events' && methodName === 'create') {
45
+ const eventType = extractEventType(args);
46
+ if (eventType) {
47
+ spanName = `${prefix}.${methodName} ${eventType}`;
48
+ }
49
+ }
50
+ return trace(spanName, { kind: await getSpanKind('INTERNAL') }, async (span) => {
51
+ // Add peer service attributes for service maps
52
+ // Use spanName for rpc.method so Datadog shows event type in resource
53
+ span?.setAttributes({
54
+ ...PeerService(WORLD_LOCAL_SERVICE.peerService),
55
+ ...RpcSystem(WORLD_LOCAL_SERVICE.rpcSystem),
56
+ ...RpcService(WORLD_LOCAL_SERVICE.rpcService),
57
+ ...RpcMethod(spanName),
58
+ });
59
+ return f(...args);
60
+ });
61
+ };
62
+ }
63
+ }
64
+ return handlers;
65
+ }
66
+ //# sourceMappingURL=instrumentObject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentObject.js","sourceRoot":"","sources":["../src/instrumentObject.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,KAAK,EACL,WAAW,EACX,WAAW,EACX,SAAS,EACT,UAAU,EACV,SAAS,GACV,MAAM,gBAAgB,CAAC;AAExB,iDAAiD;AACjD,MAAM,mBAAmB,GAAG;IAC1B,WAAW,EAAE,aAAa;IAC1B,SAAS,EAAE,OAAO;IAClB,UAAU,EAAE,aAAa;CAC1B,CAAC;AAEF;;;GAGG;AACH,SAAS,gBAAgB,CAAC,IAAe;IACvC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACxE,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAA4B,CAAC;QAChD,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YACvC,OAAO,IAAI,CAAC,SAAS,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAmB,MAAc,EAAE,CAAI;IACrE,MAAM,QAAQ,GAAG,EAAO,CAAC;IACzB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAgB,EAAE,CAAC;QAChD,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,UAAU,EAAE,CAAC;YACjC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACjB,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YAC/B,+CAA+C;YAC/C,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAK,EAAE,GAAG,IAAe,EAAE,EAAE;gBAC3C,8DAA8D;gBAC9D,IAAI,QAAQ,GAAG,GAAG,MAAM,IAAI,UAAU,EAAE,CAAC;gBACzC,IAAI,MAAM,KAAK,cAAc,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;oBACzD,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;oBACzC,IAAI,SAAS,EAAE,CAAC;wBACd,QAAQ,GAAG,GAAG,MAAM,IAAI,UAAU,IAAI,SAAS,EAAE,CAAC;oBACpD,CAAC;gBACH,CAAC;gBAED,OAAO,KAAK,CACV,QAAQ,EACR,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC,UAAU,CAAC,EAAE,EACvC,KAAK,EAAE,IAAI,EAAE,EAAE;oBACb,+CAA+C;oBAC/C,sEAAsE;oBACtE,IAAI,EAAE,aAAa,CAAC;wBAClB,GAAG,WAAW,CAAC,mBAAmB,CAAC,WAAW,CAAC;wBAC/C,GAAG,SAAS,CAAC,mBAAmB,CAAC,SAAS,CAAC;wBAC3C,GAAG,UAAU,CAAC,mBAAmB,CAAC,UAAU,CAAC;wBAC7C,GAAG,SAAS,CAAC,QAAQ,CAAC;qBACvB,CAAC,CAAC;oBACH,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;gBACpB,CAAC,CACF,CAAC;YACJ,CAAC,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["/**\n * Utility to instrument object methods with tracing.\n * This mirrors world-vercel's implementation for consistent observability.\n */\nimport {\n trace,\n getSpanKind,\n PeerService,\n RpcSystem,\n RpcService,\n RpcMethod,\n} from './telemetry.js';\n\n/** Configuration for peer service attribution */\nconst WORLD_LOCAL_SERVICE = {\n peerService: 'world-local',\n rpcSystem: 'local',\n rpcService: 'world-local',\n};\n\n/**\n * Extracts the event type from arguments for events.create calls.\n * The event data is the second argument and contains eventType.\n */\nfunction extractEventType(args: unknown[]): string | undefined {\n if (args.length >= 2 && typeof args[1] === 'object' && args[1] !== null) {\n const data = args[1] as Record<string, unknown>;\n if (typeof data.eventType === 'string') {\n return data.eventType;\n }\n }\n return undefined;\n}\n\n/**\n * Wraps all methods of an object with tracing spans.\n * @param prefix - Prefix for span names (e.g., \"world.runs\")\n * @param o - Object with methods to instrument\n * @returns Instrumented object with same interface\n */\nexport function instrumentObject<T extends object>(prefix: string, o: T): T {\n const handlers = {} as T;\n for (const key of Object.keys(o) as (keyof T)[]) {\n if (typeof o[key] !== 'function') {\n handlers[key] = o[key];\n } else {\n const f = o[key];\n const methodName = String(key);\n // @ts-expect-error - dynamic function wrapping\n handlers[key] = async (...args: unknown[]) => {\n // Build span name - for events.create, include the event type\n let spanName = `${prefix}.${methodName}`;\n if (prefix === 'world.events' && methodName === 'create') {\n const eventType = extractEventType(args);\n if (eventType) {\n spanName = `${prefix}.${methodName} ${eventType}`;\n }\n }\n\n return trace(\n spanName,\n { kind: await getSpanKind('INTERNAL') },\n async (span) => {\n // Add peer service attributes for service maps\n // Use spanName for rpc.method so Datadog shows event type in resource\n span?.setAttributes({\n ...PeerService(WORLD_LOCAL_SERVICE.peerService),\n ...RpcSystem(WORLD_LOCAL_SERVICE.rpcSystem),\n ...RpcService(WORLD_LOCAL_SERVICE.rpcService),\n ...RpcMethod(spanName),\n });\n return f(...args);\n }\n );\n };\n }\n }\n return handlers;\n}\n"]}
package/dist/queue.d.ts CHANGED
@@ -1,4 +1,11 @@
1
1
  import { type Queue } from '@workflow/world';
2
2
  import type { Config } from './config.js';
3
- export declare function createQueue(config: Partial<Config>): Queue;
3
+ export type DirectHandler = (req: Request) => Promise<Response>;
4
+ export type LocalQueue = Queue & {
5
+ /** Close the HTTP agent and release resources. */
6
+ close(): Promise<void>;
7
+ /** Register a direct in-process handler for a queue prefix, bypassing HTTP. */
8
+ registerHandler(prefix: '__wkf_step_' | '__wkf_workflow_', handler: DirectHandler): void;
9
+ };
10
+ export declare function createQueue(config: Partial<Config>): LocalQueue;
4
11
  //# sourceMappingURL=queue.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,KAAK,KAAK,EAAkB,MAAM,iBAAiB,CAAC;AAIxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAc1C,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,CA2J1D"}
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,KAAK,KAAK,EAAkB,MAAM,iBAAiB,CAAC;AAKxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAiD1C,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEhE,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG;IAC/B,kDAAkD;IAClD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,+EAA+E;IAC/E,eAAe,CACb,MAAM,EAAE,aAAa,GAAG,iBAAiB,EACzC,OAAO,EAAE,aAAa,GACrB,IAAI,CAAC;CACT,CAAC;AAeF,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,CAoP/D"}