@suss/recognize 0.14.1 → 0.15.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.
package/dist/ast.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CallOps } from '@suss/extractor';
2
- import { L as LinkFunction } from './chain-58V6lcel.cjs';
2
+ import { L as LinkFunction } from './chain-C7w99UMv.cjs';
3
3
  import '@suss/behavioral-ir';
4
4
 
5
5
  /**
package/dist/ast.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CallOps } from '@suss/extractor';
2
- import { L as LinkFunction } from './chain-58V6lcel.js';
2
+ import { L as LinkFunction } from './chain-C7w99UMv.js';
3
3
  import '@suss/behavioral-ir';
4
4
 
5
5
  /**
@@ -361,8 +361,24 @@ interface ManyIn {
361
361
  interface ChannelPart {
362
362
  /** The property the message states this part on. */
363
363
  readonly property: string;
364
+ /**
365
+ * Where the property is written. A batch command states the queue
366
+ * once beside the list of messages, so its channel part reads off
367
+ * the call's input while the messages come one entry at a time.
368
+ * Defaults to the message.
369
+ */
370
+ readonly on?: "theMessage" | "theInput";
364
371
  /** What the library uses when the message leaves this part out. */
365
372
  readonly whenAbsent?: string;
373
+ /**
374
+ * What a reader gives back when this part is written but nothing in
375
+ * the source settles it, overriding the ending's `unsettledName`.
376
+ * EventBridge wants both: its bus is nearly always an env var, whose
377
+ * name both sides of the boundary agree on, and its subject is a
378
+ * domain string, where a run-time value should leave the channel
379
+ * unnamed rather than pair against everything.
380
+ */
381
+ readonly unsettled?: UnsettledName;
366
382
  }
367
383
  /** A message sent on a wire, which pairs with whatever consumes that channel. */
