@bubblelab/bubble-runtime 0.1.14 → 0.1.16

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 (69) hide show
  1. package/dist/extraction/BubbleParser.d.ts +187 -8
  2. package/dist/extraction/BubbleParser.d.ts.map +1 -1
  3. package/dist/extraction/BubbleParser.js +2271 -117
  4. package/dist/extraction/BubbleParser.js.map +1 -1
  5. package/dist/extraction/index.js +1 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/injection/BubbleInjector.d.ts +27 -2
  11. package/dist/injection/BubbleInjector.d.ts.map +1 -1
  12. package/dist/injection/BubbleInjector.js +343 -35
  13. package/dist/injection/BubbleInjector.js.map +1 -1
  14. package/dist/injection/LoggerInjector.d.ts +12 -1
  15. package/dist/injection/LoggerInjector.d.ts.map +1 -1
  16. package/dist/injection/LoggerInjector.js +301 -13
  17. package/dist/injection/LoggerInjector.js.map +1 -1
  18. package/dist/injection/index.js +1 -0
  19. package/dist/parse/BubbleScript.d.ts +60 -3
  20. package/dist/parse/BubbleScript.d.ts.map +1 -1
  21. package/dist/parse/BubbleScript.js +133 -15
  22. package/dist/parse/BubbleScript.js.map +1 -1
  23. package/dist/parse/index.d.ts +0 -1
  24. package/dist/parse/index.d.ts.map +1 -1
  25. package/dist/parse/index.js +1 -1
  26. package/dist/parse/index.js.map +1 -1
  27. package/dist/runtime/BubbleRunner.d.ts +8 -2
  28. package/dist/runtime/BubbleRunner.d.ts.map +1 -1
  29. package/dist/runtime/BubbleRunner.js +41 -30
  30. package/dist/runtime/BubbleRunner.js.map +1 -1
  31. package/dist/runtime/index.d.ts +1 -1
  32. package/dist/runtime/index.d.ts.map +1 -1
  33. package/dist/runtime/index.js +1 -0
  34. package/dist/runtime/index.js.map +1 -1
  35. package/dist/runtime/types.js +1 -0
  36. package/dist/types/index.js +1 -0
  37. package/dist/utils/bubble-helper.d.ts +2 -2
  38. package/dist/utils/bubble-helper.d.ts.map +1 -1
  39. package/dist/utils/bubble-helper.js +6 -1
  40. package/dist/utils/bubble-helper.js.map +1 -1
  41. package/dist/utils/normalize-control-flow.d.ts +14 -0
  42. package/dist/utils/normalize-control-flow.d.ts.map +1 -0
  43. package/dist/utils/normalize-control-flow.js +179 -0
  44. package/dist/utils/normalize-control-flow.js.map +1 -0
  45. package/dist/utils/parameter-formatter.d.ts +14 -5
  46. package/dist/utils/parameter-formatter.d.ts.map +1 -1
  47. package/dist/utils/parameter-formatter.js +164 -45
  48. package/dist/utils/parameter-formatter.js.map +1 -1
  49. package/dist/utils/sanitize-script.d.ts +11 -0
  50. package/dist/utils/sanitize-script.d.ts.map +1 -0
  51. package/dist/utils/sanitize-script.js +43 -0
  52. package/dist/utils/sanitize-script.js.map +1 -0
  53. package/dist/validation/BubbleValidator.d.ts +15 -0
  54. package/dist/validation/BubbleValidator.d.ts.map +1 -1
  55. package/dist/validation/BubbleValidator.js +168 -1
  56. package/dist/validation/BubbleValidator.js.map +1 -1
  57. package/dist/validation/index.d.ts +6 -3
  58. package/dist/validation/index.d.ts.map +1 -1
  59. package/dist/validation/index.js +33 -9
  60. package/dist/validation/index.js.map +1 -1
  61. package/dist/validation/lint-rules.d.ts +91 -0
  62. package/dist/validation/lint-rules.d.ts.map +1 -0
  63. package/dist/validation/lint-rules.js +755 -0
  64. package/dist/validation/lint-rules.js.map +1 -0
  65. package/package.json +4 -4
  66. package/dist/parse/traceDependencies.d.ts +0 -18
  67. package/dist/parse/traceDependencies.d.ts.map +0 -1
  68. package/dist/parse/traceDependencies.js +0 -195
  69. package/dist/parse/traceDependencies.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA4B,MAAM,gBAAgB,CAAC"}
@@ -1 +1,2 @@
1
1
  export {};
2
+ //# sourceMappingURL=types.js.map
@@ -1 +1,2 @@
1
1
  export {};
