@holoscript/std 7.0.0 → 7.0.1

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 (132) hide show
  1. package/README.md +8 -0
  2. package/dist/chunk-DGUM43GV.js +11 -0
  3. package/dist/{chunk-PFX2JM4X.js → chunk-FAMOLKH3.js} +2 -1
  4. package/dist/{chunk-PFX2JM4X.js.map → chunk-FAMOLKH3.js.map} +1 -1
  5. package/dist/{chunk-DZHAVOCH.js → chunk-SOKH6FJ7.js} +31 -6
  6. package/dist/chunk-SOKH6FJ7.js.map +1 -0
  7. package/dist/collections.cjs +1 -0
  8. package/dist/collections.cjs.map +1 -1
  9. package/dist/collections.js +2 -2
  10. package/dist/fs/fs.d.ts.map +1 -1
  11. package/dist/fs/index.cjs +221 -8510
  12. package/dist/fs/index.cjs.map +1 -1
  13. package/dist/fs/index.js +832 -118
  14. package/dist/fs/index.js.map +1 -1
  15. package/dist/fs/sandbox.d.ts +49 -0
  16. package/dist/fs/sandbox.d.ts.map +1 -0
  17. package/dist/index.cjs +3494 -12672
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.ts +0 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +17 -7
  22. package/dist/index.js.map +1 -1
  23. package/dist/math.cjs +18 -3
  24. package/dist/math.cjs.map +1 -1
  25. package/dist/math.js +2 -2
  26. package/dist/string.js +1 -1
  27. package/dist/time.js +1 -1
  28. package/dist/traits/EconomicPrimitives.js +1 -1
  29. package/dist/traits/EconomicTraits.js +1 -1
  30. package/dist/types.d.ts.map +1 -1
  31. package/package.json +19 -9
  32. package/dist/__tests__/EconomicPrimitives.test.d.ts +0 -2
  33. package/dist/__tests__/EconomicPrimitives.test.d.ts.map +0 -1
  34. package/dist/__tests__/EconomicTraits.test.d.ts +0 -2
  35. package/dist/__tests__/EconomicTraits.test.d.ts.map +0 -1
  36. package/dist/__tests__/SimulationLabPrimitives.test.d.ts +0 -2
  37. package/dist/__tests__/SimulationLabPrimitives.test.d.ts.map +0 -1
  38. package/dist/__tests__/collections.test.d.ts +0 -2
  39. package/dist/__tests__/collections.test.d.ts.map +0 -1
  40. package/dist/__tests__/events.test.d.ts +0 -2
  41. package/dist/__tests__/events.test.d.ts.map +0 -1
  42. package/dist/__tests__/index.test.d.ts +0 -2
  43. package/dist/__tests__/index.test.d.ts.map +0 -1
  44. package/dist/__tests__/materials.test.d.ts +0 -2
  45. package/dist/__tests__/materials.test.d.ts.map +0 -1
  46. package/dist/__tests__/math.test.d.ts +0 -2
  47. package/dist/__tests__/math.test.d.ts.map +0 -1
  48. package/dist/__tests__/physics.test.d.ts +0 -2
  49. package/dist/__tests__/physics.test.d.ts.map +0 -1
  50. package/dist/__tests__/spatial.test.d.ts +0 -2
  51. package/dist/__tests__/spatial.test.d.ts.map +0 -1
  52. package/dist/__tests__/standard-library-fundamentals.test.d.ts +0 -2
  53. package/dist/__tests__/standard-library-fundamentals.test.d.ts.map +0 -1
  54. package/dist/__tests__/string-and-time-utilities.test.d.ts +0 -2
  55. package/dist/__tests__/string-and-time-utilities.test.d.ts.map +0 -1
  56. package/dist/__tests__/string-decoupled.test.d.ts +0 -2
  57. package/dist/__tests__/string-decoupled.test.d.ts.map +0 -1
  58. package/dist/__tests__/string.test.d.ts +0 -2
  59. package/dist/__tests__/string.test.d.ts.map +0 -1
  60. package/dist/__tests__/time.test.d.ts +0 -2
  61. package/dist/__tests__/time.test.d.ts.map +0 -1
  62. package/dist/chunk-DZHAVOCH.js.map +0 -1
  63. package/dist/chunk-PR4QN5HX.js +0 -43
  64. package/dist/chunk-XBA4ARST.js +0 -9313
  65. package/dist/chunk-XBA4ARST.js.map +0 -1
  66. package/dist/fs/__tests__/file-watch-system.test.d.ts +0 -2
  67. package/dist/fs/__tests__/file-watch-system.test.d.ts.map +0 -1
  68. package/dist/fs/__tests__/path-utilities.test.d.ts +0 -2
  69. package/dist/fs/__tests__/path-utilities.test.d.ts.map +0 -1
  70. package/dist/fs/__tests__/path.test.d.ts +0 -2
  71. package/dist/fs/__tests__/path.test.d.ts.map +0 -1
  72. package/dist/string.basic.test.d.ts +0 -5
  73. package/dist/string.basic.test.d.ts.map +0 -1
  74. package/dist/string.test.d.ts +0 -5
  75. package/dist/string.test.d.ts.map +0 -1
  76. package/dist/traits/__tests__/ARTraits.test.d.ts +0 -2
  77. package/dist/traits/__tests__/ARTraits.test.d.ts.map +0 -1
  78. package/dist/traits/__tests__/EconomicPrimitives.test.d.ts +0 -2
  79. package/dist/traits/__tests__/EconomicPrimitives.test.d.ts.map +0 -1
  80. package/dist/traits/__tests__/EconomicTraits.test.d.ts +0 -2
  81. package/dist/traits/__tests__/EconomicTraits.test.d.ts.map +0 -1
  82. package/error-utf8.log +0 -6
  83. package/error.log +0 -0
  84. package/src/__tests__/EconomicPrimitives.test.ts +0 -690
  85. package/src/__tests__/EconomicTraits.test.ts +0 -425
  86. package/src/__tests__/SimulationLabPrimitives.test.ts +0 -415
  87. package/src/__tests__/collections.test.ts +0 -182
  88. package/src/__tests__/events.test.ts +0 -135
  89. package/src/__tests__/index.test.ts +0 -14
  90. package/src/__tests__/materials.test.ts +0 -84
  91. package/src/__tests__/math.test.ts +0 -246
  92. package/src/__tests__/physics.test.ts +0 -83
  93. package/src/__tests__/spatial.test.ts +0 -333
  94. package/src/__tests__/standard-library-fundamentals.test.ts +0 -1318
  95. package/src/__tests__/string-and-time-utilities.test.ts +0 -1070
  96. package/src/__tests__/string-decoupled.test.ts +0 -13
  97. package/src/__tests__/string.test.ts +0 -164
  98. package/src/__tests__/time.test.ts +0 -110
  99. package/src/collections.ts +0 -850
  100. package/src/events.ts +0 -88
  101. package/src/fs/__tests__/file-watch-system.test.ts +0 -675
  102. package/src/fs/__tests__/path-utilities.test.ts +0 -375
  103. package/src/fs/__tests__/path.test.ts +0 -287
  104. package/src/fs/fs.ts +0 -692
  105. package/src/fs/index.ts +0 -194
  106. package/src/fs/path.ts +0 -310
  107. package/src/fs/watch.ts +0 -413
  108. package/src/index.ts +0 -476
  109. package/src/materials.ts +0 -109
  110. package/src/math.ts +0 -674
  111. package/src/physics.ts +0 -141
  112. package/src/spatial.ts +0 -320
  113. package/src/string.basic.test.ts +0 -14
  114. package/src/string.test.ts +0 -335
  115. package/src/string.ts +0 -531
  116. package/src/time.ts +0 -621
  117. package/src/traits/ARTraits.ts +0 -103
  118. package/src/traits/EconomicPrimitives.ts +0 -755
  119. package/src/traits/EconomicTraits.ts +0 -552
  120. package/src/traits/IoTTraits.ts +0 -102
  121. package/src/traits/SimulationLabPrimitives.ts +0 -650
  122. package/src/traits/SimulationLabTraits.ts +0 -191
  123. package/src/traits/VRRTraits.ts +0 -326
  124. package/src/traits/__tests__/ARTraits.test.ts +0 -8
  125. package/src/traits/__tests__/EconomicPrimitives.test.ts +0 -14
  126. package/src/traits/__tests__/EconomicTraits.test.ts +0 -16
  127. package/src/types.ts +0 -418
  128. package/std_error.log +0 -5
  129. package/tsconfig.json +0 -18
  130. package/tsup.config.ts +0 -20
  131. package/vitest.config.ts +0 -10
  132. /package/dist/{chunk-PR4QN5HX.js.map → chunk-DGUM43GV.js.map} +0 -0