368
384
  interface MessageSendEnding {
@@ -377,6 +393,12 @@ interface MessageSendEnding {
377
393
  readonly channelSeparator?: string;
378
394
  /** The property the message states its body on, when the pack can say. */
379
395
  readonly body?: string;
396
+ /**
397
+ * The property whose literal value rides along as the routing key.
398
+ * Not part of the channel: it scopes the message for a reader without
399
+ * being what the two sides pair on.
400
+ */
401
+ readonly routingKey?: string;
380
402
  /**
381
403
  * What a reader gives back for a channel nothing in the source
382
404
  * settles. A queue URL only exists at deploy time, so the code writes
@@ -361,8 +361,24 @@ interface ManyIn {
361
361
  interface ChannelPart {
362
362
  /** The property the message states this part on. */
363
363
  readonly property: string;
364
+ /**
365
+ * Where the property is written. A batch command states the queue
366
+ * once beside the list of messages, so its channel part reads off
367
+ * the call's input while the messages come one entry at a time.
368
+ * Defaults to the message.
369
+ */
370
+ readonly on?: "theMessage" | "theInput";
364
371
  /** What the library uses when the message leaves this part out. */
365
372
  readonly whenAbsent?: string;
373
+ /**
374
+ * What a reader gives back when this part is written but nothing in
375
+ * the source settles it, overriding the ending's `unsettledName`.
376
+ * EventBridge wants both: its bus is nearly always an env var, whose
377
+ * name both sides of the boundary agree on, and its subject is a
378
+ * domain string, where a run-time value should leave the channel
379
+ * unnamed rather than pair against everything.
380
+ */
381
+ readonly unsettled?: UnsettledName;
366
382
  }
367
383
  /** A message sent on a wire, which pairs with whatever consumes that channel. */
368
384
  interface MessageSendEnding {
@@ -377,6 +393,12 @@ interface MessageSendEnding {
377
393
  readonly channelSeparator?: string;
378
394
  /** The property the message states its body on, when the pack can say. */
379
395
  readonly body?: string;
396
+ /**
397
+ * The property whose literal value rides along as the routing key.
398
+ * Not part of the channel: it scopes the message for a reader without
399
+ * being what the two sides pair on.
400
+ */
401
+ readonly routingKey?: string;
380
402
  /**
381
403
  * What a reader gives back for a channel nothing in the source
382
404
  * settles. A queue URL only exists at deploy time, so the code writes
package/dist/index.cjs CHANGED
@@ -36,6 +36,7 @@ module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // src/compile.ts
38
38
  var import_behavioral_ir = require("@suss/behavioral-ir");
39
+ var import_extractor = require("@suss/extractor");
39
40
  var import_sql = require("@suss/sql");
40
41
 
41
42
  // src/ops.ts
@@ -258,6 +259,7 @@ function statedValue(subject, pick) {
258
259
  var NOTHING_STATED = {
259
260
  text: () => null,
260
261
  name: () => null,
262
+ asArg: () => null,
261
263
  flag: () => null,
262
264
  entries: () => [],
263
265
  items: () => [],
@@ -321,17 +323,19 @@ function messageSend(matched) {
321
323
  return null;
322
324
  }
323
325
  const messages = messagesIn(input, ending.messages);
324
- return messages.length === 0 ? null : messages.map((message) => ({
326
+ const sent = messages.length === 0 ? [NOTHING_STATED] : messages;
327
+ return sent.map((message) => ({
325
328
  type: "interaction",
326
329
  binding: (0, import_behavioral_ir.messageBusBinding)({
327
330
  recognition,
328
331
  messageBus: ending.wire,
329
- channel: channelOf(message, ending)
332
+ channel: channelOf(message, input, ending)
330
333
  }),
331
334
  callee: ops.calleeText(),
332
335
  interaction: {
333
336
  class: "message-send",
334
- ...bodyOf(message, ending)
337
+ ...bodyOf(message, ending),
338
+ ...routingKeyOf(message, ending)
335
339
  }
336
340
  }));
337
341
  }
@@ -342,15 +346,23 @@ function messagesIn(input, location) {
342
346
  const collection = input.property(location.property);
343
347
  return collection === null ? [] : collection.items();
344
348
  }
345
- function channelOf(message, ending) {
349
+ function channelOf(message, input, ending) {
346
350
  const parts = [];
347
351
  for (const part of ending.channel) {
348
- const stated = message.property(part.property)?.name(ending.unsettledName) ?? null;
349
- const value = stated === null || stated === "" ? part.whenAbsent : stated;
350
- if (value === void 0) {
352
+ const holder = part.on === "theInput" ? input : message;
353
+ const written = holder.property(part.property);
354
+ if (written === null) {
355
+ if (part.whenAbsent === void 0) {
356
+ return null;
357
+ }
358
+ parts.push(part.whenAbsent);
359
+ continue;
360
+ }
361
+ const stated = written.name(part.unsettled ?? ending.unsettledName);
362
+ if (stated === null || stated === "") {
351
363
  return null;
352
364
  }
353
- parts.push(value);
365
+ parts.push(stated);
354
366
  }
355
367
  return parts.join(ending.channelSeparator ?? "#");
356
368
  }
@@ -358,9 +370,18 @@ function bodyOf(message, ending) {
358
370
  if (ending.body === void 0) {
359
371
  return {};
360
372
  }
361
- const stated = message.property(ending.body)?.text() ?? null;
373
+ const stated = (0, import_extractor.unwrapJsonStringify)(
374
+ message.property(ending.body)?.asArg() ?? null
375
+ );
362
376
  return stated === null ? {} : { body: stated };
363
377
  }
378
+ function routingKeyOf(message, ending) {
379
+ if (ending.routingKey === void 0) {
380
+ return {};
381
+ }
382
+ const stated = message.property(ending.routingKey)?.text() ?? null;
383
+ return stated === null || stated === "" ? {} : { routingKey: stated };
384
+ }
364
385
  function sqlAccess(matched) {
365
386
  const { ops, subject, method, chain, recognition } = matched;
366
387
  const ending = chain.ending;
@@ -438,6 +459,7 @@ function messageSends(spec) {
438
459
  channel: spec.channel,
439
460
  ...spec.channelSeparator === void 0 ? {} : { channelSeparator: spec.channelSeparator },
440
461
  ...spec.body === void 0 ? {} : { body: spec.body },
462
+ ...spec.routingKey === void 0 ? {} : { routingKey: spec.routingKey },
441
463
  unsettledName: spec.unsettledName ?? "reference"
442
464
  };
443
465
  return chainFrom({
@@ -482,7 +504,10 @@ function pack(name, matches, spec) {
482
504
  requiresImport: [
483
505
  .../* @__PURE__ */ new Set([...gateOf(chains), ...spec.requiresImport ?? []])
484
506
  ],
485
- invocationRecognizers: walkedBy(chains, "invocation", spec.recognizedAs),
507
+ invocationRecognizers: [
508
+ ...walkedBy(chains, "invocation", spec.recognizedAs),
509
+ ...spec.recognizers ?? []
510
+ ],
486
511
  ...access.length === 0 ? {} : { accessRecognizers: access },
487
512
  declarations: declarationsIn(matches)
488
513
  };
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ConstructedFrom, DeclaredBy, CallOps, InvocationRecognizer, PatternPack, UnsettledName, ReceiverOrigin, PackDeclarations } from '@suss/extractor';
2
2
  export { CallOps, ConstructedFrom, DeclaredBy, DeclaredMatch, OpsCarrier, PackDeclarations, PatternPack, ReceiverOrigin, UnsettledName, ValueEntry, ValueOps } from '@suss/extractor';
