@c9up/ream 0.2.11 → 0.2.12

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 (62) hide show
  1. package/dist/Ignitor.js +4 -4
  2. package/dist/Ignitor.js.map +1 -1
  3. package/dist/container/Container.d.ts.map +1 -1
  4. package/dist/container/Container.js +7 -7
  5. package/dist/container/Container.js.map +1 -1
  6. package/dist/errors/ModuleErrors.d.ts +4 -0
  7. package/dist/errors/ModuleErrors.d.ts.map +1 -1
  8. package/dist/errors/ModuleErrors.js +12 -8
  9. package/dist/errors/ModuleErrors.js.map +1 -1
  10. package/dist/errors/PipelineStageError.d.ts +3 -3
  11. package/dist/errors/PipelineStageError.d.ts.map +1 -1
  12. package/dist/errors/PipelineStageError.js +7 -7
  13. package/dist/errors/PipelineStageError.js.map +1 -1
  14. package/dist/errors/ReamError.d.ts +14 -4
  15. package/dist/errors/ReamError.d.ts.map +1 -1
  16. package/dist/errors/ReamError.js +16 -6
  17. package/dist/errors/ReamError.js.map +1 -1
  18. package/dist/graphql/GraphQLProvider.js +1 -1
  19. package/dist/graphql/GraphQLProvider.js.map +1 -1
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +2 -2
  23. package/dist/index.js.map +1 -1
  24. package/dist/middleware/Pipeline.js +1 -1
  25. package/dist/middleware/Pipeline.js.map +1 -1
  26. package/dist/rpc/RpcProvider.js +1 -1
  27. package/dist/rpc/RpcProvider.js.map +1 -1
  28. package/dist/scheduler/Schedule.js +2 -2
  29. package/dist/scheduler/Schedule.js.map +1 -1
  30. package/dist/scheduler/ScheduleProvider.d.ts +1 -1
  31. package/dist/scheduler/ScheduleProvider.js +6 -6
  32. package/dist/scheduler/ScheduleProvider.js.map +1 -1
  33. package/dist/scheduler/Scheduler.d.ts +1 -1
  34. package/dist/scheduler/Scheduler.js +13 -13
  35. package/dist/scheduler/Scheduler.js.map +1 -1
  36. package/dist/scheduler/locks/LockBackend.js +1 -1
  37. package/dist/scheduler/locks/LockBackend.js.map +1 -1
  38. package/events.darwin-arm64.node +0 -0
  39. package/events.darwin-x64.node +0 -0
  40. package/events.linux-arm64-gnu.node +0 -0
  41. package/events.linux-x64-gnu.node +0 -0
  42. package/events.win32-x64-msvc.node +0 -0
  43. package/index.darwin-arm64.node +0 -0
  44. package/index.darwin-x64.node +0 -0
  45. package/index.linux-arm64-gnu.node +0 -0
  46. package/index.linux-x64-gnu.node +0 -0
  47. package/index.win32-x64-msvc.node +0 -0
  48. package/package.json +1 -1
  49. package/scheduler.win32-x64-msvc.node +0 -0
  50. package/src/Ignitor.ts +4 -4
  51. package/src/container/Container.ts +13 -9
  52. package/src/errors/ModuleErrors.ts +12 -8
  53. package/src/errors/PipelineStageError.ts +8 -8
  54. package/src/errors/ReamError.ts +16 -6
  55. package/src/graphql/GraphQLProvider.ts +1 -1
  56. package/src/index.ts +2 -2
  57. package/src/middleware/Pipeline.ts +1 -1
  58. package/src/rpc/RpcProvider.ts +1 -1
  59. package/src/scheduler/Schedule.ts +2 -2
  60. package/src/scheduler/ScheduleProvider.ts +6 -6
  61. package/src/scheduler/Scheduler.ts +14 -14
  62. package/src/scheduler/locks/LockBackend.ts +1 -1
