hazo_core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +84 -0
  2. package/SETUP_CHECKLIST.md +114 -0
  3. package/config/hazo_core_config.ini +14 -0
  4. package/dist/config/index.d.ts +35 -0
  5. package/dist/config/index.d.ts.map +1 -0
  6. package/dist/config/index.js +117 -0
  7. package/dist/config/index.js.map +1 -0
  8. package/dist/config/ini_parser.d.ts +10 -0
  9. package/dist/config/ini_parser.d.ts.map +1 -0
  10. package/dist/config/ini_parser.js +45 -0
  11. package/dist/config/ini_parser.js.map +1 -0
  12. package/dist/constants.d.ts +4 -0
  13. package/dist/constants.d.ts.map +1 -0
  14. package/dist/constants.js +4 -0
  15. package/dist/constants.js.map +1 -0
  16. package/dist/context/client.d.ts +27 -0
  17. package/dist/context/client.d.ts.map +1 -0
  18. package/dist/context/client.js +66 -0
  19. package/dist/context/client.js.map +1 -0
  20. package/dist/context/index.d.ts +9 -0
  21. package/dist/context/index.d.ts.map +1 -0
  22. package/dist/context/index.js +8 -0
  23. package/dist/context/index.js.map +1 -0
  24. package/dist/context/server.d.ts +23 -0
  25. package/dist/context/server.d.ts.map +1 -0
  26. package/dist/context/server.js +52 -0
  27. package/dist/context/server.js.map +1 -0
  28. package/dist/context/types.d.ts +14 -0
  29. package/dist/context/types.d.ts.map +1 -0
  30. package/dist/context/types.js +2 -0
  31. package/dist/context/types.js.map +1 -0
  32. package/dist/debug/index.d.ts +20 -0
  33. package/dist/debug/index.d.ts.map +1 -0
  34. package/dist/debug/index.js +78 -0
  35. package/dist/debug/index.js.map +1 -0
  36. package/dist/errors/auth.d.ts +14 -0
  37. package/dist/errors/auth.d.ts.map +1 -0
  38. package/dist/errors/auth.js +19 -0
  39. package/dist/errors/auth.js.map +1 -0
  40. package/dist/errors/base.d.ts +89 -0
  41. package/dist/errors/base.d.ts.map +1 -0
  42. package/dist/errors/base.js +152 -0
  43. package/dist/errors/base.js.map +1 -0
  44. package/dist/errors/config.d.ts +18 -0
  45. package/dist/errors/config.d.ts.map +1 -0
  46. package/dist/errors/config.js +14 -0
  47. package/dist/errors/config.js.map +1 -0
  48. package/dist/errors/conflict.d.ts +9 -0
  49. package/dist/errors/conflict.d.ts.map +1 -0
  50. package/dist/errors/conflict.js +14 -0
  51. package/dist/errors/conflict.js.map +1 -0
  52. package/dist/errors/external.d.ts +9 -0
  53. package/dist/errors/external.d.ts.map +1 -0
  54. package/dist/errors/external.js +14 -0
  55. package/dist/errors/external.js.map +1 -0
  56. package/dist/errors/from_zod.d.ts +33 -0
  57. package/dist/errors/from_zod.d.ts.map +1 -0
  58. package/dist/errors/from_zod.js +63 -0
  59. package/dist/errors/from_zod.js.map +1 -0
  60. package/dist/errors/index.d.ts +22 -0
  61. package/dist/errors/index.d.ts.map +1 -0
  62. package/dist/errors/index.js +12 -0
  63. package/dist/errors/index.js.map +1 -0
  64. package/dist/errors/internal.d.ts +14 -0
  65. package/dist/errors/internal.d.ts.map +1 -0
  66. package/dist/errors/internal.js +14 -0
  67. package/dist/errors/internal.js.map +1 -0
  68. package/dist/errors/not_found.d.ts +9 -0
  69. package/dist/errors/not_found.d.ts.map +1 -0
  70. package/dist/errors/not_found.js +15 -0
  71. package/dist/errors/not_found.js.map +1 -0
  72. package/dist/errors/rate_limit.d.ts +9 -0
  73. package/dist/errors/rate_limit.d.ts.map +1 -0
  74. package/dist/errors/rate_limit.js +14 -0
  75. package/dist/errors/rate_limit.js.map +1 -0
  76. package/dist/errors/unavailable.d.ts +9 -0
  77. package/dist/errors/unavailable.d.ts.map +1 -0
  78. package/dist/errors/unavailable.js +14 -0
  79. package/dist/errors/unavailable.js.map +1 -0
  80. package/dist/errors/validation.d.ts +15 -0
  81. package/dist/errors/validation.d.ts.map +1 -0
  82. package/dist/errors/validation.js +17 -0
  83. package/dist/errors/validation.js.map +1 -0
  84. package/dist/http/fetch_with_request_id.d.ts +14 -0
  85. package/dist/http/fetch_with_request_id.d.ts.map +1 -0
  86. package/dist/http/fetch_with_request_id.js +29 -0
  87. package/dist/http/fetch_with_request_id.js.map +1 -0
  88. package/dist/http/get_origin_url.d.ts +23 -0
  89. package/dist/http/get_origin_url.d.ts.map +1 -0
  90. package/dist/http/get_origin_url.js +61 -0
  91. package/dist/http/get_origin_url.js.map +1 -0
  92. package/dist/http/request_id_middleware.d.ts +38 -0
  93. package/dist/http/request_id_middleware.d.ts.map +1 -0
  94. package/dist/http/request_id_middleware.js +57 -0
  95. package/dist/http/request_id_middleware.js.map +1 -0
  96. package/dist/index.client.d.ts +16 -0
  97. package/dist/index.client.d.ts.map +1 -0
  98. package/dist/index.client.js +19 -0
  99. package/dist/index.client.js.map +1 -0
  100. package/dist/index.d.ts +23 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +27 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/logger/index.d.ts +29 -0
  105. package/dist/logger/index.d.ts.map +1 -0
  106. package/dist/logger/index.js +125 -0
  107. package/dist/logger/index.js.map +1 -0
  108. package/dist/singleton/index.d.ts +16 -0
  109. package/dist/singleton/index.d.ts.map +1 -0
  110. package/dist/singleton/index.js +42 -0
  111. package/dist/singleton/index.js.map +1 -0
  112. package/dist/utils/dates.d.ts +45 -0
  113. package/dist/utils/dates.d.ts.map +1 -0
  114. package/dist/utils/dates.js +79 -0
  115. package/dist/utils/dates.js.map +1 -0
  116. package/dist/utils/env.d.ts +7 -0
  117. package/dist/utils/env.d.ts.map +1 -0
  118. package/dist/utils/env.js +9 -0
  119. package/dist/utils/env.js.map +1 -0
  120. package/dist/utils/index.d.ts +69 -0
  121. package/dist/utils/index.d.ts.map +1 -0
  122. package/dist/utils/index.js +174 -0
  123. package/dist/utils/index.js.map +1 -0
  124. package/package.json +82 -0
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # hazo_core
2
+
3
+ Foundation package for every `hazo_*` package — errors, correlation ID propagation, config loading, logger factory, debug factory, singleton registry, and HTTP middleware.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install hazo_core
9
+ ```
10
+
11
+ Required peer deps (ship alongside hazo_core in Wave 1):
12
+
13
+ ```bash
14
+ npm install hazo_logs hazo_debug
15
+ ```
16
+
17
+ ## Quick start
18
+
19
+ ```ts
20
+ import { createLogger, loadConfig, withContext, HazoError } from 'hazo_core';
21
+ import { defineConfig } from 'hazo_core';
22
+ import z from 'zod';
23
+
24
+ // Logger (auto-injects correlationId + pkg)
25
+ const log = createLogger('hazo_mypackage');
26
+ log.info('server.started', { port: 3000 });
27
+
28
+ // Config (reads hazo_mypackage_config.ini, validates with Zod)
29
+ const config = loadConfig({
30
+ pkg: 'hazo_mypackage',
31
+ schema: defineConfig({ port: z.number().default(3000) }),
32
+ });
33
+
34
+ // Correlation ID (AsyncLocalStorage on server)
35
+ withContext({ correlationId: 'req_abc123' }, () => {
36
+ log.info('request.received'); // correlationId auto-included
37
+ });
38
+
39
+ // Errors
40
+ throw new HazoNotFoundError({ code: 'HAZO_MY_NOT_FOUND', pkg: 'hazo_mypackage', message: 'Resource missing' });
41
+ ```
42
+
43
+ ## Public API
44
+
45
+ See `design/architecture.md §5` for the complete public API surface.
46
+
47
+ ### Errors
48
+
49
+ ```ts
50
+ import { HazoError, HazoValidationError, HazoAuthError, HazoNotFoundError,
51
+ HazoConflictError, HazoRateLimitError, HazoUnavailableError,
52
+ HazoExternalError, HazoConfigError, HazoInternalError } from 'hazo_core/errors';
53
+ ```
54
+
55
+ ### Context (correlation ID)
56
+
57
+ ```ts
58
+ import { withContext, getContext, getCorrelationId } from 'hazo_core/context';
59
+ ```
60
+
61
+ ### Config, logger, debug, singletons
62
+
63
+ ```ts
64
+ import { loadConfig, defineConfig, createLogger, createDebug,
65
+ registerSingleton, getSingleton, generateRequestId,
66
+ fetchWithRequestId, createRequestIdMiddleware, REQUEST_ID_HEADER } from 'hazo_core';
67
+ ```
68
+
69
+ ### Browser-safe subset
70
+
71
+ ```ts
72
+ import { HazoError } from 'hazo_core/errors';
73
+ import { getCorrelationId, setBrowserCorrelationId } from 'hazo_core/context';
74
+ import { REQUEST_ID_HEADER } from 'hazo_core/client';
75
+ ```
76
+
77
+ ## Configuration
78
+
79
+ Copy `config/hazo_core_config.ini` to your app's config directory. See `SETUP_CHECKLIST.md` for integration steps.
80
+
81
+ ## See also
82
+
83
+ - `design/architecture.md` — full PRD and design rationale
84
+ - `SETUP_CHECKLIST.md` — step-by-step integration guide
@@ -0,0 +1,114 @@
1
+ # hazo_core — Setup Checklist
2
+
3
+ Step-by-step guide for integrating `hazo_core` into a consuming app or hazo_* package.
4
+
5
+ ## 1. Install
6
+
7
+ ```bash
8
+ npm install hazo_core hazo_logs hazo_debug
9
+ ```
10
+
11
+ ## 2. Copy the config template
12
+
13
+ ```bash
14
+ cp node_modules/hazo_core/config/hazo_core_config.ini config/hazo_core_config.ini
15
+ ```
16
+
17
+ Edit `config/hazo_core_config.ini` as needed. Key settings:
18
+
19
+ | Key | Default | Description |
20
+ |-----|---------|-------------|
21
+ | `[log] include_stack` | `false` | Include stack traces in error JSON (set to `true` for local debug) |
22
+
23
+ ## 3. Wire correlation ID at every entry point
24
+
25
+ **Next.js App Router** — add to `app/api/[...]/route.ts`:
26
+
27
+ ```ts
28
+ import { withRequestIdRouteHandler } from 'hazo_core';
29
+ export const GET = withRequestIdRouteHandler(async (req) => { ... });
30
+ ```
31
+
32
+ **Express** — add middleware:
33
+
34
+ ```ts
35
+ import { createRequestIdMiddleware } from 'hazo_core';
36
+ app.use(createRequestIdMiddleware());
37
+ ```
38
+
39
+ **Background jobs / cron** — wrap worker bootstrap:
40
+
41
+ ```ts
42
+ import { withContext, generateRequestId } from 'hazo_core';
43
+ const cid = process.env.CORRELATION_ID ?? generateRequestId();
44
+ withContext({ correlationId: cid }, () => runWorker());
45
+ ```
46
+
47
+ ## 4. Use the logger
48
+
49
+ ```ts
50
+ import { createLogger } from 'hazo_core';
51
+ const log = createLogger('myapp');
52
+ log.info('server.started', { port: 3000 });
53
+ // Logs include: pkg, correlationId, userId (if in context), env, timestamp
54
+ ```
55
+
56
+ ## 5. Use the config loader
57
+
58
+ ```ts
59
+ import { loadConfig, defineConfig } from 'hazo_core';
60
+ import z from 'zod';
61
+
62
+ const config = loadConfig({
63
+ pkg: 'myapp',
64
+ configDir: path.join(process.cwd(), 'config'),
65
+ schema: defineConfig({
66
+ port: z.number().default(3000),
67
+ secret: z.string(),
68
+ }),
69
+ });
70
+ ```
71
+
72
+ ## 6. Browser context (React apps)
73
+
74
+ Wrap your React tree in `HazoContextProvider` (from `hazo_ui`):
75
+
76
+ ```tsx
77
+ import { HazoContextProvider } from 'hazo_ui';
78
+ export default function RootLayout({ children }) {
79
+ return <HazoContextProvider>{children}</HazoContextProvider>;
80
+ }
81
+ ```
82
+
83
+ ## 7. HAZO_ENV convention
84
+
85
+ Set `HAZO_ENV` in your deployment environment to declare which environment is running. Falls back to `NODE_ENV` then `'development'`.
86
+
87
+ ```bash
88
+ HAZO_ENV=production node server.js
89
+ ```
90
+
91
+ Create per-env config overlays:
92
+ ```
93
+ config/hazo_core_config.ini # base
94
+ config/hazo_core_config.production.ini # production overrides
95
+ config/hazo_core_config.staging.ini # staging overrides
96
+ ```
97
+
98
+ ## 8. Error handling
99
+
100
+ All `hazo_*` packages throw `HazoError` subclasses. Catch at API boundaries:
101
+
102
+ ```ts
103
+ import { HazoError } from 'hazo_core/errors';
104
+
105
+ try {
106
+ await doSomething();
107
+ } catch (err) {
108
+ if (HazoError.is(err)) {
109
+ const { status, body } = err.toHttpResponse();
110
+ return Response.json(body, { status });
111
+ }
112
+ throw err;
113
+ }
114
+ ```
@@ -0,0 +1,14 @@
1
+ ; hazo_core configuration
2
+ ; All values can be overridden via env vars: HAZO_CORE_<SECTION>_<KEY>=value
3
+
4
+ [log]
5
+ ; Include stack traces in HazoError.toJSON() and log output.
6
+ ; Default: false in production, true otherwise.
7
+ ; Override with HAZO_INCLUDE_STACK=1 to force on.
8
+ include_stack = false
9
+
10
+ [env]
11
+ ; This section is an example of the per-env overlay convention.
12
+ ; Create hazo_core_config.<env>.ini alongside this file to override values
13
+ ; for a specific HAZO_ENV (e.g. hazo_core_config.development.ini).
14
+ ; See hazo_core/SETUP_CHECKLIST.md for details.
@@ -0,0 +1,35 @@
1
+ import type { ZodSchema, ZodRawShape } from 'zod';
2
+ import { z } from 'zod';
3
+ interface LoadConfigOptions<T> {
4
+ /** Package name e.g. 'hazo_auth' — used to build filename and env-var prefix */
5
+ pkg: string;
6
+ /** Defaults to process.cwd()/config */
7
+ configDir?: string;
8
+ /** Zod schema to validate the merged config object */
9
+ schema: ZodSchema<T>;
10
+ /** Defaults to getCurrentEnv() */
11
+ env?: string;
12
+ }
13
+ /**
14
+ * Load, merge, validate, and cache a package config.
15
+ *
16
+ * Load order (per §8 + D-003 + D-020 hook 2):
17
+ * 1. <configDir>/hazo_<pkg>_config.ini (base)
18
+ * 2. <configDir>/hazo_<pkg>_config.<env>.ini (env overlay, skipped when env is 'development')
19
+ * 3. HAZO_<PKG>_<SECTION>_<KEY> env-var overrides
20
+ * 4. Zod validation — throws HazoConfigError on failure
21
+ * 5. Cache under `${pkg}:${resolvedEnv}`
22
+ */
23
+ export declare function loadConfig<T>(opts: LoadConfigOptions<T>): T;
24
+ /**
25
+ * Invalidate the config cache for a given package (all envs).
26
+ * Primarily for tests and the hazo_debug "reload config" UI button.
27
+ */
28
+ export declare function reloadConfig(pkg: string): void;
29
+ /**
30
+ * Thin wrapper around z.object() that documents intent.
31
+ * Each package keeps its schema in src/config/schema.ts.
32
+ */
33
+ export declare function defineConfig<T extends ZodRawShape>(shape: T): z.ZodObject<T>;
34
+ export {};
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAClD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,UAAU,iBAAiB,CAAC,CAAC;IAC3B,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACrB,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAqED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,CAoC3D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAM9C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAE5E"}
@@ -0,0 +1,117 @@
1
+ import { z } from 'zod';
2
+ import * as path from 'node:path';
3
+ import { parseIniFile, mergeIni } from './ini_parser.js';
4
+ import { fromZodConfig } from '../errors/from_zod.js';
5
+ import { getCurrentEnv } from '../utils/env.js';
6
+ // Cache: `${pkg}:${env}` → parsed + validated config
7
+ const configCache = new Map();
8
+ /**
9
+ * Normalize a package name to the UPPER_SNAKE form used in env-var prefixes.
10
+ * e.g. 'hazo_llm_api' → 'HAZO_LLM_API'
11
+ */
12
+ function pkgToEnvPrefix(pkg) {
13
+ return pkg.toUpperCase();
14
+ }
15
+ /**
16
+ * Apply env-var overrides to a merged INI object.
17
+ *
18
+ * Scans process.env for keys matching HAZO_<PKG>_<SECTION>_<KEY> and writes
19
+ * them into the merged object at [section][key] (lowercase).
20
+ *
21
+ * Per D-003 and D-020 hook 6.
22
+ */
23
+ function applyEnvVarOverrides(merged, pkg) {
24
+ const prefix = `${pkgToEnvPrefix(pkg)}_`;
25
+ const result = { ...merged };
26
+ const overrideKeys = [];
27
+ for (const [envKey, envValue] of Object.entries(process.env)) {
28
+ if (envValue === undefined)
29
+ continue;
30
+ if (!envKey.startsWith(prefix))
31
+ continue;
32
+ // Strip the prefix and split remaining into [SECTION, KEY]
33
+ const remainder = envKey.slice(prefix.length);
34
+ const parts = remainder.split('_');
35
+ if (parts.length < 2) {
36
+ // Must have at least SECTION_KEY
37
+ continue;
38
+ }
39
+ // Convention: everything except the last token is the section,
40
+ // and the last token is the key. This handles multi-word sections/keys
41
+ // that are already joined with underscores in the env-var name.
42
+ // Simple heuristic: first token is section, rest joined as key.
43
+ const sectionUpper = parts[0];
44
+ const keyUpper = parts.slice(1).join('_');
45
+ const section = sectionUpper.toLowerCase();
46
+ const key = keyUpper.toLowerCase();
47
+ // Ensure section exists
48
+ if (typeof result[section] !== 'object' || result[section] === null) {
49
+ result[section] = {};
50
+ }
51
+ result[section][key] = envValue;
52
+ overrideKeys.push(`${section}.${key}`);
53
+ }
54
+ if (overrideKeys.length > 0) {
55
+ console.info(`[hazo_core/loadConfig] ${pkg}: env-var overrides applied for: ${overrideKeys.join(', ')}`);
56
+ }
57
+ return result;
58
+ }
59
+ /**
60
+ * Load, merge, validate, and cache a package config.
61
+ *
62
+ * Load order (per §8 + D-003 + D-020 hook 2):
63
+ * 1. <configDir>/hazo_<pkg>_config.ini (base)
64
+ * 2. <configDir>/hazo_<pkg>_config.<env>.ini (env overlay, skipped when env is 'development')
65
+ * 3. HAZO_<PKG>_<SECTION>_<KEY> env-var overrides
66
+ * 4. Zod validation — throws HazoConfigError on failure
67
+ * 5. Cache under `${pkg}:${resolvedEnv}`
68
+ */
69
+ export function loadConfig(opts) {
70
+ const resolvedEnv = opts.env ?? getCurrentEnv();
71
+ const cacheKey = `${opts.pkg}:${resolvedEnv}`;
72
+ if (configCache.has(cacheKey)) {
73
+ return configCache.get(cacheKey);
74
+ }
75
+ const configDir = opts.configDir ?? path.join(process.cwd(), 'config');
76
+ // 1. Base INI
77
+ const baseFile = path.join(configDir, `hazo_${opts.pkg}_config.ini`);
78
+ let merged = parseIniFile(baseFile);
79
+ // 2. Env overlay (skip for development)
80
+ if (resolvedEnv !== 'development') {
81
+ const overlayFile = path.join(configDir, `hazo_${opts.pkg}_config.${resolvedEnv}.ini`);
82
+ const overlay = parseIniFile(overlayFile);
83
+ merged = mergeIni(merged, overlay);
84
+ }
85
+ // 3. Env-var overrides
86
+ merged = applyEnvVarOverrides(merged, opts.pkg);
87
+ // 4. Zod validation
88
+ const result = opts.schema.safeParse(merged);
89
+ if (!result.success) {
90
+ throw fromZodConfig(result.error, {
91
+ pkg: opts.pkg,
92
+ code: `HAZO_${opts.pkg.toUpperCase()}_CONFIG_INVALID`,
93
+ });
94
+ }
95
+ // 5. Cache and return
96
+ configCache.set(cacheKey, result.data);
97
+ return result.data;
98
+ }
99
+ /**
100
+ * Invalidate the config cache for a given package (all envs).
101
+ * Primarily for tests and the hazo_debug "reload config" UI button.
102
+ */
103
+ export function reloadConfig(pkg) {
104
+ for (const key of Array.from(configCache.keys())) {
105
+ if (key.startsWith(`${pkg}:`)) {
106
+ configCache.delete(key);
107
+ }
108
+ }
109
+ }
110
+ /**
111
+ * Thin wrapper around z.object() that documents intent.
112
+ * Each package keeps its schema in src/config/schema.ts.
113
+ */
114
+ export function defineConfig(shape) {
115
+ return z.object(shape);
116
+ }
117
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Read and parse an INI file. Returns an empty object if file not found.
3
+ */
4
+ export declare function parseIniFile(filePath: string): Record<string, unknown>;
5
+ /**
6
+ * Merge two parsed INI objects. Overlay values win for scalar conflicts;
7
+ * nested objects are shallow-merged.
8
+ */
9
+ export declare function mergeIni(base: Record<string, unknown>, overlay: Record<string, unknown>): Record<string, unknown>;
10
+ //# sourceMappingURL=ini_parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ini_parser.d.ts","sourceRoot":"","sources":["../../src/config/ini_parser.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUtE;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyBzB"}
@@ -0,0 +1,45 @@
1
+ import { parse } from 'ini';
2
+ import { readFileSync, existsSync } from 'node:fs';
3
+ /**
4
+ * Read and parse an INI file. Returns an empty object if file not found.
5
+ */
6
+ export function parseIniFile(filePath) {
7
+ if (!existsSync(filePath)) {
8
+ return {};
9
+ }
10
+ try {
11
+ const content = readFileSync(filePath, 'utf-8');
12
+ return parse(content);
13
+ }
14
+ catch {
15
+ return {};
16
+ }
17
+ }
18
+ /**
19
+ * Merge two parsed INI objects. Overlay values win for scalar conflicts;
20
+ * nested objects are shallow-merged.
21
+ */
22
+ export function mergeIni(base, overlay) {
23
+ const result = { ...base };
24
+ for (const [key, value] of Object.entries(overlay)) {
25
+ const baseValue = result[key];
26
+ if (value !== null &&
27
+ typeof value === 'object' &&
28
+ !Array.isArray(value) &&
29
+ baseValue !== null &&
30
+ typeof baseValue === 'object' &&
31
+ !Array.isArray(baseValue)) {
32
+ // Both sides are plain objects — shallow-merge
33
+ result[key] = {
34
+ ...baseValue,
35
+ ...value,
36
+ };
37
+ }
38
+ else {
39
+ // Overlay wins
40
+ result[key] = value;
41
+ }
42
+ }
43
+ return result;
44
+ }
45
+ //# sourceMappingURL=ini_parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ini_parser.js","sourceRoot":"","sources":["../../src/config/ini_parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEnD;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAChD,OAAO,KAAK,CAAC,OAAO,CAA4B,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CACtB,IAA6B,EAC7B,OAAgC;IAEhC,MAAM,MAAM,GAA4B,EAAE,GAAG,IAAI,EAAE,CAAC;IAEpD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9B,IACE,KAAK,KAAK,IAAI;YACd,OAAO,KAAK,KAAK,QAAQ;YACzB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YACrB,SAAS,KAAK,IAAI;YAClB,OAAO,SAAS,KAAK,QAAQ;YAC7B,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EACzB,CAAC;YACD,+CAA+C;YAC/C,MAAM,CAAC,GAAG,CAAC,GAAG;gBACZ,GAAI,SAAqC;gBACzC,GAAI,KAAiC;aACtC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,eAAe;YACf,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare const REQUEST_ID_HEADER = "x-request-id";
2
+ export declare const HAZO_ERROR_SYMBOL: unique symbol;
3
+ export declare const HAZO_SINGLETONS_KEY = "__hazo_core_singletons__";
4
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAChD,eAAO,MAAM,iBAAiB,eAAgC,CAAC;AAC/D,eAAO,MAAM,mBAAmB,6BAA6B,CAAC"}
@@ -0,0 +1,4 @@
1
+ export const REQUEST_ID_HEADER = 'x-request-id';
2
+ export const HAZO_ERROR_SYMBOL = Symbol.for('hazo_core:error');
3
+ export const HAZO_SINGLETONS_KEY = '__hazo_core_singletons__';
4
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAChD,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAC/D,MAAM,CAAC,MAAM,mBAAmB,GAAG,0BAA0B,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { HazoContext } from './types.js';
2
+ /**
3
+ * Set (or clear) the browser-side correlation ID.
4
+ * Pass undefined to clear the current context.
5
+ * Typically called by hazo_ui's <HazoContextProvider> on mount.
6
+ */
7
+ export declare function setBrowserCorrelationId(id?: string): void;
8
+ /**
9
+ * Read the current browser context.
10
+ * Returns undefined when no context has been set.
11
+ */
12
+ export declare function getContext(): HazoContext | undefined;
13
+ /**
14
+ * Convenience accessor for the correlation ID.
15
+ * Returns undefined when no context has been set.
16
+ */
17
+ export declare function getCorrelationId(): string | undefined;
18
+ /**
19
+ * Synchronously run `fn` within a new context derived from `values`.
20
+ * Saves and restores the previous context so calls can be nested.
21
+ *
22
+ * Unlike the server implementation, this is synchronous-only — there is no
23
+ * async boundary tracking. Concurrent async operations in a browser will share
24
+ * the same context ref, which is appropriate for the single-tab model.
25
+ */
26
+ export declare function withContext<T>(values: Partial<HazoContext>, fn: () => T): T;
27
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/context/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAc9C;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CASzD;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,WAAW,GAAG,SAAS,CAEpD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAErD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAmB3E"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Browser context implementation — single mutable ref per tab.
3
+ *
4
+ * The server's AsyncLocalStorage is not available in browsers. A single module-level
5
+ * ref matches the one-tab-one-context reality (per D-004).
6
+ *
7
+ * In React apps, hazo_ui's <HazoContextProvider> calls setBrowserCorrelationId() on
8
+ * mount so all downstream code (fetch wrappers, error constructors) can read the ID.
9
+ */
10
+ let _currentContext;
11
+ /**
12
+ * Set (or clear) the browser-side correlation ID.
13
+ * Pass undefined to clear the current context.
14
+ * Typically called by hazo_ui's <HazoContextProvider> on mount.
15
+ */
16
+ export function setBrowserCorrelationId(id) {
17
+ if (id === undefined) {
18
+ _currentContext = undefined;
19
+ }
20
+ else {
21
+ _currentContext = {
22
+ ..._currentContext,
23
+ correlationId: id,
24
+ };
25
+ }
26
+ }
27
+ /**
28
+ * Read the current browser context.
29
+ * Returns undefined when no context has been set.
30
+ */
31
+ export function getContext() {
32
+ return _currentContext;
33
+ }
34
+ /**
35
+ * Convenience accessor for the correlation ID.
36
+ * Returns undefined when no context has been set.
37
+ */
38
+ export function getCorrelationId() {
39
+ return _currentContext?.correlationId;
40
+ }
41
+ /**
42
+ * Synchronously run `fn` within a new context derived from `values`.
43
+ * Saves and restores the previous context so calls can be nested.
44
+ *
45
+ * Unlike the server implementation, this is synchronous-only — there is no
46
+ * async boundary tracking. Concurrent async operations in a browser will share
47
+ * the same context ref, which is appropriate for the single-tab model.
48
+ */
49
+ export function withContext(values, fn) {
50
+ const previous = _currentContext;
51
+ const merged = values.correlationId !== undefined || previous !== undefined
52
+ ? {
53
+ ...previous,
54
+ ...values,
55
+ correlationId: values.correlationId ?? previous?.correlationId ?? '',
56
+ }
57
+ : undefined;
58
+ _currentContext = merged;
59
+ try {
60
+ return fn();
61
+ }
62
+ finally {
63
+ _currentContext = previous;
64
+ }
65
+ }
66
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/context/client.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,IAAI,eAAwC,CAAC;AAE7C;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,EAAW;IACjD,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QACrB,eAAe,GAAG,SAAS,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,eAAe,GAAG;YAChB,GAAG,eAAe;YAClB,aAAa,EAAE,EAAE;SAClB,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,eAAe,EAAE,aAAa,CAAC;AACxC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAI,MAA4B,EAAE,EAAW;IACtE,MAAM,QAAQ,GAAG,eAAe,CAAC;IAEjC,MAAM,MAAM,GACV,MAAM,CAAC,aAAa,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS;QAC1D,CAAC,CAAC;YACE,GAAG,QAAQ;YACX,GAAG,MAAM;YACT,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,QAAQ,EAAE,aAAa,IAAI,EAAE;SACrE;QACH,CAAC,CAAC,SAAS,CAAC;IAEhB,eAAe,GAAG,MAAM,CAAC;IAEzB,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,CAAC;IACd,CAAC;YAAS,CAAC;QACT,eAAe,GAAG,QAAQ,CAAC;IAC7B,CAAC;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Server-side context implementation (AsyncLocalStorage).
3
+ *
4
+ * Browser-safe consumers should import from 'hazo_core/client' or directly from
5
+ * './client.js' — they cannot use this file as it imports node:async_hooks.
6
+ */
7
+ export { withContext, getContext, getCorrelationId } from './server.js';
8
+ export type { HazoContext } from './types.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACxE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Server-side context implementation (AsyncLocalStorage).
3
+ *
4
+ * Browser-safe consumers should import from 'hazo_core/client' or directly from
5
+ * './client.js' — they cannot use this file as it imports node:async_hooks.
6
+ */
7
+ export { withContext, getContext, getCorrelationId } from './server.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { HazoContext } from './types.js';
2
+ /**
3
+ * Run `fn` within a new context that merges `values` on top of the parent context.
4
+ *
5
+ * Merge rules:
6
+ * 1. Parent context fields are inherited unless explicitly overridden by `values`.
7
+ * 2. `correlationId` is generated if neither `values` nor parent provides one.
8
+ *
9
+ * This means a child span always carries the parent's correlationId unless the caller
10
+ * explicitly wants to start a new correlation boundary.
11
+ */
12
+ export declare function withContext<T>(values: Partial<HazoContext>, fn: () => T): T;
13
+ /**
14
+ * Read the current context, if any.
15
+ * Returns undefined when called outside a withContext() scope.
16
+ */
17
+ export declare function getContext(): HazoContext | undefined;
18
+ /**
19
+ * Convenience accessor for the most-commonly-needed context field.
20
+ * Returns undefined when called outside a withContext() scope.
21
+ */
22
+ export declare function getCorrelationId(): string | undefined;
23
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/context/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAkB9C;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAc3E;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,WAAW,GAAG,SAAS,CAEpD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAErD"}