@open-mercato/events 0.6.8-develop.6918.1.7b3f32b0c8 → 0.6.8-develop.6925.1.cefa200fa4

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.
@@ -0,0 +1,191 @@
1
+ import { createEventBus } from '@open-mercato/events/index'
2
+ import type { SubscriberDescriptor } from '@open-mercato/events/types'
3
+
4
+ /**
5
+ * `dispatchQueued` is the events worker's only way to reach subscribers. The
6
+ * worker used to keep its own registry built from `getCliModules()`, which is
7
+ * populated exclusively by the `mercato` bin - so a worker started any other way
8
+ * dispatched nothing and completed the job silently. Selection now lives here, on
9
+ * the bus that already holds every registered subscriber, which also guarantees
10
+ * the inline-skip and the worker dispatch read one single-delivery decision.
11
+ */
12
+ describe('EventBus.dispatchQueued', () => {
13
+ const origFlag = process.env.OM_EVENTS_SINGLE_DELIVERY
14
+
15
+ afterEach(() => {
16
+ if (origFlag === undefined) {
17
+ delete process.env.OM_EVENTS_SINGLE_DELIVERY
18
+ }
19
+ else {
20
+ process.env.OM_EVENTS_SINGLE_DELIVERY = origFlag
21
+ }
22
+ })
23
+
24
+ function makeBus(subs: SubscriberDescriptor[]) {
25
+ const bus = createEventBus({ resolve: ((name: string) => name) as never })
26
+ bus.registerModuleSubscribers(subs)
27
+ return bus
28
+ }
29
+
30
+ function makeSub(
31
+ id: string,
32
+ event: string,
33
+ persistent: boolean,
34
+ sink: string[],
35
+ ): SubscriberDescriptor {
36
+ return { id, event, persistent, handler: () => { sink.push(id) } }
37
+ }
38
+
39
+ test('single-delivery on: pattern-matches and keeps persistent subscribers only', async () => {
40
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'true'
41
+ const calls: string[] = []
42
+ const bus = makeBus([
43
+ makeSub('exact-persistent', 'user.created', true, calls),
44
+ makeSub('exact-ephemeral', 'user.created', false, calls),
45
+ makeSub('wildcard-persistent', '*', true, calls),
46
+ makeSub('wildcard-ephemeral', '*', false, calls),
47
+ makeSub('other-event', 'user.deleted', true, calls),
48
+ ])
49
+
50
+ const results = await bus.dispatchQueued('user.created', { id: '1' })
51
+
52
+ expect(calls.sort()).toEqual(['exact-persistent', 'wildcard-persistent'])
53
+ expect(results.map((r) => r.subscriberId).sort()).toEqual(['exact-persistent', 'wildcard-persistent'])
54
+ expect(results.every((r) => r.error === undefined)).toBe(true)
55
+ })
56
+
57
+ test('selection ignores OM_EVENTS_SINGLE_DELIVERY (producer/worker env skew)', async () => {
58
+ // A worker whose env has the flag OFF must still select persistent
59
+ // subscribers by pattern. Reading the flag here used to fall back to
60
+ // exact-match, which re-ran ephemeral subscribers the producer had already
61
+ // run inline and never reached wildcard persistent subscribers - the failure
62
+ // this change exists to remove, reintroduced under env skew.
63
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'false'
64
+ const calls: string[] = []
65
+ const bus = makeBus([
66
+ makeSub('exact-persistent', 'user.created', true, calls),
67
+ makeSub('exact-ephemeral', 'user.created', false, calls),
68
+ makeSub('wildcard-persistent', '*', true, calls),
69
+ ])
70
+
71
+ await bus.dispatchQueued('user.created', {})
72
+
73
+ expect(calls.sort()).toEqual(['exact-persistent', 'wildcard-persistent'])
74
+ })
75
+
76
+ test('returns handler failures instead of swallowing them', async () => {
77
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'true'
78
+ const bus = makeBus([
79
+ {
80
+ id: 'boom',
81
+ event: 'user.created',
82
+ persistent: true,
83
+ handler: () => { throw new Error('handler exploded') },
84
+ },
85
+ { id: 'ok', event: 'user.created', persistent: true, handler: () => {} },
86
+ ])
87
+
88
+ const results = await bus.dispatchQueued('user.created', {})
89
+
90
+ const failed = results.find((r) => r.subscriberId === 'boom')
91
+ const succeeded = results.find((r) => r.subscriberId === 'ok')
92
+ expect((failed?.error as Error).message).toBe('handler exploded')
93
+ expect(succeeded?.error).toBeUndefined()
94
+ })
95
+
96
+ test('reports a rejection with an undefined reason as a failure', async () => {
97
+ // `Promise.allSettled` sets `reason: undefined` for `throw undefined` and
98
+ // `Promise.reject()`, so an outcome inferred from `error !== undefined` would
99
+ // score this as a success and let the worker complete the job.
100
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'true'
101
+ const bus = makeBus([
102
+ {
103
+ id: 'throws-undefined',
104
+ event: 'user.created',
105
+ persistent: true,
106
+ handler: () => { throw undefined },
107
+ },
108
+ {
109
+ id: 'rejects-undefined',
110
+ event: 'user.created',
111
+ persistent: true,
112
+ handler: () => Promise.reject(),
113
+ },
114
+ ])
115
+
116
+ const results = await bus.dispatchQueued('user.created', {})
117
+
118
+ expect(results.map((r) => ({ id: r.subscriberId, ok: r.ok })).sort((a, b) => a.id.localeCompare(b.id)))
119
+ .toEqual([
120
+ { id: 'rejects-undefined', ok: false },
121
+ { id: 'throws-undefined', ok: false },
122
+ ])
123
+ expect(results.every((r) => r.error === undefined)).toBe(true)
124
+ })
125
+
126
+ test('forwards trusted scope and the concrete event name to wildcard subscribers', async () => {
127
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'true'
128
+ const seen: Array<Record<string, unknown>> = []
129
+ const bus = makeBus([
130
+ {
131
+ id: 'wildcard',
132
+ event: '*',
133
+ persistent: true,
134
+ handler: (_payload, ctx) => {
135
+ seen.push({
136
+ eventName: ctx.eventName,
137
+ tenantId: ctx.tenantId,
138
+ organizationId: ctx.organizationId,
139
+ })
140
+ },
141
+ },
142
+ ])
143
+
144
+ await bus.dispatchQueued('customers.deal.won', {}, { tenantId: 't1', organizationId: 'o1' })
145
+
146
+ expect(seen).toEqual([{ eventName: 'customers.deal.won', tenantId: 't1', organizationId: 'o1' }])
147
+ })
148
+
149
+ test('falls back to the registered pattern when a subscriber has no id', async () => {
150
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'true'
151
+ const bus = createEventBus({ resolve: ((name: string) => name) as never })
152
+ bus.on('user.created', () => {}, { persistent: true })
153
+
154
+ const results = await bus.dispatchQueued('user.created', {})
155
+
156
+ expect(results).toEqual([{ subscriberId: 'user.created', ok: true }])
157
+ })
158
+
159
+ test('prefers the caller-supplied resolver over the bus creation one', async () => {
160
+ // The events worker passes its per-job `ctx.resolve` so subscribers bind to
161
+ // that job's container. Under `OM_BOOTSTRAP_CACHE` the bus may be a cached
162
+ // instance whose captured resolver still points at the container that
163
+ // bootstrapped first, which would share one `em` across concurrent jobs.
164
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'true'
165
+ let resolved: unknown = null
166
+ const bus = createEventBus({ resolve: ((name: string) => `bus:${name}`) as never })
167
+ bus.on('user.created', (_payload, ctx) => { resolved = ctx.resolve('em') }, { persistent: true })
168
+
169
+ await bus.dispatchQueued('user.created', {}, undefined, ((name: string) => `job:${name}`) as never)
170
+
171
+ expect(resolved).toBe('job:em')
172
+ })
173
+
174
+ test('falls back to the bus resolver when the caller supplies none', async () => {
175
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'true'
176
+ let resolved: unknown = null
177
+ const bus = createEventBus({ resolve: ((name: string) => `bus:${name}`) as never })
178
+ bus.on('user.created', (_payload, ctx) => { resolved = ctx.resolve('em') }, { persistent: true })
179
+
180
+ await bus.dispatchQueued('user.created', {})
181
+
182
+ expect(resolved).toBe('bus:em')
183
+ })
184
+
185
+ test('returns an empty result set when nothing matches', async () => {
186
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'true'
187
+ const bus = makeBus([makeSub('other', 'user.deleted', true, [])])
188
+
189
+ expect(await bus.dispatchQueued('user.created', {})).toEqual([])
190
+ })
191
+ })
@@ -0,0 +1,178 @@
1
+ import fs from 'node:fs'
2
+ import os from 'node:os'
3
+ import path from 'node:path'
4
+
5
+ import { createEventBus } from '@open-mercato/events/index'
6
+ import handle from '@open-mercato/events/modules/events/workers/events.worker'
7
+ import type { EventBus } from '@open-mercato/events/types'
8
+
9
+ /**
10
+ * Full round trip: persistent emit -> local file queue -> events worker -> subscriber,
11
+ * in a process where the CLI module registry was NEVER registered.
12
+ *
13
+ * That is the exact shape of the bug this suite pins. The worker used to build its
14
+ * subscriber map from `getCliModules()`, which only the `mercato` bin populates, so
15
+ * this round trip ended with the job marked COMPLETED and the subscriber never run -
16
+ * silently, because the bus had already skipped it inline under single-delivery.
17
+ */
18
+ describe('queued persistent delivery round trip (no CLI module registry)', () => {
19
+ const origCwd = process.cwd()
20
+ const origExternalWorker = process.env.OM_EVENTS_EXTERNAL_WORKER
21
+ const origFlag = process.env.OM_EVENTS_SINGLE_DELIVERY
22
+ const origStrategy = process.env.QUEUE_STRATEGY
23
+ let tmp: string
24
+
25
+ beforeEach(() => {
26
+ tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'events-roundtrip-'))
27
+ process.chdir(tmp)
28
+ process.env.OM_EVENTS_EXTERNAL_WORKER = 'true'
29
+ delete process.env.OM_EVENTS_SINGLE_DELIVERY
30
+ delete process.env.QUEUE_STRATEGY
31
+ })
32
+
33
+ afterEach(() => {
34
+ process.chdir(origCwd)
35
+ if (origExternalWorker === undefined) {
36
+ delete process.env.OM_EVENTS_EXTERNAL_WORKER
37
+ }
38
+ else {
39
+ process.env.OM_EVENTS_EXTERNAL_WORKER = origExternalWorker
40
+ }
41
+ if (origFlag === undefined) {
42
+ delete process.env.OM_EVENTS_SINGLE_DELIVERY
43
+ }
44
+ else {
45
+ process.env.OM_EVENTS_SINGLE_DELIVERY = origFlag
46
+ }
47
+ if (origStrategy === undefined) {
48
+ delete process.env.QUEUE_STRATEGY
49
+ }
50
+ else {
51
+ process.env.QUEUE_STRATEGY = origStrategy
52
+ }
53
+ try { fs.rmSync(tmp, { recursive: true, force: true }) } catch {}
54
+ })
55
+
56
+ function readQueuedJobs(): Array<{ id: string; payload: unknown }> {
57
+ const queueFile = path.join(path.resolve('.mercato/queue', 'events'), 'queue.json')
58
+ return JSON.parse(fs.readFileSync(queueFile, 'utf8'))
59
+ }
60
+
61
+ // Mirrors the context `createPerJobWorkerHandler` builds: `resolve` bound to a
62
+ // per-job request container that carries the bootstrapped event bus.
63
+ function workerContext(bus: EventBus) {
64
+ return {
65
+ jobId: 'job-1',
66
+ attemptNumber: 1,
67
+ queueName: 'events',
68
+ resolve: <T = unknown>(name: string): T => {
69
+ if (name === 'eventBus') {
70
+ return bus as unknown as T
71
+ }
72
+ throw new Error(`No mock for ${name}`)
73
+ },
74
+ }
75
+ }
76
+
77
+ it('delivers a wildcard persistent subscriber that inline delivery deliberately skipped', async () => {
78
+ const ran: Array<{ event?: string; payload: unknown; tenantId?: string | null }> = []
79
+ const bus = createEventBus({ resolve: ((name: string) => name) as never })
80
+ bus.registerModuleSubscribers([
81
+ {
82
+ id: 'webhooks:outbound-dispatch',
83
+ event: '*',
84
+ persistent: true,
85
+ handler: (payload, ctx) => {
86
+ ran.push({ event: ctx.eventName, payload, tenantId: ctx.tenantId })
87
+ },
88
+ },
89
+ ])
90
+
91
+ await bus.emit('customers.deal.won', { id: 'deal-9' }, { persistent: true, tenantId: 't1' })
92
+
93
+ // Single-delivery intends the inline skip; the worker is the sole dispatcher.
94
+ expect(ran).toEqual([])
95
+
96
+ const jobs = readQueuedJobs()
97
+ expect(jobs).toHaveLength(1)
98
+
99
+ await handle(jobs[0] as never, workerContext(bus) as never)
100
+
101
+ expect(ran).toEqual([
102
+ { event: 'customers.deal.won', payload: { id: 'deal-9' }, tenantId: 't1' },
103
+ ])
104
+ })
105
+
106
+ it('fails the job loudly when the worker container carries no event bus', async () => {
107
+ const bus = createEventBus({ resolve: ((name: string) => name) as never })
108
+ bus.registerModuleSubscribers([
109
+ { id: 'sub', event: '*', persistent: true, handler: () => {} },
110
+ ])
111
+
112
+ await bus.emit('customers.deal.won', { id: 'deal-9' }, { persistent: true })
113
+ const [job] = readQueuedJobs()
114
+
115
+ const contextWithoutBus = {
116
+ jobId: 'job-1',
117
+ attemptNumber: 1,
118
+ queueName: 'events',
119
+ resolve: <T = unknown>(name: string): T => { throw new Error(`No mock for ${name}`) },
120
+ }
121
+
122
+ await expect(handle(job as never, contextWithoutBus as never)).rejects.toThrow(
123
+ /no "eventBus" in the job container/,
124
+ )
125
+ })
126
+
127
+ it('does not re-run subscribers the emitting process already delivered inline', async () => {
128
+ // Explicit opt-out: the producer delivers inline and stamps the job, so a
129
+ // worker draining the queue must skip it.
130
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'false'
131
+ const ran: string[] = []
132
+ const bus = createEventBus({ resolve: ((name: string) => name) as never })
133
+ bus.registerModuleSubscribers([
134
+ { id: 'sub', event: 'customers.deal.won', persistent: true, handler: () => { ran.push('sub') } },
135
+ ])
136
+
137
+ await bus.emit('customers.deal.won', { id: 'deal-9' }, { persistent: true })
138
+ expect(ran).toEqual(['sub'])
139
+
140
+ const [job] = readQueuedJobs()
141
+ await handle(job as never, workerContext(bus) as never)
142
+
143
+ expect(ran).toEqual(['sub'])
144
+ })
145
+
146
+ it('fails the job when a subscriber rejects with an undefined reason', async () => {
147
+ const bus = createEventBus({ resolve: ((name: string) => name) as never })
148
+ bus.registerModuleSubscribers([
149
+ { id: 'silent', event: '*', persistent: true, handler: () => { throw undefined } },
150
+ ])
151
+
152
+ await bus.emit('customers.deal.won', { id: 'deal-9' }, { persistent: true })
153
+ const [job] = readQueuedJobs()
154
+
155
+ await expect(handle(job as never, workerContext(bus) as never)).rejects.toThrow(
156
+ '[internal] 1/1 subscriber(s) failed for event "customers.deal.won": silent',
157
+ )
158
+ })
159
+
160
+ it('surfaces a failing subscriber so the queue retries the job', async () => {
161
+ const bus = createEventBus({ resolve: ((name: string) => name) as never })
162
+ bus.registerModuleSubscribers([
163
+ {
164
+ id: 'flaky',
165
+ event: '*',
166
+ persistent: true,
167
+ handler: () => { throw new Error('downstream unavailable') },
168
+ },
169
+ ])
170
+
171
+ await bus.emit('customers.deal.won', { id: 'deal-9' }, { persistent: true })
172
+ const [job] = readQueuedJobs()
173
+
174
+ await expect(handle(job as never, workerContext(bus) as never)).rejects.toThrow(
175
+ '[internal] 1/1 subscriber(s) failed for event "customers.deal.won": flaky',
176
+ )
177
+ })
178
+ })
@@ -13,6 +13,7 @@ import type { SubscriberDescriptor } from '@open-mercato/events/types'
13
13
  describe('Event bus single-delivery (OM_EVENTS_SINGLE_DELIVERY)', () => {
14
14
  const origCwd = process.cwd()
15
15
  const origFlag = process.env.OM_EVENTS_SINGLE_DELIVERY
16
+ const origAutoSpawn = process.env.AUTO_SPAWN_WORKERS
16
17
  let tmp: string
17
18
 
18
19
  beforeEach(() => {
@@ -20,12 +21,21 @@ describe('Event bus single-delivery (OM_EVENTS_SINGLE_DELIVERY)', () => {
20
21
  process.chdir(tmp)
21
22
  delete process.env.QUEUE_STRATEGY
22
23
  delete process.env.EVENTS_STRATEGY
24
+ delete process.env.AUTO_SPAWN_WORKERS
23
25
  })
24
26
 
27
+ function restoreEnv(name: string, original: string | undefined): void {
28
+ if (original === undefined) {
29
+ delete process.env[name]
30
+ return
31
+ }
32
+ process.env[name] = original
33
+ }
34
+
25
35
  afterEach(() => {
26
36
  process.chdir(origCwd)
27
- if (origFlag === undefined) delete process.env.OM_EVENTS_SINGLE_DELIVERY
28
- else process.env.OM_EVENTS_SINGLE_DELIVERY = origFlag
37
+ restoreEnv('OM_EVENTS_SINGLE_DELIVERY', origFlag)
38
+ restoreEnv('AUTO_SPAWN_WORKERS', origAutoSpawn)
29
39
  try { fs.rmSync(tmp, { recursive: true, force: true }) } catch {}
30
40
  })
31
41
 
@@ -38,7 +48,12 @@ describe('Event bus single-delivery (OM_EVENTS_SINGLE_DELIVERY)', () => {
38
48
  return { id, event, persistent, handler: () => { sink.push(id) } }
39
49
  }
40
50
 
41
- test('default (unset): a persistent subscriber is skipped inline (single-delivery is default-on)', async () => {
51
+ function readQueuedJobs(): Array<Record<string, unknown>> {
52
+ const queuePath = path.join(path.resolve('.mercato/queue', 'events'), 'queue.json')
53
+ return JSON.parse(fs.readFileSync(queuePath, 'utf8')) as Array<Record<string, unknown>>
54
+ }
55
+
56
+ test('flag unset (default on): a persistent subscriber is skipped inline', async () => {
42
57
  delete process.env.OM_EVENTS_SINGLE_DELIVERY
43
58
  const calls: string[] = []
44
59
  const bus = createEventBus({ resolve: ((name: string) => name) as never })
@@ -52,6 +67,7 @@ describe('Event bus single-delivery (OM_EVENTS_SINGLE_DELIVERY)', () => {
52
67
  // Default-on: the persistent subscriber is deferred to the worker; only the
53
68
  // ephemeral subscriber runs inline.
54
69
  expect(calls).toEqual(['ephemeral-sub'])
70
+ expect(readQueuedJobs()[0].payload).toMatchObject({ persistentDeliveredInline: false })
55
71
  })
56
72
 
57
73
  test('flag explicitly OFF (legacy opt-out): a persistent subscriber still runs inline', async () => {
@@ -62,8 +78,44 @@ describe('Event bus single-delivery (OM_EVENTS_SINGLE_DELIVERY)', () => {
62
78
 
63
79
  await bus.emit('demo', { a: 1 }, { persistent: true })
64
80
 
65
- // Legacy dual-dispatch: inline delivery is preserved when explicitly opted out.
81
+ // Explicit opt-out: subscribers run inline. Not dual-dispatch - the queued job
82
+ // carries the stamp, so a worker draining the queue skips it.
66
83
  expect(calls).toEqual(['persistent-sub'])
84
+ expect(readQueuedJobs()[0].payload).toMatchObject({ persistentDeliveredInline: true })
85
+ })
86
+
87
+ test('flag OFF: a failed inline persistent subscriber leaves the job unstamped for the worker', async () => {
88
+ // Inline delivery logs-and-continues, so a persistent subscriber that throws
89
+ // there gets no retry of its own. Stamping the job would strand it: the
90
+ // worker would skip a job whose only run failed, silently downgrading
91
+ // persistent delivery to at-most-once. Leaving it unstamped hands the retry
92
+ // back to the queue.
93
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'false'
94
+ const bus = createEventBus({ resolve: ((name: string) => name) as never })
95
+ bus.registerModuleSubscribers([
96
+ { id: 'boom', event: 'demo', persistent: true, handler: () => { throw new Error('downstream down') } },
97
+ ])
98
+
99
+ await bus.emit('demo', { a: 1 }, { persistent: true })
100
+
101
+ expect(readQueuedJobs()[0].payload).toMatchObject({ persistentDeliveredInline: false })
102
+ })
103
+
104
+ test('flag OFF: a failed EPHEMERAL subscriber does not unstamp the job', async () => {
105
+ // Ephemeral subscribers are never worker-dispatched, so their failure must
106
+ // not hand the job to the worker - that would re-run the persistent ones.
107
+ process.env.OM_EVENTS_SINGLE_DELIVERY = 'false'
108
+ const calls: string[] = []
109
+ const bus = createEventBus({ resolve: ((name: string) => name) as never })
110
+ bus.registerModuleSubscribers([
111
+ { id: 'ephemeral-boom', event: 'demo', persistent: false, handler: () => { throw new Error('nope') } },
112
+ makeSub('persistent-ok', 'demo', true, calls),
113
+ ])
114
+
115
+ await bus.emit('demo', { a: 1 }, { persistent: true })
116
+
117
+ expect(calls).toEqual(['persistent-ok'])
118
+ expect(readQueuedJobs()[0].payload).toMatchObject({ persistentDeliveredInline: true })
67
119
  })
68
120
 
69
121
  test('flag ON: a persistent subscriber is skipped inline (deferred to the worker)', async () => {
@@ -96,7 +148,6 @@ describe('Event bus single-delivery (OM_EVENTS_SINGLE_DELIVERY)', () => {
96
148
 
97
149
  test('flag ON: persistent emit is still enqueued for the worker', async () => {
98
150
  process.env.OM_EVENTS_SINGLE_DELIVERY = 'true'
99
- const queuePath = path.join(path.resolve('.mercato/queue', 'events'), 'queue.json')
100
151
  const calls: string[] = []
101
152
  const bus = createEventBus({ resolve: ((name: string) => name) as never })
102
153
  bus.registerModuleSubscribers([makeSub('persistent-sub', 'demo', true, calls)])
@@ -104,9 +155,10 @@ describe('Event bus single-delivery (OM_EVENTS_SINGLE_DELIVERY)', () => {
104
155
  await bus.emit('demo', { a: 1 }, { persistent: true })
105
156
 
106
157
  expect(calls).toEqual([])
107
- const list = JSON.parse(fs.readFileSync(queuePath, 'utf8'))
158
+ const list = readQueuedJobs()
108
159
  expect(Array.isArray(list)).toBe(true)
109
160
  expect(list.length).toBeGreaterThanOrEqual(1)
161
+ expect(list[0].payload).toMatchObject({ persistentDeliveredInline: false })
110
162
  })
111
163
 
112
164
  test('flag ON: the queued job keeps the trusted scope from the emit options', async () => {
@@ -178,6 +230,9 @@ describe('Event bus enqueue-only persistent emit (deliverInline: false)', () =>
178
230
  const list = JSON.parse(fs.readFileSync(queuePath, 'utf8'))
179
231
  expect(Array.isArray(list)).toBe(true)
180
232
  expect(list.length).toBeGreaterThanOrEqual(1)
233
+ // Enqueue-only never delivers inline, so the worker must still dispatch it
234
+ // even though this process has no worker-availability signal.
235
+ expect(list[0].payload).toMatchObject({ persistentDeliveredInline: false })
181
236
  })
182
237
 
183
238
  test('deliverInline: false has no effect on a non-persistent emit', async () => {
@@ -191,10 +246,10 @@ describe('Event bus enqueue-only persistent emit (deliverInline: false)', () =>
191
246
  expect(calls).toEqual(['sub'])
192
247
  })
193
248
 
194
- test('deliverInline unset under legacy opt-out preserves inline dual-dispatch', async () => {
249
+ test('deliverInline unset under explicit opt-out preserves inline delivery', async () => {
195
250
  // Isolate the deliverInline default from the single-delivery default by
196
251
  // explicitly opting out: a persistent emit with deliverInline unset still
197
- // runs inline (legacy dual-dispatch).
252
+ // runs inline.
198
253
  process.env.OM_EVENTS_SINGLE_DELIVERY = 'false'
199
254
  const calls: string[] = []
200
255
  const bus = createEventBus({ resolve: ((name: string) => name) as never })