@onlineapps/mq-client-core 4.2.0 → 5.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.
@@ -81,7 +81,7 @@ const describeErrors = (errors) => (errors || [])
81
81
  */
82
82
  function validateRecurrences(document, source) {
83
83
  if (typeof source !== 'string' || source.trim() === '') {
84
- throw new Error(
84
+ throw invalidRequest(
85
85
  '[mq-client-core/scheduler-request] Missing source - Expected: a name for what is being '
86
86
  + 'checked, because every refusal below quotes it and a wrong name sends the reader to the '
87
87
  + 'wrong place. Fix: pass what the caller calls this document (e.g. "the declaration").'
@@ -89,7 +89,7 @@ function validateRecurrences(document, source) {
89
89
  }
90
90
 
91
91
  if (!document || typeof document !== 'object' || Array.isArray(document)) {
92
- throw new Error(
92
+ throw invalidRequest(
93
93
  `[mq-client-core/scheduler-request] ${source} is not a declaration - Expected: an object with a "recurrences" list, `
94
94
  + `got ${Array.isArray(document) ? 'an array' : typeof document}. `
95
95
  + 'Fix: write { "recurrences": [ … ] }, or remove the file if the service declares none.'
@@ -97,7 +97,7 @@ function validateRecurrences(document, source) {
97
97
  }
98
98
 
99
99
  if (!validateDeclaration(document)) {
100
- throw new Error(
100
+ throw invalidRequest(
101
101
  `[mq-client-core/scheduler-request] ${source} does not hold: ${describeErrors(validateDeclaration.errors)} - `
102
102
  + 'Expected: every record carries id, cookbook_ref, cron (five fields) and an explicit '
103
103
  + 'IANA timezone, and nothing this shape does not declare. '
@@ -110,7 +110,7 @@ function validateRecurrences(document, source) {
110
110
  const seen = new Set();
111
111
  for (const recurrence of document.recurrences) {
112
112
  if (seen.has(recurrence.id)) {
113
- throw new Error(
113
+ throw invalidRequest(
114
114
  `[mq-client-core/scheduler-request] ${source} declares the id "${recurrence.id}" twice - Expected: one record per `
115
115
  + 'id, because the id travels as the scheduler\'s idempotency_key and a second record '
116
116
  + 'would silently overwrite the first at every boot. '
@@ -123,6 +123,26 @@ function validateRecurrences(document, source) {
123
123
  return document.recurrences;
124
124
  }
125
125
 
126
+ /**
127
+ * The code every refusal of a builder carries — and only those (`SCHEDULER_REQUEST_INVALID`).
128
+ *
129
+ * A caller has two kinds of failure to tell apart and must never do it by the wording of a
130
+ * message: a request that cannot be built (a record that breaks the declaration rules, tenancy,
131
+ * a missing key or requester, a key the port does not declare) and a request that could not be
132
+ * SENT (no connection, a publish the broker refused). The wrapper's boot phase 0.7b isolates the
133
+ * first as a failed source and stops the boot on the second (confirmation `scheduler` 020,
134
+ * operational reading 1). So the builders mark their refusals with this code, and
135
+ * `publishSchedulerRequest()` and the port's construction never do.
136
+ */
137
+ const SCHEDULER_REQUEST_INVALID = 'SCHEDULER_REQUEST_INVALID';
138
+
139
+ /** A builder's refusal: the message as written, plus the code that names its kind. */
140
+ function invalidRequest(message) {
141
+ const error = new Error(message);
142
+ error.code = SCHEDULER_REQUEST_INVALID;
143
+ return error;
144
+ }
145
+
126
146
  /** A whole number a tenant or workspace id may be: ≥ 1, never 0 (confirmation scheduler 002). */
127
147
  const isContextId = (value) => Number.isInteger(value) && value >= 1;
128
148
 
@@ -169,7 +189,7 @@ const NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
169
189
  /** The key that identifies the item, whatever kind it is - required by the contract's root. */
170
190
  function requireIdempotencyKey(idempotencyKey) {
171
191
  if (typeof idempotencyKey !== 'string' || idempotencyKey.trim() === '') {
172
- throw new Error(
192
+ throw invalidRequest(
173
193
  '[mq-client-core/scheduler-request] Missing idempotency_key - Expected: the stable identity '
174
194
  + 'of this item, chosen by the caller. The scheduler decides a one-shot item ONCE by this '
175
195
  + 'key, so a key invented here would turn every retry of one fact into a second run. '
@@ -183,7 +203,7 @@ function requireIdempotencyKey(idempotencyKey) {
183
203
  /** The service registering, cancelling or reporting - checked by the scheduler against services.json. */
184
204
  function requireRequestedBy(requestedBy) {
185
205
  if (typeof requestedBy !== 'string' || requestedBy.trim() === '') {
186
- throw new Error(
206
+ throw invalidRequest(
187
207
  '[mq-client-core/scheduler-request] Missing requested_by - Expected: the platform name of the '
188
208
  + 'service sending this request; the scheduler checks it against api/config/services.json '
189
209
  + "and against the cookbook's own trigger declaration. "
@@ -197,7 +217,7 @@ function requireRequestedBy(requestedBy) {
197
217
  /** Both context ids, under the one rule confirmation `scheduler` 002 fixed. */
198
218
  function requireTenancy(tenantId, workspaceId) {
199
219
  if (!isContextId(tenantId)) {
200
- throw new Error(
220
+ throw invalidRequest(
201
221
  `[mq-client-core/scheduler-request] Invalid tenant_id ${JSON.stringify(tenantId)} - Expected: `
202
222
  + 'a whole number of at least 1. The owner rejected a reserved value: 0 is not a platform '
203
223
  + 'scope, and an absent tenant is an error rather than a default (confirmation scheduler 002). '
@@ -208,7 +228,7 @@ function requireTenancy(tenantId, workspaceId) {
208
228
  }
209
229
 
210
230
  if (!isContextId(workspaceId)) {
211
- throw new Error(
231
+ throw invalidRequest(
212
232
  `[mq-client-core/scheduler-request] Invalid workspace_id ${JSON.stringify(workspaceId)} - `
213
233
  + 'Expected: a whole number of at least 1, under the same rule as the tenant (confirmation '
214
234
  + 'scheduler 002). Fix: pass workspaceId with every request - a recurrence carries it in the '
@@ -242,7 +262,7 @@ function buildEventRequest({ event, tenantId, workspaceId, requestedBy, idempote
242
262
  requireTenancy(tenantId, workspaceId);
243
263
 
244
264
  if (!event || typeof event !== 'object' || Array.isArray(event)) {
245
- throw new Error(
265
+ throw invalidRequest(
246
266
  `[mq-client-core/scheduler-request] Missing event - Expected: { service, name } - who it `
247
267
  + 'happened to and what happened, both of them. '
248
268
  + 'Fix: pass the fact this service publishes, never a cookbook name.'
@@ -251,7 +271,7 @@ function buildEventRequest({ event, tenantId, workspaceId, requestedBy, idempote
251
271
 
252
272
  for (const [field, value] of [['service', event.service], ['name', event.name]]) {
253
273
  if (typeof value !== 'string' || !NAME_PATTERN.test(value)) {
254
- throw new Error(
274
+ throw invalidRequest(
255
275
  `[mq-client-core/scheduler-request] Invalid event.${field} ${JSON.stringify(value)} - `
256
276
  + 'Expected: the platform spelling, lower case letters, digits and dashes '
257
277
  + '(the pattern the scheduler\'s own contract declares). '
@@ -414,7 +434,7 @@ function createSchedulerPublisher({ mqClient, requestedBy } = {}) {
414
434
  // something, and dropping it in silence hides both (`architecture-principles.md` §3).
415
435
  for (const key of Object.keys(record)) {
416
436
  if (!RECURRING_PORT_KEYS.includes(key)) {
417
- throw new Error(
437
+ throw invalidRequest(
418
438
  `[mq-client-core/scheduler-request] registerRecurring got a key the request does not declare ("${key}") - `
419
439
  + 'Expected: exactly { id, cookbookRef, cron, timezone, tenantId, workspaceId, input?, catchUp? }. '
420
440
  + 'Fix: remove the key from the record the service returns / the ctx.scheduler call.'
@@ -472,6 +492,7 @@ function createSchedulerPublisher({ mqClient, requestedBy } = {}) {
472
492
  module.exports = {
473
493
  schedulerRequestQueue,
474
494
  SENDABLE_KINDS,
495
+ SCHEDULER_REQUEST_INVALID,
475
496
  validateRecurrences,
476
497
  buildRecurringRequest,
477
498
  buildEventRequest,