3
- import { C as Chain, M as MethodMeaning, a as MessageSendMethod, b as MessageLocation, c as ChannelPart, S as SqlMethod, I as InterpolatesLink, d as CallStep, e as StorageMethod, A as ArgumentPick, L as LinkFunction, O as OneArgument } from './chain-58V6lcel.cjs';
4
- export { f as AccessKind, g as AccessPathLink, h as ArgumentsFrom, i as ContainerArgument, j as ContainerLink, k as ContainerRule, l as ContainersLink, E as Ending, F as FromReceiver, m as InputLink, n as InputRule, K as KindAsAsked, o as Link, p as ManyIn, q as MatchStart, r as MessageSendEnding, s as MethodsLink, t as OneMessage, u as SqlEnding, v as StartLink, w as StatedInputs, x as StatedRule, y as StorageEnding, z as SubjectLink, T as ToArgument, B as ToReceiver } from './chain-58V6lcel.cjs';
3
+ import { C as Chain, M as MethodMeaning, a as MessageSendMethod, b as MessageLocation, c as ChannelPart, S as SqlMethod, I as InterpolatesLink, d as CallStep, e as StorageMethod, A as ArgumentPick, L as LinkFunction, O as OneArgument } from './chain-C7w99UMv.cjs';
4
+ export { f as AccessKind, g as AccessPathLink, h as ArgumentsFrom, i as ContainerArgument, j as ContainerLink, k as ContainerRule, l as ContainersLink, E as Ending, F as FromReceiver, m as InputLink, n as InputRule, K as KindAsAsked, o as Link, p as ManyIn, q as MatchStart, r as MessageSendEnding, s as MethodsLink, t as OneMessage, u as SqlEnding, v as StartLink, w as StatedInputs, x as StatedRule, y as StorageEnding, z as SubjectLink, T as ToArgument, B as ToReceiver } from './chain-C7w99UMv.cjs';
5
5
  import { Effect, MessageBusSemantics } from '@suss/behavioral-ir';
6
6
 
7
7
  /**
@@ -106,6 +106,11 @@ interface MessageSendsSpec {
106
106
  channelSeparator?: string;
107
107
  /** The property a message states its body on. */
108
108
  body?: string;
109
+ /**
110
+ * The property whose literal value rides along as the routing key,
111
+ * scoping the message for a reader without joining the channel.
112
+ */
113
+ routingKey?: string;
109
114
  /**
110
115
  * What a reader gives back for a channel nothing in the source
111
116
  * settles. Defaults to keeping the reference, since a queue named
@@ -175,6 +180,14 @@ interface PackSpec {
175
180
  * imports gives it something.
176
181
  */
177
182
  requiresImport?: string[];
183
+ /**
184
+ * Recognizers written against the adapter directly, run alongside the
185
+ * declared chains. A pack migrating one call at a time keeps what is
186
+ * not declared yet here, and a pack that needs a shape the endings
187
+ * cannot say keeps it here for good. Health reporting counts these as
188
+ * function links, so the cost of staying here is visible.
189
+ */
190
+ recognizers?: InvocationRecognizer[];
178
191
  }
179
192
  /** A pack, assembled from the calls it says it matches. */
180
193
  declare function pack(name: string, matches: readonly Match[], spec: PackSpec): PatternPack;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ConstructedFrom, DeclaredBy, CallOps, InvocationRecognizer, PatternPack, UnsettledName, ReceiverOrigin, PackDeclarations } from '@suss/extractor';
2
2
  export { CallOps, ConstructedFrom, DeclaredBy, DeclaredMatch, OpsCarrier, PackDeclarations, PatternPack, ReceiverOrigin, UnsettledName, ValueEntry, ValueOps } from '@suss/extractor';
