@owlmeans/queue 0.1.18-rc.4 → 0.1.18-rc.41

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 (110) hide show
  1. package/README.md +286 -8
  2. package/agent-meta/manifest.json +9 -2
  3. package/agent-meta/skills/queue/SKILL.md +243 -12
  4. package/agent-meta/skills/scheduled-jobs/SKILL.md +151 -0
  5. package/build/bridge/types.d.ts +25 -0
  6. package/build/bridge/types.d.ts.map +1 -0
  7. package/build/bridge/types.js +2 -0
  8. package/build/bridge/types.js.map +1 -0
  9. package/build/bridge.d.ts +7 -0
  10. package/build/bridge.d.ts.map +1 -0
  11. package/build/bridge.js +37 -0
  12. package/build/bridge.js.map +1 -0
  13. package/build/config.d.ts +34 -0
  14. package/build/config.d.ts.map +1 -0
  15. package/build/config.js +59 -0
  16. package/build/config.js.map +1 -0
  17. package/build/consts.d.ts +33 -0
  18. package/build/consts.d.ts.map +1 -0
  19. package/build/consts.js +35 -0
  20. package/build/consts.js.map +1 -0
  21. package/build/entrypoint-jobs/types.d.ts +29 -0
  22. package/build/entrypoint-jobs/types.d.ts.map +1 -0
  23. package/build/entrypoint-jobs/types.js +2 -0
  24. package/build/entrypoint-jobs/types.js.map +1 -0
  25. package/build/entrypoint-jobs.d.ts +5 -0
  26. package/build/entrypoint-jobs.d.ts.map +1 -0
  27. package/build/entrypoint-jobs.js +84 -0
  28. package/build/entrypoint-jobs.js.map +1 -0
  29. package/build/errors.d.ts +68 -0
  30. package/build/errors.d.ts.map +1 -0
  31. package/build/errors.js +103 -0
  32. package/build/errors.js.map +1 -0
  33. package/build/index.d.ts +18 -0
  34. package/build/index.d.ts.map +1 -1
  35. package/build/index.js +13 -1
  36. package/build/index.js.map +1 -1
  37. package/build/protocol/types.d.ts +12 -0
  38. package/build/protocol/types.d.ts.map +1 -0
  39. package/build/protocol/types.js +2 -0
  40. package/build/protocol/types.js.map +1 -0
  41. package/build/protocol.d.ts +13 -0
  42. package/build/protocol.d.ts.map +1 -0
  43. package/build/protocol.js +63 -0
  44. package/build/protocol.js.map +1 -0
  45. package/build/queue-config/types.d.ts +29 -0
  46. package/build/queue-config/types.d.ts.map +1 -0
  47. package/build/queue-config/types.js +2 -0
  48. package/build/queue-config/types.js.map +1 -0
  49. package/build/queue-config.d.ts +9 -0
  50. package/build/queue-config.d.ts.map +1 -0
  51. package/build/queue-config.js +83 -0
  52. package/build/queue-config.js.map +1 -0
  53. package/build/queue-route/types.d.ts +14 -0
  54. package/build/queue-route/types.d.ts.map +1 -0
  55. package/build/queue-route/types.js +2 -0
  56. package/build/queue-route/types.js.map +1 -0
  57. package/build/queue-route.d.ts +4 -0
  58. package/build/queue-route.d.ts.map +1 -0
  59. package/build/queue-route.js +24 -0
  60. package/build/queue-route.js.map +1 -0
  61. package/build/route.d.ts +9 -0
  62. package/build/route.d.ts.map +1 -0
  63. package/build/route.js +16 -0
  64. package/build/route.js.map +1 -0
  65. package/build/transport.d.ts +15 -0
  66. package/build/transport.d.ts.map +1 -0
  67. package/build/transport.js +59 -0
  68. package/build/transport.js.map +1 -0
  69. package/build/types.d.ts +312 -0
  70. package/build/types.d.ts.map +1 -0
  71. package/build/types.js +2 -0
  72. package/build/types.js.map +1 -0
  73. package/build/types.local.d.ts +4 -0
  74. package/build/types.local.d.ts.map +1 -0
  75. package/build/types.local.js +2 -0
  76. package/build/types.local.js.map +1 -0
  77. package/build/utils.d.ts +4 -0
  78. package/build/utils.d.ts.map +1 -0
  79. package/build/utils.js +4 -0
  80. package/build/utils.js.map +1 -0
  81. package/build/worker.d.ts +10 -0
  82. package/build/worker.d.ts.map +1 -0
  83. package/build/worker.js +24 -0
  84. package/build/worker.js.map +1 -0
  85. package/package.json +13 -3
  86. package/src/bridge/types.ts +25 -0
  87. package/src/bridge.ts +46 -0
  88. package/src/config.ts +80 -0
  89. package/src/consts.ts +38 -0
  90. package/src/entrypoint-jobs/types.ts +29 -0
  91. package/src/entrypoint-jobs.ts +105 -0
  92. package/src/errors.ts +121 -0
  93. package/src/index.ts +18 -0
  94. package/src/protocol/types.ts +16 -0
  95. package/src/protocol.ts +95 -0
  96. package/src/queue-config/types.ts +29 -0
  97. package/src/queue-config.ts +102 -0
  98. package/src/queue-route/types.ts +14 -0
  99. package/src/queue-route.ts +33 -0
  100. package/src/route.ts +23 -0
  101. package/src/transport.ts +77 -0
  102. package/src/types.ts +326 -0
  103. package/src/utils.ts +5 -0
  104. package/src/worker.ts +27 -0
  105. package/tests/bridge.spec.ts +64 -0
  106. package/tests/config.spec.ts +67 -0
  107. package/tests/protocol.spec.ts +58 -0
  108. package/tests/schedule.spec.ts +99 -0
  109. package/tsconfig.json +1 -0
  110. package/build/.gitkeep +0 -0
