@probelabs/visor 0.1.90 → 0.1.92

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 (131) hide show
  1. package/README.md +68 -2
  2. package/action.yml +1 -1
  3. package/defaults/.visor.yaml +120 -154
  4. package/dist/13.index.js +82 -0
  5. package/dist/159.index.js +38 -0
  6. package/dist/168.index.js +82 -0
  7. package/dist/201.index.js +82 -0
  8. package/dist/262.index.js +48 -0
  9. package/dist/272.index.js +82 -0
  10. package/dist/273.index.js +48 -0
  11. package/dist/320.index.js +38 -0
  12. package/dist/34.index.js +81 -0
  13. package/dist/421.index.js +48 -0
  14. package/dist/437.index.js +82 -0
  15. package/dist/441.index.js +81 -0
  16. package/dist/450.index.js +82 -0
  17. package/dist/54.index.js +81 -0
  18. package/dist/544.index.js +38 -0
  19. package/dist/558.index.js +82 -0
  20. package/dist/715.index.js +38 -0
  21. package/dist/737.index.js +82 -0
  22. package/dist/834.index.js +48 -0
  23. package/dist/85.index.js +82 -0
  24. package/dist/861.index.js +48 -0
  25. package/dist/878.index.js +81 -0
  26. package/dist/940.index.js +38 -0
  27. package/dist/989.index.js +81 -0
  28. package/dist/996.index.js +82 -0
  29. package/dist/ai-review-service.d.ts +11 -1
  30. package/dist/ai-review-service.d.ts.map +1 -1
  31. package/dist/check-execution-engine.d.ts +9 -2
  32. package/dist/check-execution-engine.d.ts.map +1 -1
  33. package/dist/cli-main.d.ts.map +1 -1
  34. package/dist/cli.d.ts.map +1 -1
  35. package/dist/config.d.ts.map +1 -1
  36. package/dist/defaults/.visor.yaml +120 -154
  37. package/dist/failure-condition-evaluator.d.ts +3 -2
  38. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  39. package/dist/generated/config-schema.d.ts +89 -2
  40. package/dist/generated/config-schema.d.ts.map +1 -1
  41. package/dist/generated/config-schema.json +109 -1
  42. package/dist/git-repository-analyzer.d.ts +17 -1
  43. package/dist/git-repository-analyzer.d.ts.map +1 -1
  44. package/dist/github-comments.d.ts.map +1 -1
  45. package/dist/github-reactions.d.ts +36 -0
  46. package/dist/github-reactions.d.ts.map +1 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +172824 -45634
  50. package/dist/liquid-extensions.d.ts +3 -0
  51. package/dist/liquid-extensions.d.ts.map +1 -1
  52. package/dist/memory-store.d.ts +129 -0
  53. package/dist/memory-store.d.ts.map +1 -0
  54. package/dist/output/issue-assistant/schema.json +29 -0
  55. package/dist/output/issue-assistant/template.liquid +1 -0
  56. package/dist/output/overview/schema.json +33 -0
  57. package/dist/output/overview/template.liquid +16 -0
  58. package/dist/output-formatters.d.ts +1 -0
  59. package/dist/output-formatters.d.ts.map +1 -1
  60. package/dist/pr-analyzer.d.ts +2 -0
  61. package/dist/pr-analyzer.d.ts.map +1 -1
  62. package/dist/proto/channelz.proto +564 -0
  63. package/dist/providers/check-provider-registry.d.ts.map +1 -1
  64. package/dist/providers/command-check-provider.d.ts.map +1 -1
  65. package/dist/providers/github-ops-provider.d.ts +18 -0
  66. package/dist/providers/github-ops-provider.d.ts.map +1 -0
  67. package/dist/providers/memory-check-provider.d.ts +56 -0
  68. package/dist/providers/memory-check-provider.d.ts.map +1 -0
  69. package/dist/reviewer.d.ts +2 -0
  70. package/dist/reviewer.d.ts.map +1 -1
  71. package/dist/sdk/check-execution-engine-L73PFZQY.mjs +11 -0
  72. package/dist/sdk/chunk-2U6BIWSY.mjs +748 -0
  73. package/dist/sdk/chunk-2U6BIWSY.mjs.map +1 -0
  74. package/dist/sdk/chunk-KVHVCGY6.mjs +103 -0
  75. package/dist/sdk/chunk-KVHVCGY6.mjs.map +1 -0
  76. package/dist/sdk/{chunk-N2PPFOSF.mjs → chunk-LJHRU3WQ.mjs} +2577 -250
  77. package/dist/sdk/chunk-LJHRU3WQ.mjs.map +1 -0
  78. package/dist/sdk/chunk-TWJKAYT6.mjs +1124 -0
  79. package/dist/sdk/chunk-TWJKAYT6.mjs.map +1 -0
  80. package/dist/sdk/liquid-extensions-AFKRYROF.mjs +14 -0
  81. package/dist/sdk/mermaid-telemetry-LZGDD35I.mjs +61 -0
  82. package/dist/sdk/mermaid-telemetry-LZGDD35I.mjs.map +1 -0
  83. package/dist/sdk/sdk.d.mts +48 -2
  84. package/dist/sdk/sdk.d.ts +48 -2
  85. package/dist/sdk/sdk.js +5352 -767
  86. package/dist/sdk/sdk.js.map +1 -1
  87. package/dist/sdk/sdk.mjs +112 -7
  88. package/dist/sdk/sdk.mjs.map +1 -1
  89. package/dist/sdk/tracer-init-O7RLXMJ3.mjs +10 -0
  90. package/dist/sdk/tracer-init-O7RLXMJ3.mjs.map +1 -0
  91. package/dist/session-registry.d.ts +19 -5
  92. package/dist/session-registry.d.ts.map +1 -1
  93. package/dist/telemetry/fallback-ndjson.d.ts +7 -0
  94. package/dist/telemetry/fallback-ndjson.d.ts.map +1 -0
  95. package/dist/telemetry/file-span-exporter.d.ts +17 -0
  96. package/dist/telemetry/file-span-exporter.d.ts.map +1 -0
  97. package/dist/telemetry/metrics.d.ts +13 -0
  98. package/dist/telemetry/metrics.d.ts.map +1 -0
  99. package/dist/telemetry/opentelemetry.d.ts +26 -0
  100. package/dist/telemetry/opentelemetry.d.ts.map +1 -0
  101. package/dist/telemetry/trace-helpers.d.ts +9 -0
  102. package/dist/telemetry/trace-helpers.d.ts.map +1 -0
  103. package/dist/telemetry/trace-report-exporter.d.ts +17 -0
  104. package/dist/telemetry/trace-report-exporter.d.ts.map +1 -0
  105. package/dist/traces/run-2025-10-15T07-21-47-696Z.ndjson +17 -0
  106. package/dist/traces/run-2025-10-15T07-21-58-106Z.ndjson +17 -0
  107. package/dist/traces/run-2025-10-15T07-21-58-693Z.ndjson +17 -0
  108. package/dist/traces/run-2025-10-15T07-21-59-167Z.ndjson +17 -0
  109. package/dist/traces/run-2025-10-15T07-21-59-629Z.ndjson +8 -0
  110. package/dist/types/cli.d.ts +4 -0
  111. package/dist/types/cli.d.ts.map +1 -1
  112. package/dist/types/config.d.ts +56 -2
  113. package/dist/types/config.d.ts.map +1 -1
  114. package/dist/utils/author-permissions.d.ts +74 -0
  115. package/dist/utils/author-permissions.d.ts.map +1 -0
  116. package/dist/utils/head-sha.d.ts +8 -0
  117. package/dist/utils/head-sha.d.ts.map +1 -0
  118. package/dist/utils/mermaid-telemetry.d.ts +3 -0
  119. package/dist/utils/mermaid-telemetry.d.ts.map +1 -0
  120. package/dist/utils/tracer-init.d.ts +12 -0
  121. package/dist/utils/tracer-init.d.ts.map +1 -0
  122. package/dist/utils/ui-helpers.d.ts +3 -0
  123. package/dist/utils/ui-helpers.d.ts.map +1 -0
  124. package/package.json +2 -2
  125. package/dist/sdk/check-execution-engine-Z2USLMN5.mjs +0 -9
  126. package/dist/sdk/chunk-FIL2OGF6.mjs +0 -68
  127. package/dist/sdk/chunk-FIL2OGF6.mjs.map +0 -1
  128. package/dist/sdk/chunk-N2PPFOSF.mjs.map +0 -1
  129. package/dist/sdk/liquid-extensions-KDECAJTV.mjs +0 -12
  130. /package/dist/sdk/{check-execution-engine-Z2USLMN5.mjs.map → check-execution-engine-L73PFZQY.mjs.map} +0 -0
  131. /package/dist/sdk/{liquid-extensions-KDECAJTV.mjs.map → liquid-extensions-AFKRYROF.mjs.map} +0 -0