3
- import { C as Chain, M as MethodMeaning, a as MessageSendMethod, b as MessageLocation, c as ChannelPart, S as SqlMethod, I as InterpolatesLink, d as CallStep, e as StorageMethod, A as ArgumentPick, L as LinkFunction, O as OneArgument } from './chain-58V6lcel.js';
4
- export { f as AccessKind, g as AccessPathLink, h as ArgumentsFrom, i as ContainerArgument, j as ContainerLink, k as ContainerRule, l as ContainersLink, E as Ending, F as FromReceiver, m as InputLink, n as InputRule, K as KindAsAsked, o as Link, p as ManyIn, q as MatchStart, r as MessageSendEnding, s as MethodsLink, t as OneMessage, u as SqlEnding, v as StartLink, w as StatedInputs, x as StatedRule, y as StorageEnding, z as SubjectLink, T as ToArgument, B as ToReceiver } from './chain-58V6lcel.js';
3
+ import { C as Chain, M as MethodMeaning, a as MessageSendMethod, b as MessageLocation, c as ChannelPart, S as SqlMethod, I as InterpolatesLink, d as CallStep, e as StorageMethod, A as ArgumentPick, L as LinkFunction, O as OneArgument } from './chain-C7w99UMv.js';
4
+ export { f as AccessKind, g as AccessPathLink, h as ArgumentsFrom, i as ContainerArgument, j as ContainerLink, k as ContainerRule, l as ContainersLink, E as Ending, F as FromReceiver, m as InputLink, n as InputRule, K as KindAsAsked, o as Link, p as ManyIn, q as MatchStart, r as MessageSendEnding, s as MethodsLink, t as OneMessage, u as SqlEnding, v as StartLink, w as StatedInputs, x as StatedRule, y as StorageEnding, z as SubjectLink, T as ToArgument, B as ToReceiver } from './chain-C7w99UMv.js';
5
5
  import { Effect, MessageBusSemantics } from '@suss/behavioral-ir';
6
6
 
7
7
  /**
@@ -106,6 +106,11 @@ interface MessageSendsSpec {
106
106
  channelSeparator?: string;
107
107
  /** The property a message states its body on. */
108
108
  body?: string;
109
+ /**
110
+ * The property whose literal value rides along as the routing key,
111
+ * scoping the message for a reader without joining the channel.
112
+ */
113
+ routingKey?: string;
109
114
  /**
110
115
  * What a reader gives back for a channel nothing in the source
111
116
  * settles. Defaults to keeping the reference, since a queue named
@@ -175,6 +180,14 @@ interface PackSpec {
175
180
  * imports gives it something.
176
181
  */
177
182
  requiresImport?: string[];
183
+ /**
184
+ * Recognizers written against the adapter directly, run alongside the
185
+ * declared chains. A pack migrating one call at a time keeps what is
186
+ * not declared yet here, and a pack that needs a shape the endings
187
+ * cannot say keeps it here for good. Health reporting counts these as
188
+ * function links, so the cost of staying here is visible.
189
+ */
190
+ recognizers?: InvocationRecognizer[];
178
191
  }
179
192
  /** A pack, assembled from the calls it says it matches. */
180
193
  declare function pack(name: string, matches: readonly Match[], spec: PackSpec): PatternPack;
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/compile.ts
2
2
  import { messageBusBinding, storageBinding } from "@suss/behavioral-ir";
3
+ import { unwrapJsonStringify } from "@suss/extractor";
3
4
  import { readSqlAccess, sqlFromParts } from "@suss/sql";
4
5
 
5
6
  // src/ops.ts