@@ -282,7 +282,7 @@ export class Container {
282
282
  key = this.#tokenToKey(target)
283
283
  if (seenAliases.has(key)) {
284
284
  const cycle = [...seenAliases, key].join(' → ')
285
- throw new ReamError('CIRCULAR_DEPENDENCY', `Circular alias detected: ${cycle}`, {
285
+ throw new ReamError('E_CIRCULAR_DEPENDENCY', `Circular alias detected: ${cycle}`, {
286
286
  hint: 'An alias chain must end at a real binding — remove one of the aliases.',
287
287
  context: { chain: cycle },
288
288
  })
@@ -302,7 +302,7 @@ export class Container {
302
302
 
303
303
  if (chain.set.has(key)) {
304
304
  const cycle = [...chain.stack, key].join(' → ')
305
- throw new ReamError('CIRCULAR_DEPENDENCY', `Circular dependency detected: ${cycle}`, {
305
+ throw new ReamError('E_CIRCULAR_DEPENDENCY', `Circular dependency detected: ${cycle}`, {
306
306
  hint: 'Break the cycle by resolving one dependency lazily inside a method (`await container.make(Dep)`) instead of injecting it in the constructor.',
307
307
  context: { chain: cycle },
308
308
  })
@@ -565,7 +565,7 @@ export class Container {
565
565
  const allKeys = [...this.#bindings.keys(), ...this.#overrides.keys()]
566
566
  const suggestion = didYouMean(key, allKeys)
567
567
  throw new ReamError(
568
- 'CONTAINER_NOT_FOUND',
568
+ 'E_CONTAINER_NOT_FOUND',
569
569
  `No binding found for '${key}'.${suggestion ? ` ${suggestion}` : ''}`,
570
570
  {
571
571
  hint: 'Register it with container.singleton() or decorate with @inject().',
@@ -666,7 +666,7 @@ export class Container {
666
666
  // instead of `new target()` with undefined deps.
667
667
  if (target.length > 0) {
668
668
  throw new ReamError(
669
- 'CONTAINER_MISSING_METADATA',
669
+ 'E_CONTAINER_MISSING_METADATA',
670
670
  `Cannot auto-construct ${target.name}: it declares ${target.length} constructor parameter(s) but no dependency metadata was found.`,
671
671
  {
672
672
  hint: 'Enable decorator metadata (swc/tsc emitDecoratorMetadata), annotate constructor params with @Inject(token), or declare static containerInjections.',
@@ -734,13 +734,13 @@ export class Container {
734
734
  // is the global registry and round-trips across modules.
735
735
  if (Symbol.keyFor(token) === undefined) {
736
736
  throw new ReamError(
737
- 'CONTAINER_INVALID_TOKEN',
737
+ 'E_CONTAINER_INVALID_TOKEN',
738
738
  `Unique Symbol() tokens are not supported — use Symbol.for("${token.description ?? 'name'}") so different modules see the same symbol.`,
739
739
  )
740
740
  }
741
741
  const desc = token.description
742
742
  if (desc === undefined) {
743
- throw new ReamError('CONTAINER_INVALID_TOKEN', 'Symbol tokens must have a description.')
743
+ throw new ReamError('E_CONTAINER_INVALID_TOKEN', 'Symbol tokens must have a description.')
744
744
  }
745
745
  this.#assertNotReserved(desc, 'symbol description')
746
746
  return `Symbol(${desc})`
@@ -750,9 +750,13 @@ export class Container {
750
750
 
751
751
  #assertNotReserved(name: string, kind: 'string' | 'symbol description'): void {
752
752
  if (RESERVED_TOKEN_NAMES.has(name)) {
753
- throw new ReamError('CONTAINER_RESERVED_TOKEN', `Reserved container token name '${name}'.`, {
754
- hint: `'${name}' would collide with an Object.prototype key. Pick a different ${kind}.`,
755
- })
753
+ throw new ReamError(
754
+ 'E_CONTAINER_RESERVED_TOKEN',
755
+ `Reserved container token name '${name}'.`,
756
+ {
757
+ hint: `'${name}' would collide with an Object.prototype key. Pick a different ${kind}.`,
758
+ },
759
+ )
756
760
  }
757
761
  }
758
762
  }
@@ -3,6 +3,10 @@
3
3
  *
4
4
  * Each module gets its own error class with appropriate defaults.
5
5
  *
6
+ * Every code these produce is `E_<MODULE>_<REASON>`: `E_` because that is what
7
+ * every framework code carries, the module name because a bare `E_NOT_FOUND`
8
+ * would not say which half of the framework raised it.
9
+ *
6
10
  * @implements FR71
7
11
  */
8
12
 
@@ -11,7 +15,7 @@ import { ReamError } from './ReamError.js'
11
15
  /** Container / IoC errors */
12
16
  export class ContainerError extends ReamError {
13
17
  constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
14
- super(`CONTAINER_${code}`, message, options)
18
+ super(code.startsWith('E_') ? code : `E_CONTAINER_${code}`, message, options)
15
19
  this.name = 'ContainerError'
16
20
  }
17
21
  }
@@ -19,7 +23,7 @@ export class ContainerError extends ReamError {
19
23
  /** Router errors */
20
24
  export class RouterError extends ReamError {
21
25
  constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
22
- super(`ROUTER_${code}`, message, options)
26
+ super(code.startsWith('E_') ? code : `E_ROUTER_${code}`, message, options)
23
27
  this.name = 'RouterError'
24
28
  }
25
29
  }
@@ -27,7 +31,7 @@ export class RouterError extends ReamError {
27
31
  /** Pipeline / Middleware errors */
28
32
  export class PipelineError extends ReamError {
29
33
  constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
30
- super(`PIPELINE_${code}`, message, options)
34
+ super(code.startsWith('E_') ? code : `E_PIPELINE_${code}`, message, options)
31
35
  this.name = 'PipelineError'
32
36
  }
33
37
  }
@@ -35,7 +39,7 @@ export class PipelineError extends ReamError {
35
39
  /** Atlas ORM errors */
36
40
  export class AtlasError extends ReamError {
37
41
  constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
38
- super(`ATLAS_${code}`, message, options)
42
+ super(code.startsWith('E_') ? code : `E_ATLAS_${code}`, message, options)
39
43
  this.name = 'AtlasError'
40
44
  }
41
45
  }
@@ -43,7 +47,7 @@ export class AtlasError extends ReamError {
43
47
  /** Rune validation errors */
44
48
  export class RuneError extends ReamError {
45
49
  constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
46
- super(`RUNE_${code}`, message, options)
50
+ super(code.startsWith('E_') ? code : `E_RUNE_${code}`, message, options)
47
51
  this.name = 'RuneError'
48
52
  }
49
53
  }
@@ -51,7 +55,7 @@ export class RuneError extends ReamError {
51
55
  /** Warden auth errors */
52
56
  export class WardenError extends ReamError {
53
57
  constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
54
- super(`WARDEN_${code}`, message, options)
58
+ super(code.startsWith('E_') ? code : `E_WARDEN_${code}`, message, options)
55
59
  this.name = 'WardenError'
56
60
  }
57
61
  }
@@ -59,7 +63,7 @@ export class WardenError extends ReamError {
59
63
  /** Event bus errors */
60
64
  export class EventsError extends ReamError {
61
65
  constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
62
- super(`EVENTS_${code}`, message, options)
66
+ super(code.startsWith('E_') ? code : `E_EVENTS_${code}`, message, options)
63
67
  this.name = 'EventsError'
64
68
  }
65
69
  }
@@ -67,7 +71,7 @@ export class EventsError extends ReamError {
67
71
  /** Forge CLI errors */
68
72
  export class ForgeError extends ReamError {
69
73
  constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
70
- super(`FORGE_${code}`, message, options)
74
+ super(code.startsWith('E_') ? code : `E_FORGE_${code}`, message, options)
71
75
  this.name = 'ForgeError'
72
76
  }
73
77
  }
@@ -7,7 +7,7 @@
7
7
  import { ReamError } from './ReamError.js'
8
8
 
9
9
  /** Pipeline stage names in execution order. */
10
- export const PIPELINE_STAGES = [
10
+ export const E_PIPELINE_STAGES = [
11
11
  'Security (Blackhole)',
12
12
  'Logging',
13
13
  'Global Middleware',
@@ -20,12 +20,12 @@ export const PIPELINE_STAGES = [
20
20
  'Response Logging',
21
21
  ] as const
22
22
 
23
- export type PipelineStageName = (typeof PIPELINE_STAGES)[number]
23
+ export type PipelineStageName = (typeof E_PIPELINE_STAGES)[number]
24
24
 
25
25
  /**
26
26
  * Create a pipeline stage error with position context.
27
27
  *
28
- * @param stageIndex - Zero-based index in PIPELINE_STAGES (clamped to valid range)
28
+ * @param stageIndex - Zero-based index in E_PIPELINE_STAGES (clamped to valid range)
29
29
  * @param originalError - The error that occurred
30
30
  * @param context - Additional context
31
31
  */
@@ -34,13 +34,13 @@ export function createPipelineError(
34
34
  originalError: Error,
35
35
  context?: Record<string, string>,
36
36
  ): ReamError {
37
- const total = PIPELINE_STAGES.length
37
+ const total = E_PIPELINE_STAGES.length
38
38
  const clampedIndex = Math.max(0, Math.min(stageIndex, total - 1))
39
- const stageName = PIPELINE_STAGES[clampedIndex]
39
+ const stageName = E_PIPELINE_STAGES[clampedIndex]
40
40
  const position = `${clampedIndex + 1}/${total}`
41
- const nextStage = clampedIndex + 1 < total ? PIPELINE_STAGES[clampedIndex + 1] : '(end)'
41
+ const nextStage = clampedIndex + 1 < total ? E_PIPELINE_STAGES[clampedIndex + 1] : '(end)'
42
42
 
43
- const errorCode = originalError instanceof ReamError ? originalError.code : 'PIPELINE_ERROR'
43
+ const errorCode = originalError instanceof ReamError ? originalError.code : 'E_PIPELINE_ERROR'
44
44
 
45
45
  return new ReamError(errorCode, originalError.message, {
46
46
  pipelineStage: `${position} (${stageName})`,
@@ -80,7 +80,7 @@ export function validatePipelineConfig(config: {
80
80
  if (!registered.has(mw) && !reported.has(mw)) {
81
81
  reported.add(mw)
82
82
  errors.push(
83
- new ReamError('PIPELINE_UNKNOWN_MIDDLEWARE', `Middleware '${mw}' is not registered`, {
83
+ new ReamError('E_PIPELINE_UNKNOWN_MIDDLEWARE', `Middleware '${mw}' is not registered`, {
84
84
  hint: `Register it with middleware.register('${mw}', handler) before using it on routes.`,
85
85
  context: {
86
86
  middleware: mw,
@@ -42,10 +42,20 @@ export function isReamError(error: unknown, ...codes: string[]): boolean {
42
42
 
43
43
  export class ReamError extends Error {
44
44
  /**
45
- * Error code, `E_`-prefixed as every framework code is — an application
46
- * matching on one should never have to remember which convention a
47
- * particular package followed (e.g. "E_ATLAS_QUERY_ERROR",
48
- * "E_CONTAINER_NOT_FOUND").
45
+ * Error code: `E_<NAMESPACE>_<REASON>`.
46
+ *
47
+ * `E_` because every framework code carries it, so an application can filter
48
+ * on one rule instead of remembering which convention a package followed.
49
+ * The namespace after it says which package raised the error — without it
50
+ * `E_FORBIDDEN` would mean three different things across the ecosystem and a
51
+ * consumer could not tell them apart (e.g. "E_ATLAS_QUERY_ERROR",
52
+ * "E_CONTAINER_NOT_FOUND", "E_WARDEN_UNKNOWN_POLICY").
53
+ *
54
+ * The exception is an identifier that already exists upstream —
55
+ * `E_UNAUTHORIZED_ACCESS`, `E_INVALID_CREDENTIALS`, `E_VALIDATION_ERROR`.
56
+ * Those are passed through verbatim, so code branching on one sees the same
57
+ * string it would elsewhere, and two packages naming the same upstream
58
+ * failure legitimately share it.
49
59
  */
50
60
  readonly code: string
51
61
 
@@ -98,7 +108,7 @@ export class ReamError extends Error {
98
108
  try {
99
109
  const parsed = JSON.parse(error.message)
100
110
  if (typeof parsed !== 'object' || parsed === null || typeof parsed.code !== 'string') {
101
- return new ReamError('UNKNOWN', error.message)
111
+ return new ReamError('E_UNKNOWN', error.message)
102
112
  }
103
113
  return new ReamError(parsed.code, parsed.message ?? error.message, {
104
114
  context:
@@ -110,7 +120,7 @@ export class ReamError extends Error {
110
120
  })
111
121
  } catch {
112
122
  // Not a JSON error — wrap as generic
113
- return new ReamError('UNKNOWN', error.message)
123
+ return new ReamError('E_UNKNOWN', error.message)
114
124
  }
115
125
  }
116
126
 
@@ -53,7 +53,7 @@ export class GraphQLProvider extends Provider {
53
53
  return
54
54
  }
55
55
  throw new ReamError(
56
- 'GRAPHQL_PROVIDER_ALREADY_REGISTERED',
56
+ 'E_GRAPHQL_PROVIDER_ALREADY_REGISTERED',
57
57
  "Container token 'graphql' is already bound to a different instance",
58
58
  {
59
59
  hint: 'Only one GraphQLProvider can own the graphql binding. Remove the duplicate provider from your reamrc.ts.',
package/src/index.ts CHANGED
@@ -286,7 +286,7 @@ export {
286
286
  export type { PipelineStageName } from './errors/PipelineStageError.js'
287
287
  export {
288
288
  createPipelineError,
289
- PIPELINE_STAGES,
289
+ E_PIPELINE_STAGES,
290
290
  validatePipelineConfig,
291
291
  } from './errors/PipelineStageError.js'
292
292
  export { isReamError, ReamError } from './errors/ReamError.js'
@@ -378,4 +378,4 @@ export {
378
378
  StatsTracker,
379
379
  } from './scheduler/index.js'
380
380
 
381
- export const VERSION = '0.2.11'
381
+ export const VERSION = '0.2.12'
@@ -188,7 +188,7 @@ function composeMiddleware(middleware: MiddlewareFunction[]): MiddlewareFunction
188
188
 
189
189
  async function dispatch(i: number): Promise<void> {
190
190
  if (i <= index) {
191
- throw new ReamError('PIPELINE_DOUBLE_NEXT', 'next() called multiple times', {
191
+ throw new ReamError('E_PIPELINE_DOUBLE_NEXT', 'next() called multiple times', {
192
192
  hint: 'A middleware called next() more than once. Each middleware should call next() at most once.',
193
193
  })
194
194
  }
@@ -56,7 +56,7 @@ export class RpcProvider extends Provider {
56
56
  // `container.resolve('rpc')` because resolution is now async.
57
57
  if (this.#registered) return
58
58
  throw new ReamError(
59
- 'RPC_PROVIDER_ALREADY_REGISTERED',
59
+ 'E_RPC_PROVIDER_ALREADY_REGISTERED',
60
60
  "Container token 'rpc' is already bound to a different instance",
61
61
  {
62
62
  hint: 'Only one RpcProvider can own the rpc binding. Remove the duplicate provider from your reamrc.ts.',
@@ -55,7 +55,7 @@ export function Schedule(cronExpr: string): MethodDecorator {
55
55
  // prototype. We need the real class either way.
56
56
  if (typeof target === 'function') {
57
57
  throw new ReamError(
58
- 'SCHEDULE_INVALID_TARGET',
58
+ 'E_SCHEDULE_INVALID_TARGET',
59
59
  `@Schedule cannot be applied to static method '${String(propertyKey)}'`,
60
60
  {
61
61
  hint: 'Move the scheduled logic to an instance method, or register the task manually via Scheduler.register().',
@@ -67,7 +67,7 @@ export function Schedule(cronExpr: string): MethodDecorator {
67
67
  // accessors, and the ticker needs a callable method.
68
68
  if (descriptor && typeof descriptor.value !== 'function') {
69
69
  throw new ReamError(
70
- 'SCHEDULE_INVALID_TARGET',
70
+ 'E_SCHEDULE_INVALID_TARGET',
71
71
  `@Schedule cannot be applied to non-method '${String(propertyKey)}' (getters and setters are not supported)`,
72
72
  {
73
73
  hint: 'Apply @Schedule to a regular method, not to an accessor.',
@@ -63,7 +63,7 @@ export class ScheduleProvider extends Provider {
63
63
  * reload, repeated `app.start()` in tests) re-binds the same
64
64
  * scheduler without error. A DIFFERENT `ScheduleProvider` trying
65
65
  * to claim the `'scheduler'` token throws
66
- * `SCHEDULE_PROVIDER_ALREADY_REGISTERED` so dueling providers do
66
+ * `E_SCHEDULE_PROVIDER_ALREADY_REGISTERED` so dueling providers do
67
67
  * not silently overwrite each other.
68
68
  */
69
69
  override register(): void {
@@ -74,7 +74,7 @@ export class ScheduleProvider extends Provider {
74
74
  // `container.resolve('scheduler')` because resolution is now async.
75
75
  if (this.#registered) return
76
76
  throw new ReamError(
77
- 'SCHEDULE_PROVIDER_ALREADY_REGISTERED',
77
+ 'E_SCHEDULE_PROVIDER_ALREADY_REGISTERED',
78
78
  "Container token 'scheduler' is already bound to a different instance",
79
79
  {
80
80
  hint: 'Only one ScheduleProvider can own the scheduler binding. Remove the duplicate provider from your reamrc.ts.',
@@ -133,7 +133,7 @@ export class ScheduleProvider extends Provider {
133
133
 
134
134
  if (!target.name) {
135
135
  throw new ReamError(
136
- 'SCHEDULE_ANONYMOUS_CLASS',
136
+ 'E_SCHEDULE_ANONYMOUS_CLASS',
137
137
  'Anonymous classes cannot declare @Schedule — task names would collide',
138
138
  {
139
139
  hint: 'Name the class explicitly (e.g. `export class Jobs { ... }`) so task names are unique and debuggable.',
@@ -148,7 +148,7 @@ export class ScheduleProvider extends Provider {
148
148
  // documented contract.
149
149
  if (typeof methodName === 'symbol') {
150
150
  throw new ReamError(
151
- 'SCHEDULE_SYMBOL_METHOD',
151
+ 'E_SCHEDULE_SYMBOL_METHOD',
152
152
  `@Schedule on symbol-keyed method is not supported on class '${target.name}'`,
153
153
  {
154
154
  hint: 'Use a regular named method so the task can be addressed uniquely.',
@@ -167,7 +167,7 @@ export class ScheduleProvider extends Provider {
167
167
  const method = instance[methodName]
168
168
  if (typeof method !== 'function') {
169
169
  throw new ReamError(
170
- 'SCHEDULE_METHOD_NOT_FOUND',
170
+ 'E_SCHEDULE_METHOD_NOT_FOUND',
171
171
  `Scheduled method ${taskName} is not callable on resolved instance`,
172
172
  )
173
173
  }
@@ -178,7 +178,7 @@ export class ScheduleProvider extends Provider {
178
178
  } catch (cause) {
179
179
  const inner = cause as ReamError & { message?: string; hint?: string }
180
180
  throw new ReamError(
181
- 'SCHEDULE_INVALID_CRON',
181
+ 'E_SCHEDULE_INVALID_CRON',
182
182
  `Failed to register scheduled task '${taskName}': ${inner.message ?? String(cause)}`,
183
183
  {
184
184
  context: { task: taskName, cronExpr },
@@ -62,7 +62,7 @@ export interface RunTaskOptions {
62
62
  /**
63
63
  * Cancel the on-demand run after `timeoutMs` milliseconds. On
64
64
  * timeout, `runTaskNow` returns `{ outcome: 'failed', error: ... }`
65
- * with a `SCHEDULE_TASK_TIMEOUT` error. The user callback continues
65
+ * with a `E_SCHEDULE_TASK_TIMEOUT` error. The user callback continues
66
66
  * running in the background — JavaScript does not support
67
67
  * pre-emptive cancellation; the timeout is observational only.
68
68
  */
@@ -140,18 +140,18 @@ function loadNative(): NativeModule {
140
140
  /**
141
141
  * Convert a thrown native error into a `ReamError`. Prefers parsing the
142
142
  * JSON payload shipped by Rust (`ream_napi_core::ReamError::into()`);
143
- * falls back to wrapping opaque errors under `SCHEDULER_NATIVE_ERROR`
143
+ * falls back to wrapping opaque errors under `E_SCHEDULER_NATIVE_ERROR`
144
144
  * so scheduler context is never lost.
145
145
  */
146
146
  function translateNativeError(e: unknown): ReamError {
147
147
  const asError = e as Error
148
148
  const parsed = ReamError.fromNapi(asError)
149
- // `fromNapi` returns an `UNKNOWN`-coded error when the message is not
149
+ // `fromNapi` returns an `E_UNKNOWN`-coded error when the message is not
150
150
  // a JSON payload. Preserve that context under a scheduler-specific
151
151
  // code so callers can trace the failure back to this layer.
152
- if (parsed.code === 'UNKNOWN') {
152
+ if (parsed.code === 'E_UNKNOWN') {
153
153
  return new ReamError(
154
- 'SCHEDULER_NATIVE_ERROR',
154
+ 'E_SCHEDULER_NATIVE_ERROR',
155
155
  `ream-scheduler NAPI call failed: ${asError.message}`,
156
156
  {
157
157
  hint: 'This is likely a bug in the scheduler. Please file an issue with the stack trace.',
@@ -204,7 +204,7 @@ export class Scheduler {
204
204
  const be = options.lockBackend as Partial<LockBackend>
205
205
  if (typeof be.acquire !== 'function' || typeof be.release !== 'function') {
206
206
  throw new ReamError(
207
- 'SCHEDULE_INVALID_LOCK_BACKEND',
207
+ 'E_SCHEDULE_INVALID_LOCK_BACKEND',
208
208
  'Scheduler options.lockBackend must implement acquire(name, ttlMs) and release(name)',
209
209
  )
210
210
  }
@@ -212,7 +212,7 @@ export class Scheduler {
212
212
  if (options.defaultLockTtlMs !== undefined) {
213
213
  if (!Number.isFinite(options.defaultLockTtlMs) || options.defaultLockTtlMs <= 0) {
214
214
  throw new ReamError(
215
- 'SCHEDULE_INVALID_LOCK_TTL',
215
+ 'E_SCHEDULE_INVALID_LOCK_TTL',
216
216
  `Scheduler options.defaultLockTtlMs must be a finite positive number, got ${options.defaultLockTtlMs}`,
217
217
  {
218
218
  hint: 'Use a millisecond value greater than zero (typical range 1_000 – 600_000).',
@@ -224,14 +224,14 @@ export class Scheduler {
224
224
  const sink = options.eventSink as Partial<ScheduleEventSink>
225
225
  if (typeof sink.emit !== 'function') {
226
226
  throw new ReamError(
227
- 'SCHEDULE_INVALID_EVENT_SINK',
227
+ 'E_SCHEDULE_INVALID_EVENT_SINK',
228
228
  'Scheduler options.eventSink must implement emit(event)',
229
229
  )
230
230
  }
231
231
  }
232
232
  if (options.errorReporter !== undefined && typeof options.errorReporter !== 'function') {
233
233
  throw new ReamError(
234
- 'SCHEDULE_INVALID_ERROR_REPORTER',
234
+ 'E_SCHEDULE_INVALID_ERROR_REPORTER',
235
235
  'Scheduler options.errorReporter must be a function',
236
236
  )
237
237
  }
@@ -376,13 +376,13 @@ export class Scheduler {
376
376
  ): void {
377
377
  if (typeof callback !== 'function') {
378
378
  throw new ReamError(
379
- 'SCHEDULE_INVALID_CALLBACK',
379
+ 'E_SCHEDULE_INVALID_CALLBACK',
380
380
  `Scheduler.register expected a function callback for task '${name}', got ${typeof callback}`,
381
381
  )
382
382
  }
383
383
  if (name === '') {
384
384
  throw new ReamError(
385
- 'SCHEDULE_INVALID_TASK_NAME',
385
+ 'E_SCHEDULE_INVALID_TASK_NAME',
386
386
  'Scheduler.register requires a non-empty task name',
387
387
  )
388
388
  }
@@ -451,7 +451,7 @@ export class Scheduler {
451
451
  // thrown error here is unexpected. Drop the local entry anyway
452
452
  // — the doc promises idempotence — but surface any unexpected
453
453
  // native failure that is not a missing-task situation.
454
- if (err.code !== 'UNKNOWN_TASK' && err.code !== 'SCHEDULER_NATIVE_ERROR') {
454
+ if (err.code !== 'UNKNOWN_TASK' && err.code !== 'E_SCHEDULER_NATIVE_ERROR') {
455
455
  this.#tasks.delete(name)
456
456
  throw err
457
457
  }
@@ -538,7 +538,7 @@ export class Scheduler {
538
538
  // real task invocations.
539
539
  this.#report(
540
540
  new ReamError(
541
- 'SCHEDULE_TASK_UNKNOWN',
541
+ 'E_SCHEDULE_TASK_UNKNOWN',
542
542
  `runTaskNow called with unknown task name: '${name}'`,
543
543
  ),
544
544
  name,
@@ -560,7 +560,7 @@ export class Scheduler {
560
560
  outcome: 'failed',
561
561
  durationMs: options.timeoutMs ?? 0,
562
562
  error: new ReamError(
563
- 'SCHEDULE_TASK_TIMEOUT',
563
+ 'E_SCHEDULE_TASK_TIMEOUT',
564
564
  `Task '${name}' did not complete within ${options.timeoutMs} ms (background execution continues)`,
565
565
  ),
566
566
  })
@@ -51,7 +51,7 @@ export interface LockBackend {
51
51
  export function assertValidTtl(ttlMs: number): void {
52
52
  if (!Number.isFinite(ttlMs) || ttlMs <= 0) {
53
53
  throw new ReamError(
54
- 'SCHEDULE_INVALID_LOCK_TTL',
54
+ 'E_SCHEDULE_INVALID_LOCK_TTL',
55
55
  `Lock TTL must be a finite positive number, got ${ttlMs}`,
56
56
  {
57
57
  hint: 'Use a millisecond value greater than zero (typical range 1_000 - 600_000).',