@@ -0,0 +1,748 @@
1
+ import {
2
+ __esm,
3
+ __export
4
+ } from "./chunk-WMJKH4XE.mjs";
5
+
6
+ // src/logger.ts
7
+ var logger_exports = {};
8
+ __export(logger_exports, {
9
+ configureLoggerFromCli: () => configureLoggerFromCli,
10
+ logger: () => logger
11
+ });
12
+ function levelToNumber(level) {
13
+ switch (level) {
14
+ case "silent":
15
+ return 0;
16
+ case "error":
17
+ return 10;
18
+ case "warn":
19
+ return 20;
20
+ case "info":
21
+ return 30;
22
+ case "verbose":
23
+ return 40;
24
+ case "debug":
25
+ return 50;
26
+ }
27
+ }
28
+ function configureLoggerFromCli(options) {
29
+ logger.configure({
30
+ outputFormat: options.output,
31
+ debug: options.debug,
32
+ verbose: options.verbose,
33
+ quiet: options.quiet
34
+ });
35
+ try {
36
+ if (options.output) process.env.VISOR_OUTPUT_FORMAT = String(options.output);
37
+ if (typeof options.debug === "boolean") {
38
+ process.env.VISOR_DEBUG = options.debug ? "true" : "false";
39
+ }
40
+ } catch {
41
+ }
42
+ }
43
+ var Logger, logger;
44
+ var init_logger = __esm({
45
+ "src/logger.ts"() {
46
+ "use strict";
47
+ Logger = class {
48
+ level = "info";
49
+ isJsonLike = false;
50
+ isTTY = typeof process !== "undefined" ? !!process.stderr.isTTY : false;
51
+ configure(opts = {}) {
52
+ let lvl = "info";
53
+ if (opts.debug || process.env.VISOR_DEBUG === "true") {
54
+ lvl = "debug";
55
+ } else if (opts.verbose || process.env.VISOR_LOG_LEVEL === "verbose") {
56
+ lvl = "verbose";
57
+ } else if (opts.quiet || process.env.VISOR_LOG_LEVEL === "quiet") {
58
+ lvl = "warn";
59
+ } else if (opts.level) {
60
+ lvl = opts.level;
61
+ } else if (process.env.VISOR_LOG_LEVEL) {
62
+ const envLvl = process.env.VISOR_LOG_LEVEL;
63
+ if (["silent", "error", "warn", "info", "verbose", "debug"].includes(envLvl)) {
64
+ lvl = envLvl;
65
+ }
66
+ }
67
+ this.level = lvl;
68
+ const output = opts.outputFormat || process.env.VISOR_OUTPUT_FORMAT || "table";
69
+ this.isJsonLike = output === "json" || output === "sarif";
70
+ }
71
+ shouldLog(level) {
72
+ const desired = levelToNumber(level);
73
+ const current = levelToNumber(this.level);
74
+ if (desired > current) return false;
75
+ if (this.isJsonLike && desired < levelToNumber("error") && this.level !== "debug" && this.level !== "verbose") {
76
+ return false;
77
+ }
78
+ return true;
79
+ }
80
+ write(msg) {
81
+ try {
82
+ process.stderr.write(msg + "\n");
83
+ } catch {
84
+ }
85
+ }
86
+ info(msg) {
87
+ if (this.shouldLog("info")) this.write(msg);
88
+ }
89
+ warn(msg) {
90
+ if (this.shouldLog("warn")) this.write(msg);
91
+ }
92
+ error(msg) {
93
+ if (this.shouldLog("error")) this.write(msg);
94
+ }
95
+ verbose(msg) {
96
+ if (this.shouldLog("verbose")) this.write(msg);
97
+ }
98
+ debug(msg) {
99
+ if (this.shouldLog("debug")) this.write(msg);
100
+ }
101
+ step(msg) {
102
+ if (this.shouldLog("info")) this.write(`\u25B6 ${msg}`);
103
+ }
104
+ success(msg) {
105
+ if (this.shouldLog("info")) this.write(`\u2714 ${msg}`);
106
+ }
107
+ };
108
+ logger = new Logger();
109
+ }
110
+ });
111
+
112
+ // src/liquid-extensions.ts
113
+ import { Liquid, Tag, Value } from "liquidjs";
114
+ import { AsyncLocalStorage } from "async_hooks";
115
+ import fs2 from "fs/promises";
116
+ import path2 from "path";
117
+
118
+ // src/utils/author-permissions.ts
119
+ var PERMISSION_HIERARCHY = [
120
+ "OWNER",
121
+ "MEMBER",
122
+ "COLLABORATOR",
123
+ "CONTRIBUTOR",
124
+ "FIRST_TIME_CONTRIBUTOR",
125
+ "FIRST_TIMER",
126
+ "NONE"
127
+ ];
128
+ function getPermissionLevel(association) {
129
+ if (!association) return PERMISSION_HIERARCHY.length;
130
+ const index = PERMISSION_HIERARCHY.indexOf(association.toUpperCase());
131
+ return index === -1 ? PERMISSION_HIERARCHY.length : index;
132
+ }
133
+ function hasMinPermission(authorAssociation, minPermission, isLocalMode = false) {
134
+ if (isLocalMode) {
135
+ return true;
136
+ }
137
+ const authorLevel = getPermissionLevel(authorAssociation);
138
+ const minLevel = getPermissionLevel(minPermission);
139
+ return authorLevel <= minLevel;
140
+ }
141
+ function isOwner(authorAssociation, isLocalMode = false) {
142
+ if (isLocalMode) return true;
143
+ return authorAssociation?.toUpperCase() === "OWNER";
144
+ }
145
+ function isMember(authorAssociation, isLocalMode = false) {
146
+ if (isLocalMode) return true;
147
+ return hasMinPermission(authorAssociation, "MEMBER", isLocalMode);
148
+ }
149
+ function isCollaborator(authorAssociation, isLocalMode = false) {
150
+ if (isLocalMode) return true;
151
+ return hasMinPermission(authorAssociation, "COLLABORATOR", isLocalMode);
152
+ }
153
+ function isContributor(authorAssociation, isLocalMode = false) {
154
+ if (isLocalMode) return true;
155
+ return hasMinPermission(authorAssociation, "CONTRIBUTOR", isLocalMode);
156
+ }
157
+ function isFirstTimer(authorAssociation, isLocalMode = false) {
158
+ if (isLocalMode) return false;
159
+ const assoc = authorAssociation?.toUpperCase();
160
+ return assoc === "FIRST_TIME_CONTRIBUTOR" || assoc === "FIRST_TIMER";
161
+ }
162
+ function createPermissionHelpers(authorAssociation, isLocalMode = false) {
163
+ return {
164
+ hasMinPermission: (minPermission) => hasMinPermission(authorAssociation, minPermission, isLocalMode),
165
+ isOwner: () => isOwner(authorAssociation, isLocalMode),
166
+ isMember: () => isMember(authorAssociation, isLocalMode),
167
+ isCollaborator: () => isCollaborator(authorAssociation, isLocalMode),
168
+ isContributor: () => isContributor(authorAssociation, isLocalMode),
169
+ isFirstTimer: () => isFirstTimer(authorAssociation, isLocalMode)
170
+ };
171
+ }
172
+ function detectLocalMode() {
173
+ return !process.env.GITHUB_ACTIONS;
174
+ }
175
+ function resolveAssociationFromEvent(eventContext, fallback) {
176
+ try {
177
+ const ec = eventContext || {};
178
+ return ec?.comment?.author_association || ec?.issue?.author_association || ec?.pull_request?.author_association || fallback;
179
+ } catch {
180
+ return fallback;
181
+ }
182
+ }
183
+
184
+ // src/memory-store.ts
185
+ init_logger();
186
+ import fs from "fs/promises";
187
+ import path from "path";
188
+ var MemoryStore = class _MemoryStore {
189
+ static instance;
190
+ data;
191
+ // namespace -> key -> value
192
+ config;
193
+ initialized = false;
194
+ constructor(config) {
195
+ this.data = /* @__PURE__ */ new Map();
196
+ this.config = this.normalizeConfig(config);
197
+ }
198
+ /**
199
+ * Get singleton instance
200
+ */
201
+ static getInstance(config) {
202
+ if (!_MemoryStore.instance) {
203
+ _MemoryStore.instance = new _MemoryStore(config);
204
+ } else if (config && !_MemoryStore.instance.initialized) {
205
+ _MemoryStore.instance.config = _MemoryStore.instance.normalizeConfig(config);
206
+ }
207
+ return _MemoryStore.instance;
208
+ }
209
+ /**
210
+ * Reset singleton instance (for testing)
211
+ */
212
+ static resetInstance() {
213
+ _MemoryStore.instance = void 0;
214
+ }
215
+ /**
216
+ * Initialize memory store (load from file if configured)
217
+ */
218
+ async initialize() {
219
+ if (this.initialized) {
220
+ return;
221
+ }
222
+ if (this.config.storage === "file" && this.config.auto_load && this.config.file) {
223
+ try {
224
+ await this.load();
225
+ logger.debug(`Memory store loaded from ${this.config.file}`);
226
+ } catch (error) {
227
+ if (error.code !== "ENOENT") {
228
+ logger.warn(
229
+ `Failed to load memory store from ${this.config.file}: ${error instanceof Error ? error.message : "Unknown error"}`
230
+ );
231
+ }
232
+ }
233
+ }
234
+ this.initialized = true;
235
+ }
236
+ /**
237
+ * Normalize and apply defaults to config
238
+ */
239
+ normalizeConfig(config) {
240
+ const storage = config?.storage || "memory";
241
+ return {
242
+ storage,
243
+ format: config?.format || "json",
244
+ file: config?.file,
245
+ namespace: config?.namespace || "default",
246
+ auto_load: config?.auto_load !== false,
247
+ auto_save: config?.auto_save !== false
248
+ };
249
+ }
250
+ /**
251
+ * Get the default namespace
252
+ */
253
+ getDefaultNamespace() {
254
+ return this.config.namespace || "default";
255
+ }
256
+ /**
257
+ * Get a value from memory
258
+ */
259
+ get(key, namespace) {
260
+ const ns = namespace || this.getDefaultNamespace();
261
+ const nsData = this.data.get(ns);
262
+ return nsData?.get(key);
263
+ }
264
+ /**
265
+ * Check if a key exists in memory
266
+ */
267
+ has(key, namespace) {
268
+ const ns = namespace || this.getDefaultNamespace();
269
+ const nsData = this.data.get(ns);
270
+ return nsData?.has(key) || false;
271
+ }
272
+ /**
273
+ * Set a value in memory (override existing)
274
+ */
275
+ async set(key, value, namespace) {
276
+ const ns = namespace || this.getDefaultNamespace();
277
+ if (!this.data.has(ns)) {
278
+ this.data.set(ns, /* @__PURE__ */ new Map());
279
+ }
280
+ const nsData = this.data.get(ns);
281
+ nsData.set(key, value);
282
+ if (this.config.storage === "file" && this.config.auto_save) {
283
+ await this.save();
284
+ }
285
+ }
286
+ /**
287
+ * Append a value to an array in memory
288
+ * If key doesn't exist, creates a new array
289
+ * If key exists but is not an array, converts it to an array
290
+ */
291
+ async append(key, value, namespace) {
292
+ const ns = namespace || this.getDefaultNamespace();
293
+ const existing = this.get(key, ns);
294
+ let newValue;
295
+ if (existing === void 0) {
296
+ newValue = [value];
297
+ } else if (Array.isArray(existing)) {
298
+ newValue = [...existing, value];
299
+ } else {
300
+ newValue = [existing, value];
301
+ }
302
+ await this.set(key, newValue, ns);
303
+ }
304
+ /**
305
+ * Increment a numeric value in memory
306
+ * If key doesn't exist, initializes to 0 before incrementing
307
+ * If key exists but is not a number, throws an error
308
+ */
309
+ async increment(key, amount = 1, namespace) {
310
+ const ns = namespace || this.getDefaultNamespace();
311
+ const existing = this.get(key, ns);
312
+ let newValue;
313
+ if (existing === void 0 || existing === null) {
314
+ newValue = amount;
315
+ } else if (typeof existing === "number") {
316
+ newValue = existing + amount;
317
+ } else {
318
+ throw new Error(
319
+ `Cannot increment non-numeric value at key '${key}' (type: ${typeof existing})`
320
+ );
321
+ }
322
+ await this.set(key, newValue, ns);
323
+ return newValue;
324
+ }
325
+ /**
326
+ * Delete a key from memory
327
+ */
328
+ async delete(key, namespace) {
329
+ const ns = namespace || this.getDefaultNamespace();
330
+ const nsData = this.data.get(ns);
331
+ if (!nsData) {
332
+ return false;
333
+ }
334
+ const deleted = nsData.delete(key);
335
+ if (deleted && this.config.storage === "file" && this.config.auto_save) {
336
+ await this.save();
337
+ }
338
+ return deleted;
339
+ }
340
+ /**
341
+ * Clear all keys in a namespace (or all namespaces if none specified)
342
+ */
343
+ async clear(namespace) {
344
+ if (namespace) {
345
+ this.data.delete(namespace);
346
+ } else {
347
+ this.data.clear();
348
+ }
349
+ if (this.config.storage === "file" && this.config.auto_save) {
350
+ await this.save();
351
+ }
352
+ }
353
+ /**
354
+ * List all keys in a namespace
355
+ */
356
+ list(namespace) {
357
+ const ns = namespace || this.getDefaultNamespace();
358
+ const nsData = this.data.get(ns);
359
+ return nsData ? Array.from(nsData.keys()) : [];
360
+ }
361
+ /**
362
+ * List all namespaces
363
+ */
364
+ listNamespaces() {
365
+ return Array.from(this.data.keys());
366
+ }
367
+ /**
368
+ * Get all data in a namespace
369
+ */
370
+ getAll(namespace) {
371
+ const ns = namespace || this.getDefaultNamespace();
372
+ const nsData = this.data.get(ns);
373
+ if (!nsData) {
374
+ return {};
375
+ }
376
+ const result = {};
377
+ for (const [key, value] of nsData.entries()) {
378
+ result[key] = value;
379
+ }
380
+ return result;
381
+ }
382
+ /**
383
+ * Load data from file
384
+ */
385
+ async load() {
386
+ if (!this.config.file) {
387
+ throw new Error("No file path configured for memory store");
388
+ }
389
+ const filePath = path.resolve(process.cwd(), this.config.file);
390
+ const content = await fs.readFile(filePath, "utf-8");
391
+ if (this.config.format === "json") {
392
+ await this.loadFromJson(content);
393
+ } else if (this.config.format === "csv") {
394
+ await this.loadFromCsv(content);
395
+ } else {
396
+ throw new Error(`Unsupported format: ${this.config.format}`);
397
+ }
398
+ }
399
+ /**
400
+ * Save data to file
401
+ */
402
+ async save() {
403
+ if (!this.config.file) {
404
+ throw new Error("No file path configured for memory store");
405
+ }
406
+ const filePath = path.resolve(process.cwd(), this.config.file);
407
+ const dir = path.dirname(filePath);
408
+ await fs.mkdir(dir, { recursive: true });
409
+ let content;
410
+ if (this.config.format === "json") {
411
+ content = this.saveToJson();
412
+ } else if (this.config.format === "csv") {
413
+ content = this.saveToCsv();
414
+ } else {
415
+ throw new Error(`Unsupported format: ${this.config.format}`);
416
+ }
417
+ await fs.writeFile(filePath, content, "utf-8");
418
+ }
419
+ /**
420
+ * Load data from JSON format
421
+ */
422
+ async loadFromJson(content) {
423
+ const data = JSON.parse(content);
424
+ this.data.clear();
425
+ for (const [namespace, nsData] of Object.entries(data)) {
426
+ if (typeof nsData === "object" && nsData !== null && !Array.isArray(nsData)) {
427
+ const nsMap = /* @__PURE__ */ new Map();
428
+ for (const [key, value] of Object.entries(nsData)) {
429
+ nsMap.set(key, value);
430
+ }
431
+ this.data.set(namespace, nsMap);
432
+ }
433
+ }
434
+ }
435
+ /**
436
+ * Save data to JSON format
437
+ */
438
+ saveToJson() {
439
+ const result = {};
440
+ for (const [namespace, nsData] of this.data.entries()) {
441
+ const nsObj = {};
442
+ for (const [key, value] of nsData.entries()) {
443
+ nsObj[key] = value;
444
+ }
445
+ result[namespace] = nsObj;
446
+ }
447
+ return JSON.stringify(result, null, 2);
448
+ }
449
+ /**
450
+ * Load data from CSV format
451
+ * CSV format: namespace,key,value,type
452
+ */
453
+ async loadFromCsv(content) {
454
+ const lines = content.split("\n").filter((line) => line.trim());
455
+ let startIndex = 0;
456
+ if (lines[0]?.startsWith("namespace,")) {
457
+ startIndex = 1;
458
+ }
459
+ this.data.clear();
460
+ const arrays = /* @__PURE__ */ new Map();
461
+ for (let i = startIndex; i < lines.length; i++) {
462
+ const line = lines[i];
463
+ const parts = this.parseCsvLine(line);
464
+ if (parts.length < 3) {
465
+ logger.warn(`Invalid CSV line ${i + 1}: ${line}`);
466
+ continue;
467
+ }
468
+ const [namespace, key, valueStr, typeStr] = parts;
469
+ const value = this.parseCsvValue(valueStr, typeStr);
470
+ if (!this.data.has(namespace)) {
471
+ this.data.set(namespace, /* @__PURE__ */ new Map());
472
+ arrays.set(namespace, /* @__PURE__ */ new Map());
473
+ }
474
+ const nsData = this.data.get(namespace);
475
+ const nsArrays = arrays.get(namespace);
476
+ if (nsData.has(key)) {
477
+ if (!nsArrays.has(key)) {
478
+ const existingValue = nsData.get(key);
479
+ nsArrays.set(key, [existingValue]);
480
+ }
481
+ nsArrays.get(key).push(value);
482
+ nsData.set(key, nsArrays.get(key));
483
+ } else {
484
+ nsData.set(key, value);
485
+ }
486
+ }
487
+ }
488
+ /**
489
+ * Save data to CSV format
490
+ */
491
+ saveToCsv() {
492
+ const lines = ["namespace,key,value,type"];
493
+ for (const [namespace, nsData] of this.data.entries()) {
494
+ for (const [key, value] of nsData.entries()) {
495
+ if (Array.isArray(value)) {
496
+ for (const item of value) {
497
+ lines.push(this.formatCsvLine(namespace, key, item));
498
+ }
499
+ } else {
500
+ lines.push(this.formatCsvLine(namespace, key, value));
501
+ }
502
+ }
503
+ }
504
+ return lines.join("\n") + "\n";
505
+ }
506
+ /**
507
+ * Parse a CSV line, handling quoted values with commas
508
+ */
509
+ parseCsvLine(line) {
510
+ const parts = [];
511
+ let current = "";
512
+ let inQuotes = false;
513
+ for (let i = 0; i < line.length; i++) {
514
+ const char = line[i];
515
+ if (char === '"') {
516
+ if (inQuotes && line[i + 1] === '"') {
517
+ current += '"';
518
+ i++;
519
+ } else {
520
+ inQuotes = !inQuotes;
521
+ }
522
+ } else if (char === "," && !inQuotes) {
523
+ parts.push(current);
524
+ current = "";
525
+ } else {
526
+ current += char;
527
+ }
528
+ }
529
+ parts.push(current);
530
+ return parts;
531
+ }
532
+ /**
533
+ * Format a CSV line with proper escaping
534
+ */
535
+ formatCsvLine(namespace, key, value) {
536
+ const type = this.getValueType(value);
537
+ const valueStr = this.formatCsvValue(value);
538
+ return `${this.escapeCsv(namespace)},${this.escapeCsv(key)},${valueStr},${type}`;
539
+ }
540
+ /**
541
+ * Escape a CSV value
542
+ */
543
+ escapeCsv(value) {
544
+ if (value.includes(",") || value.includes('"') || value.includes("\n")) {
545
+ return `"${value.replace(/"/g, '""')}"`;
546
+ }
547
+ return value;
548
+ }
549
+ /**
550
+ * Format a value for CSV storage
551
+ */
552
+ formatCsvValue(value) {
553
+ if (value === null) {
554
+ return '""';
555
+ }
556
+ if (value === void 0) {
557
+ return '""';
558
+ }
559
+ if (typeof value === "string") {
560
+ return this.escapeCsv(value);
561
+ }
562
+ if (typeof value === "number" || typeof value === "boolean") {
563
+ return this.escapeCsv(String(value));
564
+ }
565
+ return this.escapeCsv(JSON.stringify(value));
566
+ }
567
+ /**
568
+ * Parse a CSV value based on its type
569
+ */
570
+ parseCsvValue(valueStr, typeStr) {
571
+ if (!typeStr || typeStr === "string") {
572
+ return valueStr;
573
+ }
574
+ if (typeStr === "number") {
575
+ return Number(valueStr);
576
+ }
577
+ if (typeStr === "boolean") {
578
+ return valueStr === "true";
579
+ }
580
+ if (typeStr === "object" || typeStr === "array") {
581
+ try {
582
+ return JSON.parse(valueStr);
583
+ } catch {
584
+ return valueStr;
585
+ }
586
+ }
587
+ return valueStr;
588
+ }
589
+ /**
590
+ * Get the type of a value for CSV storage
591
+ */
592
+ getValueType(value) {
593
+ if (value === null || value === void 0) {
594
+ return "string";
595
+ }
596
+ if (typeof value === "number") {
597
+ return "number";
598
+ }
599
+ if (typeof value === "boolean") {
600
+ return "boolean";
601
+ }
602
+ if (Array.isArray(value)) {
603
+ return "array";
604
+ }
605
+ if (typeof value === "object") {
606
+ return "object";
607
+ }
608
+ return "string";
609
+ }
610
+ /**
611
+ * Get the current configuration
612
+ */
613
+ getConfig() {
614
+ return { ...this.config };
615
+ }
616
+ };
617
+
618
+ // src/liquid-extensions.ts
619
+ var ReadFileTag = class extends Tag {
620
+ filepath;
621
+ constructor(token, remainTokens, liquid) {
622
+ super(token, remainTokens, liquid);
623
+ this.filepath = new Value(token.args, liquid);
624
+ }
625
+ *render(ctx, emitter) {
626
+ const filePath = yield this.filepath.value(ctx, false);
627
+ if (!filePath || typeof filePath !== "string") {
628
+ emitter.write("[Error: Invalid file path]");
629
+ return;
630
+ }
631
+ const projectRoot = process.cwd();
632
+ const resolvedPath = path2.resolve(projectRoot, filePath.toString());
633
+ if (!resolvedPath.startsWith(projectRoot)) {
634
+ emitter.write("[Error: File path escapes project directory]");
635
+ return;
636
+ }
637
+ try {
638
+ const content = yield fs2.readFile(resolvedPath, "utf-8");
639
+ emitter.write(content);
640
+ } catch (error) {
641
+ const errorMessage = error instanceof Error ? error.message : error?.code || "Unknown error";
642
+ emitter.write(`[Error reading file: ${errorMessage}]`);
643
+ }
644
+ }
645
+ };
646
+ var permissionsALS = new AsyncLocalStorage();
647
+ async function withPermissionsContext(ctx, fn) {
648
+ return await permissionsALS.run(ctx, fn);
649
+ }
650
+ function configureLiquidWithExtensions(liquid) {
651
+ liquid.registerTag("readfile", ReadFileTag);
652
+ liquid.registerFilter("parse_json", (value) => {
653
+ if (typeof value !== "string") {
654
+ return value;
655
+ }
656
+ try {
657
+ return JSON.parse(value);
658
+ } catch {
659
+ return value;
660
+ }
661
+ });
662
+ liquid.registerFilter("to_json", (value) => {
663
+ try {
664
+ return JSON.stringify(value);
665
+ } catch {
666
+ return "[Error: Unable to serialize to JSON]";
667
+ }
668
+ });
669
+ liquid.registerFilter("safe_label", (value) => {
670
+ if (value == null) return "";
671
+ const s = String(value);
672
+ return s.replace(/[^A-Za-z0-9:\/]/g, "").replace(/\/{2,}/g, "/");
673
+ });
674
+ liquid.registerFilter("safe_label_list", (value) => {
675
+ if (!Array.isArray(value)) return [];
676
+ return value.map((v) => v == null ? "" : String(v)).map((s) => s.replace(/[^A-Za-z0-9:\/]/g, "").replace(/\/{2,}/g, "/")).filter((s) => s.length > 0);
677
+ });
678
+ liquid.registerFilter("unescape_newlines", (value) => {
679
+ if (value == null) return "";
680
+ const s = String(value);
681
+ return s.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ");
682
+ });
683
+ const isLocal = detectLocalMode();
684
+ const resolveAssoc = (val) => {
685
+ if (typeof val === "string" && val.length > 0) return val;
686
+ const store = permissionsALS.getStore();
687
+ return store?.authorAssociation;
688
+ };
689
+ liquid.registerFilter("has_min_permission", (authorAssociation, level) => {
690
+ return hasMinPermission(resolveAssoc(authorAssociation), level, isLocal);
691
+ });
692
+ liquid.registerFilter("is_owner", (authorAssociation) => {
693
+ return isOwner(resolveAssoc(authorAssociation), isLocal);
694
+ });
695
+ liquid.registerFilter("is_member", (authorAssociation) => {
696
+ return isMember(resolveAssoc(authorAssociation), isLocal);
697
+ });
698
+ liquid.registerFilter("is_collaborator", (authorAssociation) => {
699
+ return isCollaborator(resolveAssoc(authorAssociation), isLocal);
700
+ });
701
+ liquid.registerFilter("is_contributor", (authorAssociation) => {
702
+ return isContributor(resolveAssoc(authorAssociation), isLocal);
703
+ });
704
+ liquid.registerFilter("is_first_timer", (authorAssociation) => {
705
+ return isFirstTimer(resolveAssoc(authorAssociation), isLocal);
706
+ });
707
+ const memoryStore = MemoryStore.getInstance();
708
+ liquid.registerFilter("memory_get", (key, namespace) => {
709
+ if (typeof key !== "string") {
710
+ return void 0;
711
+ }
712
+ return memoryStore.get(key, namespace);
713
+ });
714
+ liquid.registerFilter("memory_has", (key, namespace) => {
715
+ if (typeof key !== "string") {
716
+ return false;
717
+ }
718
+ return memoryStore.has(key, namespace);
719
+ });
720
+ liquid.registerFilter("memory_list", (namespace) => {
721
+ return memoryStore.list(namespace);
722
+ });
723
+ }
724
+ function createExtendedLiquid(options = {}) {
725
+ const liquid = new Liquid({
726
+ cache: false,
727
+ strictFilters: false,
728
+ strictVariables: false,
729
+ ...options
730
+ });
731
+ configureLiquidWithExtensions(liquid);
732
+ return liquid;
733
+ }
734
+
735
+ export {
736
+ logger,
737
+ logger_exports,
738
+ init_logger,
739
+ createPermissionHelpers,
740
+ detectLocalMode,
741
+ resolveAssociationFromEvent,
742
+ MemoryStore,
743
+ ReadFileTag,
744
+ withPermissionsContext,
745
+ configureLiquidWithExtensions,
746
+ createExtendedLiquid
747
+ };
748
+ //# sourceMappingURL=chunk-2U6BIWSY.mjs.map