@jhizzard/termdeck 0.10.3 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,76 @@
1
+ // Migration SQL templating helper.
2
+ //
3
+ // Several Rumen migrations ship with placeholder markers for values that
4
+ // can only be resolved at apply-time: the user's Supabase project ref,
5
+ // service-role JWT name, etc. Migration 002 (rumen-tick schedule) and 003
6
+ // (graph-inference-tick schedule) both embed the project ref inside the
7
+ // pg_cron body that calls `net.http_post` on
8
+ // `https://<project-ref>.supabase.co/functions/v1/<name>`.
9
+ //
10
+ // Pre-Sprint 42, init-rumen.js::applySchedule did this substitution inline
11
+ // for migration 002 only — and migration 003 (added Sprint 38) shipped its
12
+ // `<project-ref>` placeholder unsubstituted. Sprint 42 T3 extracts the
13
+ // substitution into this shared helper so every migration that lists a
14
+ // known placeholder gets templated consistently.
15
+ //
16
+ // Supported placeholder syntaxes (both accepted; legacy + sigil-style):
17
+ // <project-ref>
18
+ // {{PROJECT_REF}}
19
+ //
20
+ // API:
21
+ // applyTemplating(sql, vars)
22
+ // sql: string — raw migration body
23
+ // vars: { projectRef?: string }
24
+ // returns: string — substituted body
25
+ // throws: Error — when SQL contains a placeholder but `vars` lacks the
26
+ // corresponding value. (Quietly leaving the placeholder in
27
+ // would let an unsubstituted URL ship to pg_cron, which is the
28
+ // very bug this module exists to prevent.)
29
+ //
30
+ // Idempotent: applying twice yields the same string. Safe on SQL with no
31
+ // placeholders (returns the input unchanged).
32
+
33
+ 'use strict';
34
+
35
+ const PLACEHOLDER_SYNTAXES = Object.freeze({
36
+ projectRef: [/<project-ref>/g, /\{\{PROJECT_REF\}\}/g],
37
+ });
38
+
39
+ function hasPlaceholder(sql, patterns) {
40
+ for (const pat of patterns) {
41
+ pat.lastIndex = 0;
42
+ if (pat.test(sql)) return true;
43
+ }
44
+ return false;
45
+ }
46
+
47
+ function substitute(sql, patterns, value) {
48
+ let out = sql;
49
+ for (const pat of patterns) {
50
+ out = out.replace(pat, value);
51
+ }
52
+ return out;
53
+ }
54
+
55
+ function applyTemplating(sql, vars) {
56
+ if (typeof sql !== 'string') {
57
+ throw new TypeError('applyTemplating: sql must be a string');
58
+ }
59
+ const v = vars || {};
60
+ let out = sql;
61
+
62
+ for (const [varName, patterns] of Object.entries(PLACEHOLDER_SYNTAXES)) {
63
+ if (!hasPlaceholder(out, patterns)) continue;
64
+ const value = v[varName];
65
+ if (typeof value !== 'string' || value.length === 0) {
66
+ throw new Error(
67
+ `applyTemplating: SQL contains ${varName} placeholder but vars.${varName} is missing or empty. ` +
68
+ `Refusing to ship an unsubstituted placeholder to the database.`
69
+ );
70
+ }
71
+ out = substitute(out, patterns, value);
72
+ }
73
+ return out;
74
+ }
75
+
76
+ module.exports = { applyTemplating, PLACEHOLDER_SYNTAXES };