@sendbird/actionbook-core 0.10.12 → 0.10.14

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/index.js CHANGED
@@ -2858,6 +2858,20 @@ function collectAllVariables(structures) {
2858
2858
  }
2859
2859
 
2860
2860
  // src/jinja/evaluator.ts
2861
+ var ERROR_MESSAGES = {
2862
+ UNTERMINATED_STRING: "Unfinished statement.",
2863
+ EMPTY_EXPRESSION: "Empty expression.",
2864
+ INVALID_EXPRESSION: "Invalid expression.",
2865
+ unexpectedChar: (char) => `Invalid character. ${char}`,
2866
+ expectedToken: (expected, got) => `Expected ${expected}, got ${got}`,
2867
+ unexpectedToken: (value) => `Invalid text at the end of the statement. ${value}`,
2868
+ unexpectedTokenInPrimary: (type, value) => {
2869
+ if (type === "RPAREN") {
2870
+ return "Missing closing parenthesis.";
2871
+ }
2872
+ return `Missing value.`;
2873
+ }
2874
+ };
2861
2875
  var KEYWORDS2 = {
2862
2876
  and: "AND",
2863
2877
  or: "OR",
@@ -2892,7 +2906,7 @@ function tokenize(input) {
2892
2906
  i++;
2893
2907
  }
2894
2908
  }
2895
- if (i >= input.length) return { tokens: [], error: "Unterminated string literal" };
2909
+ if (i >= input.length) return { tokens: [], error: ERROR_MESSAGES.UNTERMINATED_STRING };
2896
2910
  i++;
2897
2911
  tokens.push({ type: "STRING", value: str });
2898
2912
  continue;
@@ -2980,7 +2994,7 @@ function tokenize(input) {
2980
2994
  i++;
2981
2995
  continue;
2982
2996
  }
2983
- return { tokens: [], error: `Unexpected character: ${input[i]}` };
2997
+ return { tokens: [], error: ERROR_MESSAGES.unexpectedChar(input[i]) };
2984
2998
  }
2985
2999
  tokens.push({ type: "EOF", value: "" });
2986
3000
  return { tokens };
@@ -3004,7 +3018,7 @@ var Parser = class {
3004
3018
  expect(type) {
3005
3019
  const t = this.peek();
3006
3020
  if (t.type !== type) {
3007
- throw new Error(`Expected ${type}, got ${t.type}`);
3021
+ throw new Error(ERROR_MESSAGES.expectedToken(type, t.type));
3008
3022
  }
3009
3023
  return this.advance();
3010
3024
  }
@@ -3018,7 +3032,7 @@ var Parser = class {
3018
3032
  evaluate() {
3019
3033
  const result = this.orExpr();
3020
3034
  if (this.peek().type !== "EOF") {
3021
- throw new Error(`Unexpected token: ${this.peek().value}`);
3035
+ throw new Error(ERROR_MESSAGES.unexpectedToken(this.peek().value));
3022
3036
  }
3023
3037
  return result;
3024
3038
  }
@@ -3130,7 +3144,7 @@ var Parser = class {
3130
3144
  return val;
3131
3145
  }
3132
3146
  default:
3133
- throw new Error(`Unexpected token in primary: ${t.type} "${t.value}"`);
3147
+ throw new Error(ERROR_MESSAGES.unexpectedTokenInPrimary(t.type, t.value));
3134
3148
  }
3135
3149
  }
