@lolkda/dsh-prompt-manager 3.0.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +606 -0
  3. package/client/client.js +2320 -0
  4. package/cordis.patch.yml +20 -0
  5. package/environment.md +24 -0
  6. package/lib/entries.js +303 -0
  7. package/lib/entries.js.map +1 -0
  8. package/lib/guard.js +134 -0
  9. package/lib/guard.js.map +1 -0
  10. package/lib/index.js +959 -0
  11. package/lib/index.js.map +1 -0
  12. package/lib/net.js +179 -0
  13. package/lib/net.js.map +1 -0
  14. package/lib/pack.js +327 -0
  15. package/lib/pack.js.map +1 -0
  16. package/lib/probe.js +251 -0
  17. package/lib/probe.js.map +1 -0
  18. package/lib/routes.js +718 -0
  19. package/lib/routes.js.map +1 -0
  20. package/lib/scripts.js +803 -0
  21. package/lib/scripts.js.map +1 -0
  22. package/lib/source.js +308 -0
  23. package/lib/source.js.map +1 -0
  24. package/lib/store.js +223 -0
  25. package/lib/store.js.map +1 -0
  26. package/lib/subscriptions.js +269 -0
  27. package/lib/subscriptions.js.map +1 -0
  28. package/lib/sync.js +646 -0
  29. package/lib/sync.js.map +1 -0
  30. package/lib/types/entries.d.ts +194 -0
  31. package/lib/types/entries.d.ts.map +1 -0
  32. package/lib/types/guard.d.ts +63 -0
  33. package/lib/types/guard.d.ts.map +1 -0
  34. package/lib/types/index.d.ts +176 -0
  35. package/lib/types/index.d.ts.map +1 -0
  36. package/lib/types/net.d.ts +81 -0
  37. package/lib/types/net.d.ts.map +1 -0
  38. package/lib/types/pack.d.ts +298 -0
  39. package/lib/types/pack.d.ts.map +1 -0
  40. package/lib/types/probe.d.ts +150 -0
  41. package/lib/types/probe.d.ts.map +1 -0
  42. package/lib/types/routes.d.ts +85 -0
  43. package/lib/types/routes.d.ts.map +1 -0
  44. package/lib/types/scripts.d.ts +455 -0
  45. package/lib/types/scripts.d.ts.map +1 -0
  46. package/lib/types/source.d.ts +194 -0
  47. package/lib/types/source.d.ts.map +1 -0
  48. package/lib/types/store.d.ts +140 -0
  49. package/lib/types/store.d.ts.map +1 -0
  50. package/lib/types/subscriptions.d.ts +204 -0
  51. package/lib/types/subscriptions.d.ts.map +1 -0
  52. package/lib/types/sync.d.ts +248 -0
  53. package/lib/types/sync.d.ts.map +1 -0
  54. package/package.json +100 -0