@@ -0,0 +1,58 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import { contract, protocol, schema } from '@owlmeans/entrypoint'
3
+ import { route } from '@owlmeans/route'
4
+ import type { JSONSchemaType } from 'ajv'
5
+ import { job, queueProtocolOf, type JobRecord } from '../src/index.js'
6
+
7
+ interface Input { value: number }
8
+ interface Output { doubled: number }
9
+ const InputSchema = schema<Input>({
10
+ type: 'object', properties: { value: { type: 'number' } }, required: ['value'], additionalProperties: false,
11
+ } as JSONSchemaType<Input>)
12
+ const OutputSchema = schema<Output>({
13
+ type: 'object', properties: { doubled: { type: 'number' } }, required: ['doubled'], additionalProperties: false,
14
+ } as JSONSchemaType<Output>)
15
+ const declaration = protocol(
16
+ route('test:double', '/double', job({ queue: 'test' })),
17
+ contract(InputSchema, OutputSchema),
18
+ )
19
+
20
+ const context = (reply: unknown = { value: { doubled: 6 } }) => {
21
+ const created: Partial<JobRecord>[] = []
22
+ return {
23
+ cfg: { queue: { queues: [{ name: 'test', jobs: [declaration.alias] }] } },
24
+ jobs: () => ({
25
+ create: async (record: Partial<JobRecord>) => {
26
+ created.push(record)
27
+ return { ...record, id: 'job-1' } as JobRecord
28
+ },
29
+ wait: async () => reply,
30
+ }),
31
+ created,
32
+ }
33
+ }
34
+
35
+ describe('protocol-aware queue calls', () => {
36
+ test('derives the broker address and keeps per-call options', async () => {
37
+ const ctx = context()
38
+ const queued = await queueProtocolOf(ctx as never).enqueue(declaration, { body: { value: 3 } }, {
39
+ delay: 250, attempts: 3, id: 'projection-1',
40
+ })
41
+ expect(queued.queue).toBe('test')
42
+ expect(ctx.created[0]?.name).toBe(declaration.alias)
43
+ expect(ctx.created[0]?.opts).toEqual({ delay: 250, attempts: 3, id: 'projection-1' })
44
+ expect(await queueProtocolOf(ctx as never).waitFor(declaration, queued as never)).toEqual({ doubled: 6 })
45
+ })
46
+
47
+ test('rejects raw aliases and non-queue declarations at runtime', async () => {
48
+ await expect(queueProtocolOf(context() as never).enqueue(declaration.alias as never, {} as never)).rejects.toThrow()
49
+ const http = protocol(route('test:http', '/http'), contract())
50
+ await expect(queueProtocolOf(context() as never).enqueue(http, {})).rejects.toThrow()
51
+ })
52
+
53
+ test('rejects a job from another queue or protocol', async () => {
54
+ await expect(queueProtocolOf(context() as never).waitFor(declaration, {
55
+ id: 'x', queue: 'other', name: declaration.alias, data: {},
56
+ } as never)).rejects.toThrow()
57
+ })
58
+ })
@@ -0,0 +1,99 @@
1
+ import { describe, test, expect } from 'bun:test'
2
+ import { declareQueue, declareSchedule } from '../src/config.js'
3
+ import { queueConfigOf } from '../src/queue-config.js'
4
+ import { ScheduleMisdeclared, UnknownJobName, UnknownQueue } from '../src/errors.js'
5
+ import type { Config, ScheduleDeclaration } from '../src/types.js'
6
+
7
+ const cfg = (): Config => declareQueue(
8
+ declareQueue({ ready: false, service: 'test', type: 'backend' } as unknown as Config,
9
+ 'maintenance', ['reconcile', 'sweep']),
10
+ 'work', ['story:code']
11
+ )
12
+
13
+ const nightly: ScheduleDeclaration = {
14
+ id: 'nightly-reconcile', queue: 'maintenance', name: 'reconcile', pattern: '17 3 * * *', tz: 'UTC',
15
+ }
16
+
17
+ const misdeclared = (schedule: Partial<ScheduleDeclaration>): () => void =>
18
+ () => queueConfigOf(cfg()).assertSchedule({ ...nightly, ...schedule } as ScheduleDeclaration)
19
+
20
+ describe('declaring schedules', () => {
21
+ test('a declaration is recorded and read back by its queue', () => {
22
+ const config = declareSchedule(cfg(), nightly)
23
+
24
+ expect(queueConfigOf(config).schedulesOf('maintenance')).toEqual([nightly])
25
+ expect(queueConfigOf(config).schedulesOf('work')).toEqual([])
26
+ expect(queueConfigOf(config).schedulesOf()).toHaveLength(1)
27
+ })
28
+
29
+ test('re-declaring an id replaces rather than appends', () => {
30
+ let config = declareSchedule(cfg(), nightly)
31
+ config = declareSchedule(config, { ...nightly, pattern: '0 4 * * *' })
32
+
33
+ expect(queueConfigOf(config).schedulesOf()).toHaveLength(1)
34
+ expect(queueConfigOf(config).schedulesOf()[0].pattern).toBe('0 4 * * *')
35
+ })
36
+
37
+ /**
38
+ * A scheduled run of a name the queue does not accept would fail on every tick. Refusing at
39
+ * declaration turns that into one error at boot, and the refused schedule is not recorded.
40
+ */
41
+ test('a schedule for an undeclared queue or job name is refused and not recorded', () => {
42
+ const config = cfg()
43
+
44
+ expect(() => declareSchedule(config, { ...nightly, queue: 'missing' })).toThrow(UnknownQueue)
45
+ expect(() => declareSchedule(config, { ...nightly, name: 'story:code' })).toThrow(UnknownJobName)
46
+ expect(queueConfigOf(config).schedulesOf()).toHaveLength(0)
47
+ })
48
+
49
+ test('re-declaring the queue keeps its schedules', () => {
50
+ const config = declareQueue(declareSchedule(cfg(), nightly), 'maintenance', ['reconcile'])
51
+
52
+ expect(queueConfigOf(config).queueOf('maintenance').jobs).toEqual(['reconcile'])
53
+ expect(queueConfigOf(config).schedulesOf()).toEqual([nightly])
54
+ })
55
+ })
56
+
57
+ describe('asserting a schedule', () => {
58
+ test('exactly one of every and pattern', () => {
59
+ expect(misdeclared({ every: 1_000 })).toThrow(/every-or-pattern/)
60
+ expect(misdeclared({ pattern: undefined, tz: undefined })).toThrow(/every-or-pattern/)
61
+ expect(misdeclared({ pattern: undefined, tz: undefined, every: 0 })).toThrow(/:every$/)
62
+ expect(misdeclared({ pattern: undefined, tz: undefined, every: 1.5 })).toThrow(ScheduleMisdeclared)
63
+ })
64
+
65
+ test('tz and immediately belong to a pattern only', () => {
66
+ expect(misdeclared({ pattern: undefined, every: 1_000 })).toThrow(/:tz$/)
67
+ expect(misdeclared({ pattern: undefined, tz: undefined, every: 1_000, immediately: true }))
68
+ .toThrow(/:immediately$/)
69
+ expect(misdeclared({ immediately: true, startDate: new Date() })).toThrow(/immediately-start-date/)
70
+ expect(misdeclared({ immediately: true })).not.toThrow()
71
+ })
72
+
73
+ test('the broker owns a scheduled run\'s id and delay', () => {
74
+ expect(misdeclared({ opts: { id: 'fixed' } as ScheduleDeclaration['opts'] })).toThrow(/opts-id/)
75
+ expect(misdeclared({ opts: { delay: 10 } as ScheduleDeclaration['opts'] })).toThrow(/opts-delay/)
76
+ expect(misdeclared({ opts: { attempts: 3 } })).not.toThrow()
77
+ })
78
+
79
+ test('an empty id is refused', () => {
80
+ expect(misdeclared({ id: ' ' })).toThrow(ScheduleMisdeclared)
81
+ })
82
+ })
83
+
84
+ describe('asserting every schedule', () => {
85
+ test('two schedules under one id are refused', () => {
86
+ const config = declareSchedule(cfg(), nightly)
87
+ config.queue?.schedules?.push({ ...nightly, pattern: '0 5 * * *' })
88
+
89
+ expect(() => queueConfigOf(config).assertSchedules()).toThrow(/duplicate/)
90
+ })
91
+
92
+ /** The configuration is plain data; a schedule written into it directly is checked too. */
93
+ test('a schedule written past declareSchedule is still checked', () => {
94
+ const config = declareSchedule(cfg(), nightly)
95
+ config.queue?.schedules?.push({ id: 'orphan', queue: 'maintenance', name: 'gone', every: 1_000 })
96
+
97
+ expect(() => queueConfigOf(config).assertSchedules()).toThrow(UnknownJobName)
98
+ })
99
+ })
package/tsconfig.json CHANGED
@@ -10,6 +10,7 @@
10
10
  "exclude": [
11
11
  "./dist/**/*",
12
12
  "./build/**/*",
13
+ "./tests/**/*",
13
14
  "./*.ts"
14
15
  ]
15
16
  }
package/build/.gitkeep DELETED
File without changes