3136
3150
  resolveVariable(name) {
@@ -3187,13 +3201,13 @@ function validateCondition(condition) {
3187
3201
  if (trimmed === "") return { valid: true };
3188
3202
  const { tokens, error: tokenError } = tokenize(trimmed);
3189
3203
  if (tokenError) return { valid: false, error: tokenError };
3190
- if (tokens.length === 0) return { valid: false, error: "Empty expression" };
3204
+ if (tokens.length === 0) return { valid: false, error: ERROR_MESSAGES.EMPTY_EXPRESSION };
3191
3205
  try {
3192
3206
  const parser = new Parser(tokens, /* @__PURE__ */ new Map());
3193
3207
  parser.evaluate();
3194
3208
  return { valid: true };
3195
3209
  } catch (e) {
3196
- const msg = e instanceof Error ? e.message : "Invalid expression";
3210
+ const msg = e instanceof Error ? e.message : ERROR_MESSAGES.INVALID_EXPRESSION;
3197
3211
  return { valid: false, error: msg };
3198
3212
  }
3199
3213
  }
@@ -5019,12 +5033,9 @@ function actionbookToAST(manual) {
5019
5033
  }
5020
5034
 
5021
5035
  // src/graph/builder.ts
5022
- var idCounter = 0;
5023
- function nextId() {
5024
- return `vn_${++idCounter}`;
5025
- }
5026
- function resetIds() {
5027
- idCounter = 0;
5036
+ function createIdGenerator() {
5037
+ let counter = 0;
5038
+ return () => `vn_${++counter}`;
5028
5039
  }
5029
5040
  function truncate(s, max = 40) {
5030
5041
  const t = s.trim();
@@ -5127,10 +5138,29 @@ function merge(target, source) {
5127
5138
  }
5128
5139
  target.annotations.push(...source.annotations);
5129
5140
  }
5141
+ function extractFirstLabel(blocks) {
5142
+ for (const b of blocks) {
5143
+ if (b.type === "paragraph" || b.type === "heading") {
5144
+ return inlineSummary2(b.content || []);
5145
+ }
5146
+ if (b.type === "bulletList" || b.type === "orderedList") {
5147
+ const firstLi = b.content[0];
5148
+ if (firstLi) {
5149
+ const result = extractFirstLabel(firstLi.content);
5150
+ if (result) return result;
5151
+ }
5152
+ }
5153
+ }
5154
+ return null;
5155
+ }
5130
5156
  function isBulletListBranching(block) {
5131
5157
  if (block.type !== "bulletList") return false;
5132
5158
  for (const li of block.content) {
5133
5159
  if (li.content.length > 1) return true;
5160
+ if (li.content.length === 1) {
5161
+ const only = li.content[0];
5162
+ if (only.type === "bulletList" || only.type === "orderedList" || only.type === "jinjaIfBlock") return true;
5163
+ }
5134
5164
  const analysis = analyzeBlock(li);
5135
5165
  if (analysis.hasHandoff || analysis.hasEndCall || analysis.hasToolCall || analysis.hasGoto) return true;
5136
5166
  }
@@ -5153,7 +5183,7 @@ function processBlocks(blocks, startBlockIndex, ctx) {
5153
5183
  function flushContent() {
5154
5184
  if (!contentAccum) return null;
5155
5185
  const node = {
5156
- id: nextId(),
5186
+ id: ctx.nextId(),
5157
5187
  type: "CONTENT",
5158
5188
  label: truncate(contentAccum.lines[0] || ""),
5159
5189
  lines: contentAccum.lines,
@@ -5211,7 +5241,7 @@ function processBlocks(blocks, startBlockIndex, ctx) {
5211
5241
  if (analysis.hasHandoff) {
5212
5242
  flushContent();
5213
5243
  const node = {
5214
- id: nextId(),
5244
+ id: ctx.nextId(),
5215
5245
  type: "HANDOFF",
5216
5246
  label: "Handoff",
5217
5247
  lines: [text2],
@@ -5228,7 +5258,7 @@ function processBlocks(blocks, startBlockIndex, ctx) {
5228
5258
  if (analysis.hasEndCall) {
5229
5259
  flushContent();
5230
5260
  const node = {
5231
- id: nextId(),
5261
+ id: ctx.nextId(),
5232
5262
  type: "END_CONVERSATION",
5233
5263
  label: "End",
5234
5264
  lines: [text2],
@@ -5255,33 +5285,13 @@ function processBlocks(blocks, startBlockIndex, ctx) {
5255
5285
  if (block.type === "orderedList") {
5256
5286
  const items = block.content;
5257
5287
  for (const li of items) {
5258
- const text2 = inlineSummary2(li.content[0]?.content || [], 60);
5259
- const analysis = analyzeBlock(li);
5260
- if (analysis.hasHandoff) {
5288
+ const sub = processBlocks(li.content, blockIndex, ctx);
5289
+ if (sub.firstId) {
5261
5290
  flushContent();
5262
- const node = {
5263
- id: nextId(),
5264
- type: "HANDOFF",
5265
- label: "Handoff",
5266
- lines: [text2],
5267
- section: ctx.currentSection,
5268
- annotations: analysis.annotations,
5269
- sourceBlockRange: { start: blockIndex, end: blockIndex }
5270
- };
5271
- ctx.nodes.push(node);
5272
- for (const pid of prevLastIds) addEdge(ctx, pid, node.id, "SEQUENTIAL");
5273
- if (!firstId) firstId = node.id;
5274
- prevLastIds = [node.id];
5275
- continue;
5276
- }
5277
- if (!contentAccum) {
5278
- contentAccum = { lines: [], annotations: [], startIdx: blockIndex, endIdx: blockIndex, jumpPointIds: [] };
5291
+ for (const pid of prevLastIds) addEdge(ctx, pid, sub.firstId, "SEQUENTIAL");
5292
+ if (!firstId) firstId = sub.firstId;
5293
+ prevLastIds = sub.lastIds;
5279
5294
  }
5280
- contentAccum.lines.push(text2);
5281
- contentAccum.annotations.push(...analysis.annotations);
5282
- contentAccum.endIdx = blockIndex;
5283
- contentAccum.jumpPointIds.push(...analysis.jumpPointIds);
5284
- if (analysis.hasGoto) contentAccum.gotoTarget = analysis.gotoTarget ?? void 0;
5285
5295
  }
5286
5296
  continue;
5287
5297
  }
@@ -5304,9 +5314,9 @@ function processBlocks(blocks, startBlockIndex, ctx) {
5304
5314
  for (let li = 0; li < items.length; li++) {
5305
5315
  const item = items[li];
5306
5316
  const firstBlock = item.content[0];
5307
- const conditionText = firstBlock && (firstBlock.type === "paragraph" || firstBlock.type === "heading") ? inlineSummary2(firstBlock.content || []) : `Branch ${li + 1}`;
5317
+ const conditionText = firstBlock && (firstBlock.type === "paragraph" || firstBlock.type === "heading") ? inlineSummary2(firstBlock.content || []) : extractFirstLabel(item.content) || `Branch ${li + 1}`;
5308
5318
  const condNode = {
5309
- id: nextId(),
5319
+ id: ctx.nextId(),
5310
5320
  type: "BRANCH",
5311
5321
  label: conditionText,
5312
5322
  lines: [conditionText],
@@ -5338,7 +5348,7 @@ function processBlocks(blocks, startBlockIndex, ctx) {
5338
5348
  if (block.type === "jinjaIfBlock") {
5339
5349
  flushContent();
5340
5350
  const branchNode = {
5341
- id: nextId(),
5351
+ id: ctx.nextId(),
5342
5352
  type: "BRANCH",
5343
5353
  label: "Jinja If",
5344
5354
  lines: [],
@@ -5370,13 +5380,13 @@ function processBlocks(blocks, startBlockIndex, ctx) {
5370
5380
  return { firstId, lastIds: prevLastIds };
5371
5381
  }
5372
5382
  function buildVizGraph(doc2) {
5373
- resetIds();
5374
5383
  const ctx = {
5375
5384
  nodes: [],
5376
5385
  edges: [],
5377
5386
  anchorMap: {},
5378
5387
  currentSection: "_default",
5379
- edgeCounter: 0
5388
+ edgeCounter: 0,
5389
+ nextId: createIdGenerator()
5380
5390
  };
5381
5391
  const startNode = {
5382
5392
  id: "START",
@@ -5392,20 +5402,23 @@ function buildVizGraph(doc2) {
5392
5402
  if (result.firstId) {
5393
5403
  addEdge(ctx, "START", result.firstId, "SEQUENTIAL");
5394
5404
  }
5395
- for (const lastId of result.lastIds) {
5396
- const lastNode = ctx.nodes.find((n) => n.id === lastId);
5397
- if (lastNode && lastNode.type !== "HANDOFF" && lastNode.type !== "END_CONVERSATION") {
5398
- const endNode = {
5399
- id: nextId(),
5400
- type: "END_CONVERSATION",
5401
- label: "End",
5402
- lines: [],
5403
- section: lastNode.section,
5404
- annotations: [],
5405
- sourceBlockRange: { start: -1, end: -1 }
5406
- };
5407
- ctx.nodes.push(endNode);
5408
- addEdge(ctx, lastId, endNode.id, "SEQUENTIAL");
5405
+ const danglingIds = result.lastIds.filter((id) => {
5406
+ const n = ctx.nodes.find((node) => node.id === id);
5407
+ return n && n.type !== "HANDOFF" && n.type !== "END_CONVERSATION";
5408
+ });
5409
+ if (danglingIds.length > 0) {
5410
+ const endNode = {
5411
+ id: ctx.nextId(),
5412
+ type: "END_CONVERSATION",
5413
+ label: "End",
5414
+ lines: [],
5415
+ section: ctx.currentSection,
5416
+ annotations: [],
5417
+ sourceBlockRange: { start: -1, end: -1 }
5418
+ };
5419
+ ctx.nodes.push(endNode);
5420
+ for (const id of danglingIds) {
5421
+ addEdge(ctx, id, endNode.id, "SEQUENTIAL");
5409
5422
  }
5410
5423
  }
5411
5424
  for (const node of ctx.nodes) {
@@ -5414,7 +5427,7 @@ function buildVizGraph(doc2) {
5414
5427
  if (targetNodeId) {
5415
5428
  addEdge(ctx, node.id, targetNodeId, "GOTO", node.gotoTarget);
5416
5429
  } else {
5417
- addEdge(ctx, node.id, node.id, "BROKEN_REF", node.gotoTarget);
5430
+ node.annotations = [...node.annotations, { kind: "jump_point", label: `\u26A0 ${node.gotoTarget} (broken)` }];
5418
5431
  }
5419
5432
  }
5420
5433
  const sectionMap = /* @__PURE__ */ new Map();