2
+ //# sourceMappingURL=index.js.map
@@ -1,10 +1,10 @@
1
1
  import { BubbleFactory } from '@bubblelab/bubble-core';
2
- import { BubbleNodeType } from '@bubblelab/shared-schemas';
2
+ import { BubbleName, BubbleNodeType } from '@bubblelab/shared-schemas';
3
3
  /**
4
4
  * Build a lookup map from className to bubble metadata
5
5
  */
6
6
  export declare function buildClassNameLookup(factory: BubbleFactory): Map<string, {
7
- bubbleName: string;
7
+ bubbleName: BubbleName;
8
8
  className: string;
9
9
  nodeType: BubbleNodeType;
10
10
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"bubble-helper.d.ts","sourceRoot":"","sources":["../../src/utils/bubble-helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,GACrB,GAAG,CACJ,MAAM,EACN;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,CACpE,CAiBA"}
1
+ {"version":3,"file":"bubble-helper.d.ts","sourceRoot":"","sources":["../../src/utils/bubble-helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEvE;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,GACrB,GAAG,CACJ,MAAM,EACN;IAAE,UAAU,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,CACxE,CAqBA"}
@@ -8,7 +8,12 @@ export function buildClassNameLookup(factory) {
8
8
  const className = ctor.name;
9
9
  const bubbleName = ctor.bubbleName ?? className;
10
10
  const nodeType = ctor.type ?? 'unknown';
11
- lookup.set(className, { bubbleName, className, nodeType });
11
+ lookup.set(className, {
12
+ bubbleName: bubbleName,
13
+ className,
14
+ nodeType,
15
+ });
12
16
  }
13
17
  return lookup;
14
18
  }
19
+ //# sourceMappingURL=bubble-helper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bubble-helper.js","sourceRoot":"","sources":["../../src/utils/bubble-helper.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAsB;IAKtB,MAAM,MAAM,GAAG,IAAI,GAAG,EAGnB,CAAC;IACJ,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,EAA+B,CAAC;IAE1D,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;QACvB,MAAM,SAAS,GAAI,IAAoC,CAAC,IAAI,CAAC;QAC7D,MAAM,UAAU,GACb,IAA2C,CAAC,UAAU,IAAI,SAAS,CAAC;QACvE,MAAM,QAAQ,GACX,IAA6C,CAAC,IAAI,IAAI,SAAS,CAAC;QACnE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"bubble-helper.js","sourceRoot":"","sources":["../../src/utils/bubble-helper.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAsB;IAKtB,MAAM,MAAM,GAAG,IAAI,GAAG,EAGnB,CAAC;IACJ,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,EAA+B,CAAC;IAE1D,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;QACvB,MAAM,SAAS,GAAI,IAAoC,CAAC,IAAI,CAAC;QAC7D,MAAM,UAAU,GACb,IAA2C,CAAC,UAAU,IAAI,SAAS,CAAC;QACvE,MAAM,QAAQ,GACX,IAA6C,CAAC,IAAI,IAAI,SAAS,CAAC;QACnE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE;YACpB,UAAU,EAAE,UAAwB;YACpC,SAAS;YACT,QAAQ;SACT,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Normalizes braceless control flow statements by wrapping them with braces.
3
+ * This prevents issues when injecting logging or other statements into the code.
4
+ *
5
+ * Transforms:
6
+ * if (x) doSomething();
7
+ * else doOther();
8
+ *
9
+ * Into:
10
+ * if (x) { doSomething(); }
11
+ * else { doOther(); }
12
+ */
13
+ export declare function normalizeBracelessControlFlow(code: string): string;
14
+ //# sourceMappingURL=normalize-control-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize-control-flow.d.ts","sourceRoot":"","sources":["../../src/utils/normalize-control-flow.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkClE"}
@@ -0,0 +1,179 @@
1
+ import { parse } from '@typescript-eslint/typescript-estree';
2
+ /**
3
+ * Normalizes braceless control flow statements by wrapping them with braces.
4
+ * This prevents issues when injecting logging or other statements into the code.
5
+ *
6
+ * Transforms:
7
+ * if (x) doSomething();
8
+ * else doOther();
9
+ *
10
+ * Into:
11
+ * if (x) { doSomething(); }
12
+ * else { doOther(); }
13
+ */
14
+ export function normalizeBracelessControlFlow(code) {
15
+ let ast;
16
+ try {
17
+ ast = parse(code, {
18
+ range: true,
19
+ loc: true,
20
+ sourceType: 'module',
21
+ ecmaVersion: 2022,
22
+ });
23
+ }
24
+ catch {
25
+ // If parsing fails, return original code
26
+ return code;
27
+ }
28
+ const insertions = [];
29
+ // Collect all braceless control flow bodies
30
+ collectBracelessBodies(ast, code, insertions);
31
+ if (insertions.length === 0) {
32
+ return code;
33
+ }
34
+ // Sort insertions by position in reverse order to preserve positions during modification
35
+ insertions.sort((a, b) => b.openBracePos - a.openBracePos);
36
+ // Apply insertions
37
+ let result = code;
38
+ for (const insertion of insertions) {
39
+ result = applyBraceInsertion(result, insertion);
40
+ }
41
+ return result;
42
+ }
43
+ function collectBracelessBodies(node, code, insertions) {
44
+ if (!node || typeof node !== 'object')
45
+ return;
46
+ // Check if statement
47
+ if (node.type === 'IfStatement') {
48
+ const ifStmt = node;
49
+ // Check consequent (then branch)
50
+ if (ifStmt.consequent.type !== 'BlockStatement') {
51
+ const insertion = createBraceInsertion(ifStmt, ifStmt.consequent, code, 'consequent');
52
+ if (insertion) {
53
+ insertions.push(insertion);
54
+ }
55
+ }
56
+ // Check alternate (else branch) - but not if it's another IfStatement (else if)
57
+ if (ifStmt.alternate &&
58
+ ifStmt.alternate.type !== 'BlockStatement' &&
59
+ ifStmt.alternate.type !== 'IfStatement') {
60
+ const insertion = createBraceInsertion(ifStmt, ifStmt.alternate, code, 'alternate');
61
+ if (insertion) {
62
+ insertions.push(insertion);
63
+ }
64
+ }
65
+ }
66
+ // Check for statement
67
+ if (node.type === 'ForStatement') {
68
+ const forStmt = node;
69
+ if (forStmt.body.type !== 'BlockStatement') {
70
+ const insertion = createBraceInsertion(forStmt, forStmt.body, code);
71
+ if (insertion) {
72
+ insertions.push(insertion);
73
+ }
74
+ }
75
+ }
76
+ // Check for-in statement
77
+ if (node.type === 'ForInStatement') {
78
+ const forInStmt = node;
79
+ if (forInStmt.body.type !== 'BlockStatement') {
80
+ const insertion = createBraceInsertion(forInStmt, forInStmt.body, code);
81
+ if (insertion) {
82
+ insertions.push(insertion);
83
+ }
84
+ }
85
+ }
86
+ // Check for-of statement
87
+ if (node.type === 'ForOfStatement') {
88
+ const forOfStmt = node;
89
+ if (forOfStmt.body.type !== 'BlockStatement') {
90
+ const insertion = createBraceInsertion(forOfStmt, forOfStmt.body, code);
91
+ if (insertion) {
92
+ insertions.push(insertion);
93
+ }
94
+ }
95
+ }
96
+ // Check while statement
97
+ if (node.type === 'WhileStatement') {
98
+ const whileStmt = node;
99
+ if (whileStmt.body.type !== 'BlockStatement') {
100
+ const insertion = createBraceInsertion(whileStmt, whileStmt.body, code);
101
+ if (insertion) {
102
+ insertions.push(insertion);
103
+ }
104
+ }
105
+ }
106
+ // Check do-while statement
107
+ if (node.type === 'DoWhileStatement') {
108
+ const doWhileStmt = node;
109
+ if (doWhileStmt.body.type !== 'BlockStatement') {
110
+ const insertion = createBraceInsertion(doWhileStmt, doWhileStmt.body, code);
111
+ if (insertion) {
112
+ insertions.push(insertion);
113
+ }
114
+ }
115
+ }
116
+ // Recursively process children
117
+ for (const key of Object.keys(node)) {
118
+ if (key === 'parent' || key === 'loc' || key === 'range')
119
+ continue;
120
+ const child = node[key];
121
+ if (Array.isArray(child)) {
122
+ for (const item of child) {
123
+ if (item && typeof item === 'object' && 'type' in item) {
124
+ collectBracelessBodies(item, code, insertions);
125
+ }
126
+ }
127
+ }
128
+ else if (child && typeof child === 'object' && 'type' in child) {
129
+ collectBracelessBodies(child, code, insertions);
130
+ }
131
+ }
132
+ }
133
+ function createBraceInsertion(parent, body, code, branchType) {
134
+ if (!body.range)
135
+ return null;
136
+ // For 'else' branches, we need to find where the 'else' keyword ends
137
+ let openBracePos;
138
+ if (branchType === 'alternate') {
139
+ // Find the 'else' keyword before this statement
140
+ // Search backwards from the body start to find 'else'
141
+ const searchStart = Math.max(0, body.range[0] - 20);
142
+ const searchRegion = code.substring(searchStart, body.range[0]);
143
+ const elseMatch = searchRegion.match(/else\s*$/);
144
+ if (elseMatch) {
145
+ openBracePos = body.range[0];
146
+ }
147
+ else {
148
+ openBracePos = body.range[0];
149
+ }
150
+ }
151
+ else if (branchType === 'consequent') {
152
+ // For 'if' consequent, insert after the closing paren of condition
153
+ openBracePos = body.range[0];
154
+ }
155
+ else {
156
+ // For for/while loops, insert after the closing paren
157
+ openBracePos = body.range[0];
158
+ }
159
+ // Get the indentation of the parent statement
160
+ const parentLine = code.substring(0, parent.range[0]).split('\n').pop() || '';
161
+ const indentation = parentLine.match(/^\s*/)?.[0] || '';
162
+ return {
163
+ openBracePos,
164
+ closeBracePos: body.range[1],
165
+ indentation,
166
+ };
167
+ }
168
+ function applyBraceInsertion(code, insertion) {
169
+ const { openBracePos, closeBracePos, indentation } = insertion;
170
+ // Get the content between the positions
171
+ const beforeOpen = code.substring(0, openBracePos);
172
+ const bodyContent = code.substring(openBracePos, closeBracePos);
173
+ const afterClose = code.substring(closeBracePos);
174
+ // Always put the statement on its own line inside the braces
175
+ // This ensures logging can be injected after the statement but before the closing brace
176
+ const bodyIndent = indentation + ' ';
177
+ return `${beforeOpen}{\n${bodyIndent}${bodyContent.trim()}\n${indentation}}${afterClose}`;
178
+ }
179
+ //# sourceMappingURL=normalize-control-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize-control-flow.js","sourceRoot":"","sources":["../../src/utils/normalize-control-flow.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,13 @@
1
1
  /**
2
2
  * Utility functions for formatting bubble parameters
3
3
  */
4
+ import { BubbleParameter, ParsedBubbleWithInfo } from '@bubblelab/shared-schemas';
4
5
  /**
5
- * Build parameters object string from bubble parameters
6
+ * Check if a string contains function literal patterns.
7
+ * When function literals are present, the source code must be preserved as-is
8
+ * because functions cannot be safely serialized or condensed to single-line.
6
9
  */
7
- import { BubbleParameter, ParsedBubbleWithInfo } from '@bubblelab/shared-schemas';
10
+ export declare function containsFunctionLiteral(value: string): boolean;
8
11
  export declare function buildParametersObject(parameters: BubbleParameter[], variableId?: number, includeLoggerConfig?: boolean, dependencyGraphLiteral?: string, currentUniqueId?: string): string;
9
12
  /**
10
13
  * Format a parameter value based on its type
@@ -15,13 +18,19 @@ export declare function formatParameterValue(value: unknown, type: string): stri
15
18
  * Returns an array of objects with at least a name field, or null if parsing fails.
16
19
  */
17
20
  export declare function parseToolsParamValue(raw: unknown): Array<Record<string, unknown>> | null;
21
+ /**
22
+ * Condense a parameters string to a single line.
23
+ * Used when parameters don't contain function literals.
24
+ */
25
+ export declare function condenseToSingleLine(input: string): string;
18
26
  /**
19
27
  * Replace a bubble instantiation with updated parameters
20
28
  *
21
29
  * This function:
22
- * 1. Replaces the bubble instantiation line with a single-line version
23
- * 2. Deletes all lines that were part of the original multi-line parameters
24
- * 3. Uses bubble.location.endLine to know exactly where to stop deleting
30
+ * 1. Replaces the bubble instantiation line with updated parameters
31
+ * 2. Preserves multi-line structure when function literals are present
32
+ * 3. Condenses to single-line otherwise
33
+ * 4. Uses bubble.location.endLine to know exactly where to stop deleting
25
34
  */
26
35
  export declare function replaceBubbleInstantiation(lines: string[], bubble: ParsedBubbleWithInfo): void;
27
36
  //# sourceMappingURL=parameter-formatter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parameter-formatter.d.ts","sourceRoot":"","sources":["../../src/utils/parameter-formatter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,OAAO,EACL,eAAe,EACf,oBAAoB,EACrB,MAAM,2BAA2B,CAAC;AAEnC,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,eAAe,EAAE,EAC7B,UAAU,CAAC,EAAE,MAAM,EACnB,mBAAmB,GAAE,OAAc,EACnC,sBAAsB,CAAC,EAAE,MAAM,EAC/B,eAAe,GAAE,MAAW,GAC3B,MAAM,CA2ER;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CA8CzE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,GACX,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAyBvC;AAoID;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,EAAE,oBAAoB,QA+E7B"}
1
+ {"version":3,"file":"parameter-formatter.d.ts","sourceRoot":"","sources":["../../src/utils/parameter-formatter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,EACrB,MAAM,2BAA2B,CAAC;AAkBnC;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9D;AAuBD,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,eAAe,EAAE,EAC7B,UAAU,CAAC,EAAE,MAAM,EACnB,mBAAmB,GAAE,OAAc,EACnC,sBAAsB,CAAC,EAAE,MAAM,EAC/B,eAAe,GAAE,MAAW,GAC3B,MAAM,CAgJR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAsDzE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,GACX,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAyBvC;AAoID;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAmCD;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,EAAE,oBAAoB,QA0E7B"}
@@ -1,53 +1,128 @@
1
1
  /**
2
2
  * Utility functions for formatting bubble parameters
3
3
  */
4
+ const INVOCATION_KEY_EXPR = '__bubbleFlowSelf?.__getInvocationCallSiteKey?.() ?? ""';
5
+ /**
6
+ * Patterns that indicate function literals in source code.
7
+ * Used to detect when parameters contain functions that cannot be safely condensed.
8
+ */
9
+ const FUNCTION_LITERAL_PATTERNS = [
10
+ 'func:', // Object property with function value
11
+ '=>', // Arrow function
12
+ 'function(', // Function expression
13
+ 'function (', // Function expression with space
14
+ 'async(', // Async arrow function
15
+ 'async (', // Async function with space
16
+ ];
17
+ /**
18
+ * Check if a string contains function literal patterns.
19
+ * When function literals are present, the source code must be preserved as-is
20
+ * because functions cannot be safely serialized or condensed to single-line.
21
+ */
22
+ export function containsFunctionLiteral(value) {
23
+ return FUNCTION_LITERAL_PATTERNS.some((pattern) => value.includes(pattern));
24
+ }
25
+ function buildInvocationOverrideReference(variableId) {
26
+ if (typeof variableId !== 'number') {
27
+ return undefined;
28
+ }
29
+ return `globalThis["__bubbleInvocationDependencyGraphs"]?.[${INVOCATION_KEY_EXPR}]?.[${JSON.stringify(String(variableId))}]`;
30
+ }
31
+ function buildDependencyGraphExpression(dependencyGraphLiteral, overrideRef) {
32
+ if (overrideRef) {
33
+ return `${overrideRef} ?? ${dependencyGraphLiteral}`;
34
+ }
35
+ return dependencyGraphLiteral;
36
+ }
4
37
  export function buildParametersObject(parameters, variableId, includeLoggerConfig = true, dependencyGraphLiteral, currentUniqueId = '') {
5
38
  if (!parameters || parameters.length === 0) {
6
39
  return '{}';
7
40
  }
41
+ const dependencyGraphLiteralSafe = dependencyGraphLiteral && dependencyGraphLiteral.length > 0
42
+ ? dependencyGraphLiteral
43
+ : undefined;
44
+ const invocationOverrideRef = buildInvocationOverrideReference(variableId);
45
+ const dependencyGraphExpr = dependencyGraphLiteralSafe !== undefined
46
+ ? buildDependencyGraphExpression(dependencyGraphLiteralSafe, invocationOverrideRef)
47
+ : (invocationOverrideRef ?? undefined);
48
+ const currentUniqueIdLiteral = JSON.stringify(currentUniqueId);
49
+ const currentUniqueIdExpr = invocationOverrideRef !== undefined
50
+ ? `${invocationOverrideRef}?.uniqueId ?? ${currentUniqueIdLiteral}`
51
+ : currentUniqueIdLiteral;
8
52
  // Handle single variable parameter case (e.g., new GoogleDriveBubble(params))
9
53
  if (parameters.length === 1 && parameters[0].type === 'variable') {
10
54
  const paramValue = formatParameterValue(parameters[0].value, parameters[0].type);
11
55
  if (includeLoggerConfig) {
12
- const depGraphPart = dependencyGraphLiteral && dependencyGraphLiteral.length > 0
13
- ? `, dependencyGraph: ${dependencyGraphLiteral}`
56
+ const variableIdExpr = typeof variableId === 'number'
57
+ ? `(__bubbleFlowSelf?.__computeInvocationVariableId?.(${variableId}) ?? ${variableId})`
58
+ : 'undefined';
59
+ const depGraphPart = dependencyGraphExpr !== undefined
60
+ ? `, dependencyGraph: ${dependencyGraphExpr}`
14
61
  : '';
15
- const currentIdPart = `, currentUniqueId: ${JSON.stringify(currentUniqueId)}`;
16
- return `${paramValue}, {logger: __bubbleFlowSelf.logger, variableId: ${variableId}${depGraphPart}${currentIdPart}}`;
62
+ const currentIdPart = `, currentUniqueId: ${currentUniqueIdExpr}`;
63
+ const invocationKeyPart = ', invocationCallSiteKey: __bubbleFlowSelf?.__getInvocationCallSiteKey?.()';
64
+ return `${paramValue}, {logger: __bubbleFlowSelf.logger, variableId: ${variableIdExpr}${depGraphPart}${currentIdPart}${invocationKeyPart}}`;
17
65
  }
18
66
  return paramValue;
19
67
  }
20
- // Handle single variable parameter + credentials case (e.g., new GoogleDriveBubble(params) with injected credentials)
21
- if (parameters.length === 2 &&
22
- parameters.some((p) => p.type === 'variable') &&
23
- parameters.some((p) => p.name === 'credentials' && p.type === 'object')) {
24
- const paramsParam = parameters.find((p) => p.type === 'variable');
25
- const credentialsParam = parameters.find((p) => p.name === 'credentials' && p.type === 'object');
26
- if (paramsParam && credentialsParam) {
68
+ const nonCredentialParams = parameters.filter((p) => p.name !== 'credentials');
69
+ const credentialsParam = parameters.find((p) => p.name === 'credentials' && p.type === 'object');
70
+ // Separate spreads from regular properties
71
+ const spreadParams = nonCredentialParams.filter((p) => p.source === 'spread');
72
+ const regularParams = nonCredentialParams.filter((p) => p.source !== 'spread');
73
+ // Handle single variable parameter + credentials case (existing logic)
74
+ if (credentialsParam &&
75
+ nonCredentialParams.length === 1 &&
76
+ nonCredentialParams[0].type === 'variable') {
77
+ const paramsParam = nonCredentialParams[0];
78
+ // Only spread if the parameter source is 'first-arg' (represents entire first argument),
79
+ // or if source is undefined (backward compatibility) and name is 'arg0' (parser's fallback).
80
+ const shouldSpread = paramsParam.source === 'first-arg' ||
81
+ (paramsParam.source === undefined && paramsParam.name === 'arg0');
82
+ if (shouldSpread) {
27
83
  const paramsValue = formatParameterValue(paramsParam.value, paramsParam.type);
28
84
  const credentialsValue = formatParameterValue(credentialsParam.value, credentialsParam.type);
29
85
  if (includeLoggerConfig) {
30
- const depGraphPart = dependencyGraphLiteral && dependencyGraphLiteral.length > 0
31
- ? `, dependencyGraph: ${dependencyGraphLiteral}`
86
+ const variableIdExpr = typeof variableId === 'number'
87
+ ? `(__bubbleFlowSelf?.__computeInvocationVariableId?.(${variableId}) ?? ${variableId})`
88
+ : 'undefined';
89
+ const depGraphPart = dependencyGraphExpr !== undefined
90
+ ? `, dependencyGraph: ${dependencyGraphExpr}`
32
91
  : '';
33
- const currentIdPart = `, currentUniqueId: ${JSON.stringify(currentUniqueId)}`;
34
- return `{...${paramsValue}, credentials: ${credentialsValue}}, {logger: __bubbleFlowSelf.logger, variableId: ${variableId}${depGraphPart}${currentIdPart}}`;
92
+ const currentIdPart = `, currentUniqueId: ${currentUniqueIdExpr}`;
93
+ const invocationKeyPart = ', invocationCallSiteKey: __bubbleFlowSelf?.__getInvocationCallSiteKey?.()';
94
+ return `{...${paramsValue}, credentials: ${credentialsValue}}, {logger: __bubbleFlowSelf.logger, variableId: ${variableIdExpr}${depGraphPart}${currentIdPart}${invocationKeyPart}}`;
35
95
  }
36
96
  return `{...${paramsValue}, credentials: ${credentialsValue}}`;
37
97
  }
38
98
  }
39
- const paramEntries = parameters.map((param) => {
99
+ // Build parameter entries: regular properties first, then spreads
100
+ const regularEntries = regularParams.map((param) => {
40
101
  const value = formatParameterValue(param.value, param.type);
41
102
  return `${param.name}: ${value}`;
42
103
  });
43
- const paramsString = `{\n ${paramEntries.join(',\n ')}\n }`;
104
+ const spreadEntries = spreadParams.map((param) => {
105
+ const value = formatParameterValue(param.value, param.type);
106
+ return `...${value}`;
107
+ });
108
+ // Combine all entries: regular properties, spreads, then credentials
109
+ const allEntries = [...regularEntries, ...spreadEntries];
110
+ if (credentialsParam) {
111
+ const credentialsValue = formatParameterValue(credentialsParam.value, credentialsParam.type);
112
+ allEntries.push(`credentials: ${credentialsValue}`);
113
+ }
114
+ const paramsString = `{\n ${allEntries.join(',\n ')}\n }`;
44
115
  // Only add the logger configuration if explicitly requested
45
116
  if (includeLoggerConfig) {
46
- const depGraphPart = dependencyGraphLiteral && dependencyGraphLiteral.length > 0
47
- ? `, dependencyGraph: ${dependencyGraphLiteral}`
117
+ const variableIdExpr = typeof variableId === 'number'
118
+ ? `(__bubbleFlowSelf?.__computeInvocationVariableId?.(${variableId}) ?? ${variableId})`
119
+ : 'undefined';
120
+ const depGraphPart = dependencyGraphExpr !== undefined
121
+ ? `, dependencyGraph: ${dependencyGraphExpr}`
48
122
  : '';
49
- const currentIdPart = `, currentUniqueId: ${JSON.stringify(currentUniqueId)}`;
50
- return `${paramsString}, {logger: __bubbleFlowSelf.logger, variableId: ${variableId}${depGraphPart}${currentIdPart}}`;
123
+ const currentIdPart = `, currentUniqueId: ${currentUniqueIdExpr}`;
124
+ const invocationKeyPart = ', invocationCallSiteKey: __bubbleFlowSelf?.__getInvocationCallSiteKey?.()';
125
+ return `${paramsString}, {logger: __bubbleFlowSelf.logger, variableId: ${variableIdExpr}${depGraphPart}${currentIdPart}${invocationKeyPart}}`;
51
126
  }
52
127
  return paramsString;
53
128
  }
@@ -75,6 +150,10 @@ export function formatParameterValue(value, type) {
75
150
  // If caller provided a source literal string, keep it as code
76
151
  if (typeof value === 'string') {
77
152
  const trimmed = value.trim();
153
+ // Preserve source code if it contains function literals
154
+ if (containsFunctionLiteral(trimmed)) {
155
+ return value;
156
+ }
78
157
  if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
79
158
  trimmed.startsWith('new ')) {
80
159
  return value;
@@ -84,6 +163,10 @@ export function formatParameterValue(value, type) {
84
163
  case 'array':
85
164
  if (typeof value === 'string') {
86
165
  const trimmed = value.trim();
166
+ // Preserve source code if it contains function literals
167
+ if (containsFunctionLiteral(trimmed)) {
168
+ return value;
169
+ }
87
170
  if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
88
171
  return value;
89
172
  }
@@ -250,29 +333,73 @@ function stripCommentsOutsideStrings(input) {
250
333
  }
251
334
  return result;
252
335
  }
336
+ /**
337
+ * Condense a parameters string to a single line.
338
+ * Used when parameters don't contain function literals.
339
+ */
340
+ export function condenseToSingleLine(input) {
341
+ return input
342
+ .replace(/\s*\n\s*/g, ' ')
343
+ .replace(/\s{2,}/g, ' ')
344
+ .replace(/\{\s+/g, '{ ')
345
+ .replace(/\s+\}/g, ' }')
346
+ .replace(/\s*,\s*/g, ', ')
347
+ .trim();
348
+ }
349
+ /**
350
+ * Replace lines in an array, handling both single-line and multi-line replacements.
351
+ * Returns the number of lines that were effectively added or removed.
352
+ */
353
+ function replaceLines(lines, startIndex, deleteCount, replacement) {
354
+ const replacementLines = replacement.split('\n');
355
+ const isMultiLine = replacementLines.length > 1;
356
+ if (isMultiLine) {
357
+ // Multi-line replacement: replace first line, delete old lines, insert remaining
358
+ lines[startIndex] = replacementLines[0];
359
+ if (deleteCount > 0) {
360
+ lines.splice(startIndex + 1, deleteCount);
361
+ }
362
+ if (replacementLines.length > 1) {
363
+ lines.splice(startIndex + 1, 0, ...replacementLines.slice(1));
364
+ }
365
+ return replacementLines.length - 1 - deleteCount;
366
+ }
367
+ else {
368
+ // Single-line replacement
369
+ lines[startIndex] = replacement;
370
+ if (deleteCount > 0) {
371
+ lines.splice(startIndex + 1, deleteCount);
372
+ }
373
+ return -deleteCount;
374
+ }
375
+ }
253
376
  /**
254
377
  * Replace a bubble instantiation with updated parameters
255
378
  *
256
379
  * This function:
257
- * 1. Replaces the bubble instantiation line with a single-line version
258
- * 2. Deletes all lines that were part of the original multi-line parameters
259
- * 3. Uses bubble.location.endLine to know exactly where to stop deleting
380
+ * 1. Replaces the bubble instantiation line with updated parameters
381
+ * 2. Preserves multi-line structure when function literals are present
382
+ * 3. Condenses to single-line otherwise
383
+ * 4. Uses bubble.location.endLine to know exactly where to stop deleting
260
384
  */
261
385
  export function replaceBubbleInstantiation(lines, bubble) {
386
+ if (bubble.invocationCallSiteKey) {
387
+ // Invocation-specific clones are logical constructs and shouldn't rewrite source
388
+ return;
389
+ }
262
390
  const { location, className, parameters } = bubble;
263
- // Build the new single-line instantiation
391
+ // Build the parameters object string
264
392
  const dependencyGraphLiteral = JSON.stringify(bubble.dependencyGraph || { name: bubble.bubbleName, dependencies: [] }).replace(/</g, '\u003c');
265
- let parametersObject = buildParametersObject(parameters, bubble.variableId, true, dependencyGraphLiteral, String(bubble.variableId));
266
- // Remove JS/TS comments that would otherwise break single-line formatting
393
+ const currentUniqueIdValue = bubble.dependencyGraph?.uniqueId ?? String(bubble.variableId);
394
+ let parametersObject = buildParametersObject(parameters, bubble.variableId, true, dependencyGraphLiteral, currentUniqueIdValue);
395
+ // Remove JS/TS comments that would otherwise break formatting
267
396
  parametersObject = stripCommentsOutsideStrings(parametersObject);
268
- // Condense to single line
269
- parametersObject = parametersObject
270
- .replace(/\s*\n\s*/g, ' ')
271
- .replace(/\s{2,}/g, ' ')
272
- .replace(/\{\s+/g, '{ ')
273
- .replace(/\s+\}/g, ' }')
274
- .replace(/\s*,\s*/g, ', ')
275
- .trim();
397
+ // Check if parameters contain function literals before condensing
398
+ // Function literals cannot be safely condensed to single-line
399
+ const hasFunctions = containsFunctionLiteral(parametersObject);
400
+ if (!hasFunctions) {
401
+ parametersObject = condenseToSingleLine(parametersObject);
402
+ }
276
403
  const newInstantiationBase = `new ${className}(${parametersObject})`;
277
404
  // Find the line with the bubble instantiation
278
405
  for (let i = location.startLine - 1; i < lines.length; i++) {
@@ -286,12 +413,8 @@ export function replaceBubbleInstantiation(lines, bubble) {
286
413
  const actionCall = bubble.hasActionCall ? '.action()' : '';
287
414
  const newExpression = `${hadAwait ? 'await ' : ''}${newInstantiationBase}${actionCall}`;
288
415
  const replacement = `${indentation}${declaration} ${variableName} = ${newExpression}`;
289
- lines[i] = replacement;
290
- // Delete all lines that were part of the old multi-line parameters
291
416
  const linesToDelete = location.endLine - (i + 1);
292
- if (linesToDelete > 0) {
293
- lines.splice(i + 1, linesToDelete);
294
- }
417
+ replaceLines(lines, i, linesToDelete, replacement);
295
418
  }
296
419
  // Pattern 2: Anonymous bubble (await new Bubble(...).action())
297
420
  else if (bubble.variableName.startsWith('_anonymous_')) {
@@ -301,12 +424,8 @@ export function replaceBubbleInstantiation(lines, bubble) {
301
424
  const newExpression = `${hadAwait ? 'await ' : ''}${newInstantiationBase}${actionCall}`;
302
425
  const beforeClean = beforePattern.replace(/\bawait\s*$/, '');
303
426
  const replacement = `${beforeClean}${newExpression}`;
304
- lines[i] = replacement;
305
- // Delete all lines that were part of the old multi-line parameters
306
427
  const linesToDelete = location.endLine - (i + 1);
307
- if (linesToDelete > 0) {
308
- lines.splice(i + 1, linesToDelete);
309
- }
428
+ replaceLines(lines, i, linesToDelete, replacement);
310
429
  }
311
430
  break;
312
431
  }