@@ -0,0 +1,20 @@
1
+ # The dsh-prompt-manager bundle patch: mounting this package is one insert.
2
+ #
3
+ # A profile that installs this package gains this row automatically — `dsh plugin
4
+ # add` (and any other install path) reconciles `dsh.profile.bundles` against the
5
+ # installed dependencies, and a dependency whose manifest declares
6
+ # `dsh.bundle.patch` joins the layer stack. The row mounts the Host half, which is
7
+ # also what makes the browser half load: the client module system serves the
8
+ # `exports["./client"]` bundle of every package that declares `dsh.client` *and* is
9
+ # an active Loader entry.
10
+ #
11
+ # No `config` here on purpose: the plugin's own defaults (machine-environment facts
12
+ # registered as variables) are what a fresh install wants. Disable or repoint them
13
+ # in the profile's own cordis.patch.yml, which applies after every bundle layer.
14
+ #
15
+ # The entry id is plugin-owned and must not collide with another registry entry's
16
+ # entry IDs, so it is the package's own name rather than the bare `prompt-manager`.
17
+
18
+ - insert:
19
+ - id: dsh-prompt-manager
20
+ name: '@lolkda/dsh-prompt-manager'
package/environment.md ADDED
@@ -0,0 +1,24 @@
1
+ # Machine environment
2
+
3
+ Detected on the machine running the DSH host process, and refreshed whenever the prompt plugin mounts. Every value below is measured rather than assumed — when one of them conflicts with what you observe, trust the observation.
4
+
5
+ ## System
6
+
7
+ - {{os}}, release `{{os_release}}` (`{{platform}}` / `{{arch}}`)
8
+
9
+ ## Shells
10
+
11
+ - **bash** `{{bash}}` — write POSIX syntax: `&&`, `$VAR`, forward slashes.
12
+ - **PowerShell** `pwsh` `{{pwsh}}`
13
+ - Use PowerShell 7 or newer. Windows PowerShell 5.1 is not UTF-8 by default — file writes go through the system ANSI code page, and what it pipes to native programs differs as well — so non-ASCII text is mangled or fails outright. Do not fall back to it.
14
+ - A value of `(not installed)` above means this machine has no usable PowerShell; that is not a reason to reach for 5.1.
15
+
16
+ ## Version control
17
+
18
+ - **git** `{{git}}`
19
+ - A repository with no committer identity fails its first commit with `unable to auto-detect email`. Check before committing (`git config user.email`), and where the repository already has commits, reuse their author rather than inventing one.
20
+
21
+ ## Languages
22
+
23
+ - **Node** `{{node}}`
24
+ - **Python** `{{python}}` — the command is normally `python`. On Windows `python3` may be an App Execution Alias stub that starts, prints nothing, and reports no error, so a silent `python3 --version` is not evidence that Python is missing.
package/lib/entries.js ADDED
@@ -0,0 +1,303 @@
1
+ /**
2
+ * The prompt index: entry identity and the settings schema that carries one
3
+ * section per entry.
4
+ *
5
+ * An entry's markdown body lives in one file per entry under the store
6
+ * directory; everything else about it — title, order, whether it is injected —
7
+ * lives in the `prompt-manager` settings namespace this module describes. Keeping
8
+ * the index in the settings document means the browser gets reads, writes,
9
+ * revision fencing, and the "user overrode this" flag from the shared settings
10
+ * transport, while the bodies stay plain `.md` files a person can edit directly.
11
+ * The same namespace carries the presets: named selections that decide injection
12
+ * on their own while one of them is active, so a single `activePreset` write
13
+ * swaps the whole set without touching any entry.
14
+ *
15
+ * Entry bodies live either as plain `.md` files a person can edit directly, or
16
+ * as one markdown file shipped with this package: a fresh install starts with
17
+ * the built-in machine-environment prompt, and anything a person writes or
18
+ * subscribes overrides it.
19
+ *
20
+ * @module @lolkda/dsh-prompt-manager/entries
21
+ */
22
+ import { readFileSync } from 'node:fs';
23
+ import { fileURLToPath } from 'node:url';
24
+ /** Order handed to the first entry a person adds; later additions sort after it. */
25
+ export const USER_ORDER_START = 30;
26
+ /** At most this many entries may be active at once. */
27
+ export const MAX_ENTRIES = 50;
28
+ /** At most this many presets may be configured. */
29
+ export const MAX_PRESETS = 20;
30
+ /** At most this many entries one preset may select. */
31
+ export const MAX_PRESET_ENTRIES = MAX_ENTRIES;
32
+ /** Ref a source starts on when the settings page names none. */
33
+ export const DEFAULT_SOURCE_REF = 'main';
34
+ /** Largest accepted body, in bytes. */
35
+ export const MAX_BODY_BYTES = 256 * 1024;
36
+ /** Largest accepted title, in characters. */
37
+ export const MAX_TITLE_LENGTH = 120;
38
+ /** Largest accepted id, in characters. */
39
+ export const MAX_ID_LENGTH = 64;
40
+ /**
41
+ * Entry id grammar. The id becomes a file name and a prompt-section name
42
+ * suffix, so it stays lowercase, hyphenated, and path-safe by construction.
43
+ */
44
+ export const ENTRY_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
45
+ /** The prompts this package ships. */
46
+ export const BUILTIN_PROMPTS = [
47
+ {
48
+ id: 'env',
49
+ title: '机器环境',
50
+ order: 5,
51
+ file: new URL('../environment.md', import.meta.url),
52
+ },
53
+ ];
54
+ /**
55
+ * The base layer's entries: one per {@link BUILTIN_PROMPTS} entry, enabled.
56
+ *
57
+ * They behave like any other entry — the page lists them, a write overrides
58
+ * them, a toggle disables them — so the built-in prose is a starting point
59
+ * rather than something the deployment cannot reach.
60
+ *
61
+ * @returns fresh entry records, safe to hand to a settings base layer.
62
+ */
63
+ export function builtinEntries() {
64
+ return BUILTIN_PROMPTS.map((prompt) => ({
65
+ id: prompt.id,
66
+ title: prompt.title,
67
+ order: prompt.order,
68
+ enabled: true,
69
+ }));
70
+ }
71
+ /**
72
+ * The packaged body for one entry id.
73
+ * @param id - entry id to look up.
74
+ * @returns the exact UTF-8 markdown, or `undefined` when the package ships none
75
+ * for that id, or the packaged file cannot be read.
76
+ */
77
+ export function readBuiltinBody(id) {
78
+ const prompt = BUILTIN_PROMPTS.find((candidate) => candidate.id === id);
79
+ if (prompt === undefined)
80
+ return undefined;
81
+ try {
82
+ const text = readFileSync(fileURLToPath(prompt.file), 'utf8');
83
+ return text.trim().length === 0 ? undefined : text;
84
+ }
85
+ catch {
86
+ return undefined;
87
+ }
88
+ }
89
+ /** Whether a value is a usable entry id. */
90
+ export function isEntryId(value) {
91
+ return typeof value === 'string' && value.length <= MAX_ID_LENGTH && ENTRY_ID_PATTERN.test(value);
92
+ }
93
+ /**
94
+ * Turn a human title into an unused entry id.
95
+ *
96
+ * Only ASCII letters and digits survive, so a title written entirely in a
97
+ * non-Latin script falls back to the `entry` stem. That is deliberate: the id
98
+ * is an internal handle (file name and section-name suffix), never user copy.
99
+ *
100
+ * @param title - the label a person typed.
101
+ * @param taken - ids already in use.
102
+ * @returns a lowercase, hyphenated, unused id.
103
+ */
104
+ export function entryIdFor(title, taken) {
105
+ const stem = title
106
+ .toLowerCase()
107
+ .replace(/[^a-z0-9]+/g, '-')
108
+ .replace(/^-+|-+$/g, '')
109
+ .slice(0, 32);
110
+ const base = stem.length >= 2 ? stem : 'entry';
111
+ const used = new Set(taken);
112
+ if (!used.has(base))
113
+ return base;
114
+ for (let suffix = 2; suffix < 1000; suffix += 1) {
115
+ const candidate = `${base.slice(0, 28)}-${String(suffix)}`;
116
+ if (!used.has(candidate))
117
+ return candidate;
118
+ }
119
+ return `${base.slice(0, 24)}-${String(Date.now())}`;
120
+ }
121
+ /**
122
+ * Keep one id when it is free, or take the nearest free variant of it.
123
+ *
124
+ * For the places where the id *is* the identity rather than a handle derived
125
+ * from a title: a subscribed entry's body is looked up by id upstream, so an
126
+ * imported `env` that collides is far better off as `env-2` than as whatever a
127
+ * title — possibly written entirely in a non-Latin script — would slug to.
128
+ *
129
+ * @param preferred - the id that was asked for.
130
+ * @param taken - ids already in use.
131
+ * @returns `preferred`, or a `-2`-suffixed variant of its stem.
132
+ */
133
+ export function freeId(preferred, taken) {
134
+ const used = new Set(taken);
135
+ if (isEntryId(preferred) && !used.has(preferred))
136
+ return preferred;
137
+ const stem = (isEntryId(preferred) ? preferred : 'entry').replace(/-\d+$/, '').slice(0, 28);
138
+ const base = stem.length >= 2 ? stem : 'entry';
139
+ if (!used.has(base))
140
+ return base;
141
+ for (let suffix = 2; suffix < 1000; suffix += 1) {
142
+ const candidate = `${base.slice(0, 28)}-${String(suffix)}`;
143
+ if (!used.has(candidate))
144
+ return candidate;
145
+ }
146
+ return `${base.slice(0, 24)}-${String(Date.now())}`;
147
+ }
148
+ /** One usable entry, or `undefined` when the raw value is unusable. */
149
+ function toEntry(raw) {
150
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
151
+ return undefined;
152
+ const record = raw;
153
+ const { id, title, order, enabled, source } = record;
154
+ if (!isEntryId(id))
155
+ return undefined;
156
+ if (typeof order !== 'number' || !Number.isFinite(order))
157
+ return undefined;
158
+ if (typeof enabled !== 'boolean')
159
+ return undefined;
160
+ const label = typeof title === 'string' ? title.trim() : '';
161
+ const entry = {
162
+ id,
163
+ title: label.length === 0 ? id : label.slice(0, MAX_TITLE_LENGTH),
164
+ order,
165
+ enabled,
166
+ };
167
+ if (typeof source === 'string' && source.length > 0)
168
+ entry.source = source.slice(0, 64);
169
+ return entry;
170
+ }
171
+ /**
172
+ * Narrow one resolved settings value into an index. A hand-edited document can
173
+ * hold anything, so unusable entries are dropped rather than thrown: the worst
174
+ * case is a prompt with fewer sections, never a session that cannot assemble.
175
+ *
176
+ * @param raw - the resolved `prompt-manager` namespace value.
177
+ * @returns the usable entries, deduplicated by id and capped at {@link MAX_ENTRIES}.
178
+ */
179
+ export function parseEntries(raw) {
180
+ const list = typeof raw === 'object' && raw !== null && !Array.isArray(raw)
181
+ ? raw['entries']
182
+ : undefined;
183
+ if (!Array.isArray(list))
184
+ return [];
185
+ const entries = [];
186
+ const seen = new Set();
187
+ for (const candidate of list) {
188
+ const entry = toEntry(candidate);
189
+ if (entry === undefined || seen.has(entry.id))
190
+ continue;
191
+ seen.add(entry.id);
192
+ entries.push(entry);
193
+ if (entries.length >= MAX_ENTRIES)
194
+ break;
195
+ }
196
+ return entries;
197
+ }
198
+ /** One usable preset, or `undefined` when the raw value is unusable. */
199
+ function toPreset(raw) {
200
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
201
+ return undefined;
202
+ const record = raw;
203
+ const { id, name, entries } = record;
204
+ if (!isEntryId(id))
205
+ return undefined;
206
+ const label = typeof name === 'string' ? name.trim() : '';
207
+ const chosen = [];
208
+ const seen = new Set();
209
+ if (Array.isArray(entries)) {
210
+ for (const candidate of entries) {
211
+ // An id the index does not carry right now is kept: it may belong to a
212
+ // subscription that has not come back yet, and dropping it here would
213
+ // silently rewrite what the person selected.
214
+ if (typeof candidate !== 'string' || !isEntryId(candidate) || seen.has(candidate))
215
+ continue;
216
+ seen.add(candidate);
217
+ chosen.push(candidate);
218
+ if (chosen.length >= MAX_PRESET_ENTRIES)
219
+ break;
220
+ }
221
+ }
222
+ return { id, name: label.length === 0 ? id : label.slice(0, MAX_TITLE_LENGTH), entries: chosen };
223
+ }
224
+ /**
225
+ * Narrow a resolved settings value into the preset list. Like {@link parseEntries},
226
+ * a hand-edited document can hold anything, so unusable presets are dropped
227
+ * rather than thrown: the worst case is a smaller list, never an assembly that
228
+ * cannot be built.
229
+ *
230
+ * @param raw - the resolved `presets` field.
231
+ * @returns the usable presets, deduplicated by id and capped at {@link MAX_PRESETS}.
232
+ */
233
+ export function parsePresets(raw) {
234
+ if (!Array.isArray(raw))
235
+ return [];
236
+ const presets = [];
237
+ const seen = new Set();
238
+ for (const candidate of raw) {
239
+ const preset = toPreset(candidate);
240
+ if (preset === undefined || seen.has(preset.id))
241
+ continue;
242
+ seen.add(preset.id);
243
+ presets.push(preset);
244
+ if (presets.length >= MAX_PRESETS)
245
+ break;
246
+ }
247
+ return presets;
248
+ }
249
+ /**
250
+ * The id of the preset in force.
251
+ * @param raw - the resolved `activePreset` field.
252
+ * @returns the configured id, or `''` when the deployment runs without a preset.
253
+ */
254
+ export function activePresetOf(raw) {
255
+ return typeof raw === 'string' ? raw.trim() : '';
256
+ }
257
+ /**
258
+ * Build the `prompt-manager` namespace schema.
259
+ *
260
+ * @param factory - the schemastery factory loaded at mount.
261
+ * @returns a schema carrying the entry index, the subscriptions, and the
262
+ * outbound settings the engine reads.
263
+ */
264
+ export function buildIndexSchema(factory) {
265
+ const entry = factory.object({
266
+ id: factory.string().required(),
267
+ title: factory.string().default(''),
268
+ order: factory.number().default(USER_ORDER_START),
269
+ enabled: factory.boolean().default(true),
270
+ // No default: a local entry must resolve without the field at all. With
271
+ // `.default('')` every entry carried `source: ''`, which reads as
272
+ // "subscribed" to anything testing presence rather than value, and a
273
+ // settings write-back would then persist the phantom field to disk.
274
+ source: factory.string(),
275
+ });
276
+ const source = factory.object({
277
+ id: factory.string().required(),
278
+ repo: factory.string().required(),
279
+ ref: factory.string().default(DEFAULT_SOURCE_REF),
280
+ mirror: factory.string().default(''),
281
+ enabled: factory.boolean().default(true),
282
+ });
283
+ const preset = factory.object({
284
+ id: factory.string().required(),
285
+ name: factory.string().default(''),
286
+ entries: factory.array(factory.string()).default([]),
287
+ });
288
+ const proxy = factory.object({
289
+ kind: factory.string().default('none'),
290
+ url: factory.string().default(''),
291
+ }).default({ kind: 'none', url: '' });
292
+ return factory.object({
293
+ entries: factory.array(entry).default([]),
294
+ presets: factory.array(preset).default([]),
295
+ // No default beyond the empty string: an unset preset means the entries'
296
+ // own switches decide, which is what a deployment that never made one gets.
297
+ activePreset: factory.string().default(''),
298
+ sources: factory.array(source).default([]),
299
+ mirror: factory.string().default(''),
300
+ proxy,
301
+ });
302
+ }
303
+ //# sourceMappingURL=entries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entries.js","sourceRoot":"","sources":["../src/entries.ts"],"names":[],"mappings":"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"}
package/lib/guard.js ADDED
@@ -0,0 +1,134 @@
1
+ /**
2
+ * The reference guard: keeps a prompt body from breaking the whole assembly.
3
+ *
4
+ * The registry interpolates section text strictly — a reference it cannot
5
+ * resolve, and a reference whose shape is not a variable name, both throw, and
6
+ * one throwing section fails the entire assembly. Since the plugin serves text
7
+ * that a person can also hand-edit in `sections/`, one stray `{{ }}` in a
8
+ * markdown file would otherwise break every model step of the profile.
9
+ *
10
+ * This module therefore mirrors the registry's own scan, reference for
11
+ * reference, and defuses exactly the groups the registry would throw on: it
12
+ * puts a zero-width space after each of the two opening braces, so the text
13
+ * reads the same to a model but no longer starts a reference. Everything the
14
+ * registry can resolve is left untouched, so a variable another row registers
15
+ * still interpolates normally.
16
+ *
17
+ * The scan below is a deliberate mirror of the registry's `interpolate`
18
+ * (`@deepseek-ai/dsh-system-prompt`, `lib/index.js`, its `GROUP_AT` and
19
+ * `VARIABLE_NAME` constants). When that implementation changes shape, this one
20
+ * has to follow.
21
+ *
22
+ * @module @lolkda/dsh-prompt-manager/guard
23
+ */
24
+ /** A complete `{{...}}` group at the scan position, as the registry reads it. */
25
+ const GROUP_AT = /^\{\{([^{}]*)\}\}/;
26
+ /** Variable-name grammar the registry enforces between the braces. */
27
+ const VARIABLE_NAME = /^[a-z][a-z0-9_]*$/;
28
+ /** Longest snippet of an unusable reference carried into a report. */
29
+ const SNIPPET_LENGTH = 16;
30
+ /**
31
+ * Zero-width space, inserted after each brace of a reference that must not
32
+ * resolve. It is invisible in rendered prose, and because every emitted `{` is
33
+ * followed by one, neither the escaped reference nor its neighbours can ever
34
+ * re-form a `{{` pair.
35
+ */
36
+ export const ESCAPE_MARK = '\u200b';
37
+ /** One defused reference, as it appears in the guarded text. */
38
+ function escapedOpener() {
39
+ return `{${ESCAPE_MARK}{${ESCAPE_MARK}`;
40
+ }
41
+ /** Whether one reference name resolves for the assembly being rendered. */
42
+ function resolvable(name, variables) {
43
+ return VARIABLE_NAME.test(name) && Object.hasOwn(variables, name) && variables[name] !== undefined;
44
+ }
45
+ /**
46
+ * Defuse every reference the registry would throw on.
47
+ *
48
+ * A reference is defused when its shape is not a variable name, when no
49
+ * variable of that name exists for this assembly, or when the variable exists
50
+ * but has no value. A lone `{{` with no later `}}` is literal prose to the
51
+ * registry and is left exactly as it is.
52
+ *
53
+ * @param text - the section text about to be handed to the registry.
54
+ * @param variables - the names the registry will resolve for this assembly.
55
+ * @returns the safe text and the references that were defused.
56
+ */
57
+ export function sanitizeReferences(text, variables) {
58
+ const escaped = [];
59
+ let result = '';
60
+ let last = 0;
61
+ let open = text.indexOf('{{');
62
+ while (open >= 0) {
63
+ const group = GROUP_AT.exec(text.slice(open));
64
+ if (group === null) {
65
+ // No complete group here. The registry only fails a stray opener when a
66
+ // closer follows it somewhere; otherwise the opener is ordinary prose.
67
+ if (text.indexOf('}}', open + 2) >= 0) {
68
+ result += text.slice(last, open) + escapedOpener();
69
+ escaped.push(text.slice(open, open + SNIPPET_LENGTH));
70
+ }
71
+ else {
72
+ result += text.slice(last, open + 2);
73
+ }
74
+ last = open + 2;
75
+ open = text.indexOf('{{', last);
76
+ continue;
77
+ }
78
+ const name = group[0].slice(2, -2);
79
+ if (resolvable(name, variables)) {
80
+ result += text.slice(last, open + group[0].length);
81
+ last = open + group[0].length;
82
+ }
83
+ else {
84
+ // Defuse the opener and keep the body of the reference verbatim: the
85
+ // prose a person wrote must survive, closing braces included.
86
+ result += text.slice(last, open) + escapedOpener();
87
+ escaped.push(group[0]);
88
+ last = open + 2;
89
+ }
90
+ open = text.indexOf('{{', last);
91
+ }
92
+ return { text: result + text.slice(last), escaped };
93
+ }
94
+ /**
95
+ * The references that can never resolve, whatever else is registered.
96
+ *
97
+ * A malformed group throws for every deployment, so a save can refuse it up
98
+ * front. A well-formed name is not listed: another row may register it, and a
99
+ * name that is merely unregistered today is a body the person may still be
100
+ * writing.
101
+ *
102
+ * @param text - the body about to be stored.
103
+ * @returns the unusable references, deduplicated, in the order they appear.
104
+ */
105
+ export function malformedReferences(text) {
106
+ const found = [];
107
+ const seen = new Set();
108
+ let last = 0;
109
+ let open = text.indexOf('{{');
110
+ while (open >= 0) {
111
+ const group = GROUP_AT.exec(text.slice(open));
112
+ if (group === null) {
113
+ if (text.indexOf('}}', open + 2) >= 0) {
114
+ const snippet = text.slice(open, open + SNIPPET_LENGTH);
115
+ if (!seen.has(snippet)) {
116
+ seen.add(snippet);
117
+ found.push(snippet);
118
+ }
119
+ }
120
+ last = open + 2;
121
+ open = text.indexOf('{{', last);
122
+ continue;
123
+ }
124
+ const name = group[0].slice(2, -2);
125
+ if (!VARIABLE_NAME.test(name) && !seen.has(group[0])) {
126
+ seen.add(group[0]);
127
+ found.push(group[0]);
128
+ }
129
+ last = open + group[0].length;
130
+ open = text.indexOf('{{', last);
131
+ }
132
+ return found;
133
+ }
134
+ //# sourceMappingURL=guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}