package/src/string.ts DELETED
@@ -1,531 +0,0 @@
1
- /**
2
- * @holoscript/std - String Module
3
- *
4
- * String manipulation utilities for HoloScript Plus programs.
5
- */
6
-
7
- /**
8
- * Check if a string is empty or only whitespace
9
- */
10
- export function isBlank(s: string): boolean {
11
- return s.trim().length === 0;
12
- }
13
-
14
- /**
15
- * Check if a string is not empty and not only whitespace
16
- */
17
- export function isNotBlank(s: string): boolean {
18
- return s.trim().length > 0;
19
- }
20
-
21
- /**
22
- * Capitalize the first letter of a string
23
- */
24
- export function capitalize(s: string): string {
25
- if (s.length === 0) return s;
26
- return s[0].toUpperCase() + s.slice(1);
27
- }
28
-
29
- /**
30
- * Capitalize the first letter of each word
31
- */
32
- export function titleCase(s: string): string {
33
- return s.split(/\s+/).map(capitalize).join(' ');
34
- }
35
-
36
- /**
37
- * Convert to camelCase
38
- */
39
- export function camelCase(s: string): string {
40
- return s
41
- .replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))
42
- .replace(/^[A-Z]/, (c) => c.toLowerCase());
43
- }
44
-
45
- /**
46
- * Convert to PascalCase
47
- */
48
- export function pascalCase(s: string): string {
49
- const camel = camelCase(s);
50
- return camel.length > 0 ? camel[0].toUpperCase() + camel.slice(1) : camel;
51
- }
52
-
53
- /**
54
- * Convert to snake_case
55
- */
56
- export function snakeCase(s: string): string {
57
- return s
58
- .replace(/([a-z])([A-Z])/g, '$1_$2')
59
- .replace(/[-\s]+/g, '_')
60
- .toLowerCase();
61
- }
62
-
63
- /**
64
- * Convert to kebab-case
65
- */
66
- export function kebabCase(s: string): string {
67
- return s
68
- .replace(/([a-z])([A-Z])/g, '$1-$2')
69
- .replace(/[_\s]+/g, '-')
70
- .toLowerCase();
71
- }
72
-
73
- /**
74
- * Convert to SCREAMING_SNAKE_CASE
75
- */
76
- export function constantCase(s: string): string {
77
- return snakeCase(s).toUpperCase();
78
- }
79
-
80
- /**
81
- * Pad a string on the left to reach target length
82
- */
83
- export function padLeft(s: string, length: number, char = ' '): string {
84
- return s.padStart(length, char);
85
- }
86
-
87
- /**
88
- * Pad a string on the right to reach target length
89
- */
90
- export function padRight(s: string, length: number, char = ' '): string {
91
- return s.padEnd(length, char);
92
- }
93
-
94
- /**
95
- * Pad a string on both sides to center it
96
- */
97
- export function center(s: string, length: number, char = ' '): string {
98
- if (s.length >= length) return s;
99
- const totalPadding = length - s.length;
100
- const leftPadding = Math.floor(totalPadding / 2);
101
- const rightPadding = totalPadding - leftPadding;
102
- return char.repeat(leftPadding) + s + char.repeat(rightPadding);
103
- }
104
-
105
- /**
106
- * Truncate a string to max length, adding ellipsis if needed
107
- */
108
- export function truncate(s: string, maxLength: number, ellipsis = '...'): string {
109
- if (s.length <= maxLength) return s;
110
- return s.slice(0, maxLength - ellipsis.length) + ellipsis;
111
- }
112
-
113
- /**
114
- * Truncate a string in the middle
115
- */
116
- export function truncateMiddle(s: string, maxLength: number, ellipsis = '...'): string {
117
- if (s.length <= maxLength) return s;
118
- const availableLength = maxLength - ellipsis.length;
119
- const leftLength = Math.floor(availableLength / 2);
120
- const rightLength = Math.ceil(availableLength / 2);
121
- return s.slice(0, leftLength) + ellipsis + s.slice(-rightLength);
122
- }
123
-
124
- /**
125
- * Repeat a string n times
126
- */
127
- export function repeat(s: string, count: number): string {
128
- return s.repeat(count);
129
- }
130
-
131
- /**
132
- * Reverse a string
133
- */
134
- export function reverse(s: string): string {
135
- return [...s].reverse().join('');
136
- }
137
-
138
- /**
139
- * Count occurrences of a substring
140
- */
141
- export function count(s: string, substring: string): number {
142
- if (substring.length === 0) return 0;
143
- let count = 0;
144
- let pos = 0;
145
- while ((pos = s.indexOf(substring, pos)) !== -1) {
146
- count++;
147
- pos += 1;
148
- }
149
- return count;
150
- }
151
-
152
- /**
153
- * Check if string contains a substring (case-insensitive)
154
- */
155
- export function containsIgnoreCase(s: string, substring: string): boolean {
156
- return s.toLowerCase().includes(substring.toLowerCase());
157
- }
158
-
159
- /**
160
- * Check if string starts with prefix (case-insensitive)
161
- */
162
- export function startsWithIgnoreCase(s: string, prefix: string): boolean {
163
- return s.toLowerCase().startsWith(prefix.toLowerCase());
164
- }
165
-
166
- /**
167
- * Check if string ends with suffix (case-insensitive)
168
- */
169
- export function endsWithIgnoreCase(s: string, suffix: string): boolean {
170
- return s.toLowerCase().endsWith(suffix.toLowerCase());
171
- }
172
-
173
- /**
174
- * Remove all whitespace from a string
175
- */
176
- export function removeWhitespace(s: string): string {
177
- return s.replace(/\s+/g, '');
178
- }
179
-
180
- /**
181
- * Collapse multiple spaces into single space
182
- */
183
- export function collapseWhitespace(s: string): string {
184
- return s.replace(/\s+/g, ' ').trim();
185
- }
186
-
187
- /**
188
- * Remove a prefix if present
189
- */
190
- export function removePrefix(s: string, prefix: string): string {
191
- return s.startsWith(prefix) ? s.slice(prefix.length) : s;
192
- }
193
-
194
- /**
195
- * Remove a suffix if present
196
- */
197
- export function removeSuffix(s: string, suffix: string): string {
198
- return s.endsWith(suffix) ? s.slice(0, -suffix.length) : s;
199
- }
200
-
201
- /**
202
- * Wrap a string with prefix and suffix
203
- */
204
- export function wrap(s: string, wrapper: string): string;
205
- export function wrap(s: string, prefix: string, suffix: string): string;
206
- export function wrap(s: string, prefixOrWrapper: string, suffix?: string): string {
207
- if (suffix === undefined) {
208
- return prefixOrWrapper + s + prefixOrWrapper;
209
- }
210
- return prefixOrWrapper + s + suffix;
211
- }
212
-
213
- /**
214
- * Unwrap a string by removing matching prefix and suffix
215
- */
216
- export function unwrap(s: string, wrapper: string): string;
217
- export function unwrap(s: string, prefix: string, suffix: string): string;
218
- export function unwrap(s: string, prefixOrWrapper: string, suffix?: string): string {
219
- const prefix = prefixOrWrapper;
220
- const actualSuffix = suffix ?? prefixOrWrapper;
221
- if (s.startsWith(prefix) && s.endsWith(actualSuffix)) {
222
- return s.slice(prefix.length, -actualSuffix.length || undefined);
223
- }
224
- return s;
225
- }
226
-
227
- /**
228
- * Split a string into lines
229
- */
230
- export function lines(s: string): string[] {
231
- return s.split(/\r?\n/);
232
- }
233
-
234
- /**
235
- * Split a string into words
236
- */
237
- export function words(s: string): string[] {
238
- return s.split(/\s+/).filter((w) => w.length > 0);
239
- }
240
-
241
- /**
242
- * Split a string into characters
243
- */
244
- export function chars(s: string): string[] {
245
- return [...s];
246
- }
247
-
248
- /**
249
- * Join strings with a separator
250
- */
251
- export function join(strings: string[], separator = ''): string {
252
- return strings.join(separator);
253
- }
254
-
255
- /**
256
- * Format a template string with values
257
- */
258
- export function format(template: string, values: Record<string, unknown>): string {
259
- return template.replace(/\{(\w+)\}/g, (match, key) => {
260
- return key in values ? String(values[key]) : match;
261
- });
262
- }
263
-
264
- /**
265
- * Format a number as a string with grouping
266
- */
267
- export function formatNumber(n: number, options: Intl.NumberFormatOptions = {}): string {
268
- return n.toLocaleString('en-US', options);
269
- }
270
-
271
- /**
272
- * Format a number with commas
273
- */
274
- export function numberWithCommas(n: number): string {
275
- return n.toLocaleString('en-US');
276
- }
277
-
278
- /**
279
- * Format bytes as human-readable string
280
- */
281
- export function formatBytes(bytes: number, decimals = 2): string {
282
- if (bytes === 0) return '0 B';
283
- const k = 1024;
284
- if (bytes < 0) return `${bytes} B`;
285
- const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
286
- const i = Math.floor(Math.log(bytes) / Math.log(k));
287
- return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
288
- }
289
-
290
- /**
291
- * Format duration in milliseconds as human-readable string
292
- */
293
- export function formatDuration(ms: number): string {
294
- if (ms < 1000) return `${ms}ms`;
295
- if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
296
- if (ms < 3600000) {
297
- return `${(ms / 60000).toFixed(1)}m`;
298
- }
299
- const _hours = Math.floor(ms / 3600000);
300
- const _mins = Math.floor((ms % 3600000) / 60000);
301
- return `${(ms / 3600000).toFixed(1)}h`;
302
- }
303
-
304
- /**
305
- * Escape HTML special characters
306
- */
307
- export function escapeHtml(s: string): string {
308
- const htmlEntities: Record<string, string> = {
309
- '&': '&amp;',
310
- '<': '&lt;',
311
- '>': '&gt;',
312
- '"': '&quot;',
313
- "'": '&#39;',
314
- };
315
- return s.replace(/[&<>"']/g, (c) => htmlEntities[c]);
316
- }
317
-
318
- /**
319
- * Unescape HTML entities
320
- */
321
- export function unescapeHtml(s: string): string {
322
- const htmlEntities: Record<string, string> = {
323
- '&amp;': '&',
324
- '&lt;': '<',
325
- '&gt;': '>',
326
- '&quot;': '"',
327
- '&#39;': "'",
328
- '&#x27;': "'",
329
- '&#x2F;': '/',
330
- };
331
- return s.replace(
332
- /&(?:amp|lt|gt|quot|#39|#x27|#x2F);/g,
333
- (entity) => htmlEntities[entity] || entity
334
- );
335
- }
336
-
337
- /**
338
- * Escape regex special characters
339
- */
340
- export function escapeRegex(s: string): string {
341
- return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
342
- }
343
-
344
- /**
345
- * Generate a slug from a string
346
- */
347
- export function slugify(s: string): string {
348
- return s
349
- .toLowerCase()
350
- .normalize('NFD')
351
- .replace(/[\u0300-\u036f]/g, '') // Remove diacritics
352
- .replace(/[^a-z0-9]+/g, '-')
353
- .replace(/^-+|-+$/g, '');
354
- }
355
-
356
- /**
357
- * Check if string is a valid identifier
358
- */
359
- export function isValidIdentifier(s: string): boolean {
360
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(s);
361
- }
362
-
363
- /**
364
- * Check if string is numeric
365
- */
366
- export function isNumeric(s: string): boolean {
367
- return !isNaN(parseFloat(s)) && isFinite(Number(s));
368
- }
369
-
370
- /**
371
- * Check if string is alphanumeric
372
- */
373
- export function isAlphanumeric(s: string): boolean {
374
- return /^[a-zA-Z0-9]+$/.test(s);
375
- }
376
-
377
- /**
378
- * Check if string is alphabetic
379
- */
380
- export function isAlpha(s: string): boolean {
381
- return /^[a-zA-Z]+$/.test(s);
382
- }
383
-
384
- /**
385
- * Generate a random string
386
- */
387
- export function randomString(
388
- length: number,
389
- charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
390
- ): string {
391
- let result = '';
392
- for (let i = 0; i < length; i++) {
393
- result += charset[Math.floor(Math.random() * charset.length)];
394
- }
395
- return result;
396
- }
397
-
398
- /**
399
- * Generate a UUID v4
400
- */
401
- export function uuid(): string {
402
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
403
- const r = (Math.random() * 16) | 0;
404
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
405
- return v.toString(16);
406
- });
407
- }
408
-
409
- /**
410
- * Indent each line of a string
411
- */
412
- export function indent(s: string, spaces: number, char = ' '): string {
413
- const prefix = char.repeat(spaces);
414
- return s
415
- .split('\n')
416
- .map((line) => prefix + line)
417
- .join('\n');
418
- }
419
-
420
- /**
421
- * Dedent a string (remove common leading whitespace)
422
- */
423
- export function dedent(s: string): string {
424
- const linesList = lines(s);
425
- const nonEmptyLines = linesList.filter((l) => l.trim().length > 0);
426
- if (nonEmptyLines.length === 0) return s;
427
-
428
- const minIndent = Math.min(
429
- ...nonEmptyLines.map((line) => {
430
- const match = line.match(/^(\s*)/);
431
- return match ? match[1].length : 0;
432
- })
433
- );
434
-
435
- return linesList.map((line) => line.slice(minIndent)).join('\n');
436
- }
437
-
438
- /**
439
- * Word wrap text to a maximum width
440
- */
441
- export function wordWrap(s: string, maxWidth: number): string {
442
- const wordsList = words(s);
443
- const linesList: string[] = [];
444
- let currentLine = '';
445
-
446
- for (const word of wordsList) {
447
- if (currentLine.length === 0) {
448
- currentLine = word;
449
- } else if (currentLine.length + 1 + word.length <= maxWidth) {
450
- currentLine += ' ' + word;
451
- } else {
452
- linesList.push(currentLine);
453
- currentLine = word;
454
- }
455
- }
456
-
457
- if (currentLine.length > 0) {
458
- linesList.push(currentLine);
459
- }
460
-
461
- return linesList.join('\n');
462
- }
463
-
464
- /**
465
- * Levenshtein distance between two strings
466
- */
467
- export function levenshtein(a: string, b: string): number {
468
- if (a.length === 0) return b.length;
469
- if (b.length === 0) return a.length;
470
-
471
- const matrix: number[][] = [];
472
-
473
- for (let i = 0; i <= b.length; i++) {
474
- matrix[i] = [i];
475
- }
476
- for (let j = 0; j <= a.length; j++) {
477
- matrix[0][j] = j;
478
- }
479
-
480
- for (let i = 1; i <= b.length; i++) {
481
- for (let j = 1; j <= a.length; j++) {
482
- if (b[i - 1] === a[j - 1]) {
483
- matrix[i][j] = matrix[i - 1][j - 1];
484
- } else {
485
- matrix[i][j] = Math.min(
486
- matrix[i - 1][j - 1] + 1, // substitution
487
- matrix[i][j - 1] + 1, // insertion
488
- matrix[i - 1][j] + 1 // deletion
489
- );
490
- }
491
- }
492
- }
493
-
494
- return matrix[b.length][a.length];
495
- }
496
-
497
- function lcsLength(a: string, b: string): number {
498
- const n = b.length;
499
- const dp: number[] = new Array(n + 1).fill(0);
500
- for (let i = 0; i < a.length; i++) {
501
- let prev = 0;
502
- for (let j = 0; j < n; j++) {
503
- const tmp = dp[j + 1];
504
- dp[j + 1] = a[i] === b[j] ? prev + 1 : Math.max(dp[j + 1], dp[j]);
505
- prev = tmp;
506
- }
507
- }
508
- return dp[n];
509
- }
510
-
511
- /**
512
- * Check similarity between two strings (0-1) using Sorensen-Dice over LCS
513
- */
514
- export function similarity(a: string, b: string): number {
515
- const total = a.length + b.length;
516
- if (total === 0) return 1;
517
- return (2 * lcsLength(a, b)) / total;
518
- }
519
-
520
- /**
521
- * Extract HoloScript trait annotations (@name) and JSX component names from code
522
- */
523
- export function extractTraits(code: string): string[] {
524
- const traits = new Set<string>();
525
- let m: RegExpExecArray | null;
526
- const atRe = /@([a-zA-Z_]\w*)/g;
527
- while ((m = atRe.exec(code)) !== null) traits.add(`@${m[1]}`);
528
- const tagRe = /<([A-Z][a-zA-Z0-9]*)\b/g;
529
- while ((m = tagRe.exec(code)) !== null) traits.add(m[1]);
530
- return [...traits];
531
- }