@@ -222,6 +223,7 @@ function statedValue(subject, pick) {
222
223
  var NOTHING_STATED = {
223
224
  text: () => null,
224
225
  name: () => null,
226
+ asArg: () => null,
225
227
  flag: () => null,
226
228
  entries: () => [],
227
229
  items: () => [],
@@ -285,17 +287,19 @@ function messageSend(matched) {
285
287
  return null;
286
288
  }
287
289
  const messages = messagesIn(input, ending.messages);
288
- return messages.length === 0 ? null : messages.map((message) => ({
290
+ const sent = messages.length === 0 ? [NOTHING_STATED] : messages;
291
+ return sent.map((message) => ({
289
292
  type: "interaction",
290
293
  binding: messageBusBinding({
291
294
  recognition,
292
295
  messageBus: ending.wire,
293
- channel: channelOf(message, ending)
296
+ channel: channelOf(message, input, ending)
294
297
  }),
295
298
  callee: ops.calleeText(),
296
299
  interaction: {
297
300
  class: "message-send",
298
- ...bodyOf(message, ending)
301
+ ...bodyOf(message, ending),
302
+ ...routingKeyOf(message, ending)
299
303
  }
300
304
  }));
301
305
  }
@@ -306,15 +310,23 @@ function messagesIn(input, location) {
306
310
  const collection = input.property(location.property);
307
311
  return collection === null ? [] : collection.items();
308
312
  }
309
- function channelOf(message, ending) {
313
+ function channelOf(message, input, ending) {
310
314
  const parts = [];
311
315
  for (const part of ending.channel) {
312
- const stated = message.property(part.property)?.name(ending.unsettledName) ?? null;
313
- const value = stated === null || stated === "" ? part.whenAbsent : stated;
314
- if (value === void 0) {
316
+ const holder = part.on === "theInput" ? input : message;
317
+ const written = holder.property(part.property);
318
+ if (written === null) {
319
+ if (part.whenAbsent === void 0) {
320
+ return null;
321
+ }
322
+ parts.push(part.whenAbsent);
323
+ continue;
324
+ }
325
+ const stated = written.name(part.unsettled ?? ending.unsettledName);
326
+ if (stated === null || stated === "") {
315
327
  return null;
316
328
  }
317
- parts.push(value);
329
+ parts.push(stated);
318
330
  }
319
331
  return parts.join(ending.channelSeparator ?? "#");
320
332
  }
@@ -322,9 +334,18 @@ function bodyOf(message, ending) {
322
334
  if (ending.body === void 0) {
323
335
  return {};
324
336
  }
325
- const stated = message.property(ending.body)?.text() ?? null;
337
+ const stated = unwrapJsonStringify(
338
+ message.property(ending.body)?.asArg() ?? null
339
+ );
326
340
  return stated === null ? {} : { body: stated };
327
341
  }
342
+ function routingKeyOf(message, ending) {
343
+ if (ending.routingKey === void 0) {
344
+ return {};
345
+ }
346
+ const stated = message.property(ending.routingKey)?.text() ?? null;
347
+ return stated === null || stated === "" ? {} : { routingKey: stated };
348
+ }
328
349
  function sqlAccess(matched) {
329
350
  const { ops, subject, method, chain, recognition } = matched;
330
351
  const ending = chain.ending;
@@ -402,6 +423,7 @@ function messageSends(spec) {
402
423
  channel: spec.channel,
403
424
  ...spec.channelSeparator === void 0 ? {} : { channelSeparator: spec.channelSeparator },
404
425
  ...spec.body === void 0 ? {} : { body: spec.body },
426
+ ...spec.routingKey === void 0 ? {} : { routingKey: spec.routingKey },
405
427
  unsettledName: spec.unsettledName ?? "reference"
406
428
  };
407
429
  return chainFrom({
@@ -446,7 +468,10 @@ function pack(name, matches, spec) {
446
468
  requiresImport: [
447
469
  .../* @__PURE__ */ new Set([...gateOf(chains), ...spec.requiresImport ?? []])
448
470
  ],
449
- invocationRecognizers: walkedBy(chains, "invocation", spec.recognizedAs),
471
+ invocationRecognizers: [
472
+ ...walkedBy(chains, "invocation", spec.recognizedAs),
473
+ ...spec.recognizers ?? []
474
+ ],
450
475
  ...access.length === 0 ? {} : { accessRecognizers: access },
451
476
  declarations: declarationsIn(matches)
452
477
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/recognize",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "description": "Write a pack as data: a chain of named links that any language adapter with the executor ops can run.",
6
6
  "keywords": [
@@ -49,9 +49,9 @@
49
49
  "typecheck": "tsc --noEmit"
50
50
  },
51
51
  "dependencies": {
52
- "@suss/behavioral-ir": "0.14.1",
53
- "@suss/extractor": "0.14.1",
54
- "@suss/sql": "0.14.1"
52
+ "@suss/behavioral-ir": "0.15.0",
53
+ "@suss/extractor": "0.15.0",
54
+ "@suss/sql": "0.15.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "tsup": "*",