@aippy/vite-plugins 0.4.0-dev.3 → 0.4.0-dev.4

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.
package/README.md CHANGED
@@ -104,6 +104,10 @@ UNLICENSED - Aippy Team
104
104
 
105
105
  ## Asset files
106
106
 
107
- `src/config/assets.json` is a flat URL dictionary. The plugin writes only that file. The project-root `assets-plan.json` is read-only input for reserved names and `implementation.path` exclusions; a missing or unreadable plan does not stop the build. Invalid dictionary JSON, or a single-file v1 plan stored in the dictionary, stops the build without overwriting the file.
107
+ `src/config/assets.json` is a flat URL dictionary. The plugin writes only that file. The project-root `assets-plan.json` is read-only input for reserved names and `implementation.path` exclusions. The plugin does not validate the plan; aippy-chat does. A missing or unreadable plan does not stop the build. Invalid dictionary JSON, a single-file v1 plan stored in the dictionary, or a failed dictionary/wrapper write stops the build without overwriting the file. A source file the plugin cannot parse or rewrite only logs a warning.
108
108
 
109
- Import the generated map from `@/config/assets`. It exports the dictionary's string values. `window.getReplacedAssets` still mutates that object in place. Preload reads those string values and URLs in other source files, not prompts inside the plan. The JSON schema remains `@aippy/vite-plugins/assets.schema.json`. Version 0.4.0-dev.3 is prepared locally and is not published yet.
109
+ Import the generated map from `@/config/assets`. It exports the dictionary's string values. `window.getReplacedAssets` still mutates that object in place. Preload reads those string values and URLs in other source files, not prompts inside the plan.
110
+
111
+ Source written as `aippy-asset:` + `encodeURIComponent(key)` + `:` + url rewrites to that dictionary key. The url may be any reference aippy-chat accepts (`ValidReference`): http(s), root-relative, or relative. A token the rewrite cannot reach (template literal, CSS, HTML, unknown key) is reduced to its plain url, on disk in `devMode` and in every build's `transform`, so a token never reaches the browser.
112
+
113
+ Version 0.4.0-dev.3 is published. The `dev` dist-tag points at it. `latest` remains `0.3.0`.
@@ -1,2 +1,2 @@
1
- import { t as e } from "../aippy-preload-DtngFmJ5.js";
1
+ import { t as e } from "../aippy-preload-BjhVTEKH.js";
2
2
  export { e as aippyPreloadPlugin, e as default };
@@ -1,98 +1,87 @@
1
1
  import { extractAssetsFromContent as e, isValidAsset as t } from "./utils/index.js";
2
- import { a as n, l as r, m as i, n as a, o, s } from "./generator-utils-CFYv7n_h.js";
3
- import { promises as c } from "fs";
4
- import l from "path";
2
+ import { l as n, m as r, s as i } from "./generator-utils-VznlFF00.js";
3
+ import { promises as a } from "fs";
4
+ import o from "path";
5
5
  //#region src/aippy-preload/scanner-utils.ts
6
- async function u(t) {
7
- let n = [], r = [l.join(process.cwd(), "index.html")];
8
- for (let i of r) try {
9
- let r = await c.readFile(i, "utf-8"), a = e(r, "html", t);
10
- n.push(...a);
6
+ async function s(t) {
7
+ let n = [], r = [o.join(process.cwd(), "index.html")];
8
+ for (let o of r) try {
9
+ let r = i(await a.readFile(o, "utf-8")), s = e(r, "html", t);
10
+ n.push(...s);
11
11
  } catch {
12
12
  console.warn("⚠️ No index.html found in project root");
13
13
  }
14
14
  return n;
15
15
  }
16
- async function d(t, n) {
17
- let u = [], d = l.resolve(t, "config/assets.json"), f, p = /* @__PURE__ */ new Set();
16
+ async function c(t, s) {
17
+ let c = [], l = o.resolve(t, "config/assets.json"), u = /* @__PURE__ */ new Set();
18
18
  try {
19
- let e = JSON.parse(await c.readFile(d, "utf8"));
20
- if (a(e)) {
21
- let t = s(JSON.stringify(e));
22
- o(t) && (f = t);
23
- } else e && typeof e == "object" && !Array.isArray(e) && (p = new Set(Object.keys(e)));
19
+ let e = JSON.parse(await a.readFile(l, "utf8"));
20
+ e && typeof e == "object" && !Array.isArray(e) && (u = new Set(Object.keys(e)));
24
21
  } catch {}
25
- let m = await i(t, [
22
+ let d = await r(t, [
26
23
  ".ts",
27
24
  ".tsx",
28
25
  ".js",
29
26
  ".jsx"
30
27
  ]);
31
- for (let i of m) try {
32
- if (f && l.resolve(i) === d.replace(/\.json$/, ".ts")) continue;
33
- let a = await c.readFile(i, "utf-8"), o = e(a, "js", n), s = /* @__PURE__ */ new Set([...p, ...f ? Object.keys(f.assets) : []]);
34
- o.some((e) => s.has(e)) && (o = e(r(a, i, t, d), "js", n)), u.push(...o);
28
+ for (let r of d) try {
29
+ let o = i(await a.readFile(r, "utf-8")), d = e(o, "js", s);
30
+ d.some((e) => u.has(e)) && (d = e(n(o, r, t, l), "js", s)), c.push(...d);
35
31
  } catch (e) {
36
- console.warn(`⚠️ Failed to read JS file: ${i}`, e);
32
+ console.warn(`⚠️ Failed to read JS file: ${r}`, e);
37
33
  }
38
- return u;
34
+ return c;
39
35
  }
40
- async function f(t, n) {
41
- let r = [], a = await i(t, [
36
+ async function l(t, n) {
37
+ let o = [], s = await r(t, [
42
38
  ".css",
43
39
  ".scss",
44
40
  ".sass",
45
41
  ".less"
46
42
  ]);
47
- for (let t of a) try {
48
- let i = await c.readFile(t, "utf-8"), a = e(i, "css", n);
49
- r.push(...a);
43
+ for (let t of s) try {
44
+ let r = i(await a.readFile(t, "utf-8")), s = e(r, "css", n);
45
+ o.push(...s);
50
46
  } catch (e) {
51
47
  console.warn(`⚠️ Failed to read CSS file: ${t}`, e);
52
48
  }
53
- return r;
49
+ return o;
54
50
  }
55
- function p(e, n) {
51
+ function u(e, n) {
56
52
  if (!t(e) || /["'<>\s`]/.test(e)) return !1;
57
53
  let r = e.split(/[?#]/, 1)[0].toLowerCase();
58
54
  return n.some((e) => r.endsWith(e.toLowerCase()));
59
55
  }
60
- async function m(t, r) {
61
- let u = [], d = await i(t, [".json"]);
62
- for (let i of d) try {
63
- let d = await c.readFile(i, "utf-8");
64
- if (l.basename(i) === "assets-plan.json") continue;
65
- if (l.resolve(i) === l.resolve(t, "config/assets.json")) {
66
- let e = JSON.parse(d);
67
- if (a(e)) {
68
- let e = s(d);
69
- if (o(e)) {
70
- u.push(...Object.values(n(e)));
71
- continue;
72
- }
73
- }
56
+ async function d(t, n) {
57
+ let s = [], c = await r(t, [".json"]);
58
+ for (let r of c) try {
59
+ let c = i(await a.readFile(r, "utf-8"));
60
+ if (o.basename(r) === "assets-plan.json") continue;
61
+ if (o.resolve(r) === o.resolve(t, "config/assets.json")) {
62
+ let e = JSON.parse(c);
74
63
  if (e && typeof e == "object" && !Array.isArray(e)) {
75
- for (let t of Object.values(e)) typeof t == "string" && p(t, r) && u.push(t);
64
+ for (let t of Object.values(e)) typeof t == "string" && u(t, n) && s.push(t);
76
65
  continue;
77
66
  }
78
67
  }
79
- let f = e(d, "js", r);
80
- u.push(...f);
68
+ let l = e(c, "js", n);
69
+ s.push(...l);
81
70
  } catch (e) {
82
- console.warn(`⚠️ Failed to read JSON file: ${i}`, e);
71
+ console.warn(`⚠️ Failed to read JSON file: ${r}`, e);
83
72
  }
84
- return u;
73
+ return s;
85
74
  }
86
- async function h(e, t, n) {
75
+ async function f(e, t, n) {
87
76
  console.log("🔍 Scanning source files for static assets...");
88
77
  let r = [];
89
78
  if (n) {
90
79
  console.log("🔍 Deep scanning source file contents...");
91
80
  let [n, i, a, o] = await Promise.all([
92
- u(t),
93
- d(e, t),
94
- f(e, t),
95
- m(e, t)
81
+ s(t),
82
+ c(e, t),
83
+ l(e, t),
84
+ d(e, t)
96
85
  ]);
97
86
  r.push(...n, ...i, ...a, ...o);
98
87
  }
@@ -100,20 +89,20 @@ async function h(e, t, n) {
100
89
  }
101
90
  //#endregion
102
91
  //#region src/aippy-preload/html-utils.ts
103
- var g = "<head>", _ = 6;
104
- function v(e) {
92
+ var p = "<head>", m = 6;
93
+ function h(e) {
105
94
  return e.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
106
95
  }
107
- function y(e) {
108
- return e.map((e) => ` <meta content="${v(e)}" name="aippy:preload">`).join("\n");
96
+ function g(e) {
97
+ return e.map((e) => ` <meta content="${h(e)}" name="aippy:preload">`).join("\n");
109
98
  }
110
- function b(e, t) {
111
- let n = e.indexOf(g);
99
+ function _(e, t) {
100
+ let n = e.indexOf(p);
112
101
  if (n === -1) throw Error("Could not find <head> tag in HTML");
113
- let r = n + _;
102
+ let r = n + m;
114
103
  return e.slice(0, r) + "\n" + t + "\n" + e.slice(r);
115
104
  }
116
- function x(e) {
105
+ function v(e) {
117
106
  let t = e.length;
118
107
  if (console.log(`📁 Found ${t} static ${t === 1 ? "asset" : "assets"} in source files`), t === 0) {
119
108
  console.log("⚠️ No static assets found in source files.");
@@ -121,22 +110,22 @@ function x(e) {
121
110
  }
122
111
  console.log(`📋 ${t === 1 ? "Asset" : "Assets"} found:`), e.forEach((e) => console.log(` - ${e}`));
123
112
  }
124
- function S(e) {
113
+ function y(e) {
125
114
  console.log(`✅ Injected ${e} preload meta ${e === 1 ? "tag" : "tags"} into index.html`);
126
115
  }
127
116
  //#endregion
128
117
  //#region src/aippy-preload/index.ts
129
- var C = 3, w = 1e3;
130
- function T(e) {
118
+ var b = 3, x = 1e3;
119
+ function S(e) {
131
120
  return new Promise((t) => setTimeout(t, e));
132
121
  }
133
- async function E(e, t = C, n = w) {
122
+ async function C(e, t = b, n = x) {
134
123
  for (let r = 1; r <= t; r++) try {
135
- return await c.readFile(e, "utf-8");
124
+ return await a.readFile(e, "utf-8");
136
125
  } catch (e) {
137
126
  if (e.code === "ENOENT") {
138
127
  if (r < t) {
139
- console.log(`⏳ index.html not found, waiting ${n}ms before retry (attempt ${r}/${t})...`), await T(n);
128
+ console.log(`⏳ index.html not found, waiting ${n}ms before retry (attempt ${r}/${t})...`), await S(n);
140
129
  continue;
141
130
  }
142
131
  return console.log(`⚠️ index.html not found after ${t} attempts, skipping preload injection.`), null;
@@ -145,7 +134,7 @@ async function E(e, t = C, n = w) {
145
134
  }
146
135
  return null;
147
136
  }
148
- function D(e = {}) {
137
+ function w(e = {}) {
149
138
  let t = {
150
139
  extensions: [
151
140
  ".png",
@@ -170,12 +159,12 @@ function D(e = {}) {
170
159
  enforce: "post",
171
160
  async writeBundle() {
172
161
  try {
173
- let e = await h(t.srcDir, t.extensions, t.deepScan);
174
- if (x(e), e.length === 0) return;
175
- let n = l.join(t.outDir, "index.html"), r = await E(n);
162
+ let e = await f(t.srcDir, t.extensions, t.deepScan);
163
+ if (v(e), e.length === 0) return;
164
+ let n = o.join(t.outDir, "index.html"), r = await C(n);
176
165
  if (r === null) return;
177
- let i = b(r, y(e));
178
- await c.writeFile(n, i, "utf-8"), S(e.length);
166
+ let i = _(r, g(e));
167
+ await a.writeFile(n, i, "utf-8"), y(e.length);
179
168
  } catch (e) {
180
169
  console.error("❌ Error in aippy preload plugin:", e);
181
170
  }
@@ -183,4 +172,4 @@ function D(e = {}) {
183
172
  };
184
173
  }
185
174
  //#endregion
186
- export { D as t };
175
+ export { w as t };
@@ -1,6 +1,33 @@
1
1
  import { AssetInfo } from './types';
2
2
  /** A flat dictionary is a v1 plan only when schemaVersion is a number and assets is an object. */
3
3
  export declare function isSingleFilePlan(value: unknown): boolean;
4
+ export declare const ASSET_TOKEN_PREFIX = "aippy-asset:";
5
+ /**
6
+ * Mirrors aippy-chat `assetmanifest.ValidReference`: every value the server
7
+ * puts in the inventory as a usable url must be accepted here, including
8
+ * root-relative and relative paths.
9
+ */
10
+ export declare function isDictionaryResourceURL(value: string): boolean;
11
+ /** Keys that own each usable dictionary URL. A shared URL lists every key. */
12
+ export declare function dictionaryResourceOwners(flat: Record<string, unknown>): Map<string, string[]>;
13
+ /** Percent-encode a dictionary key so `:` and `$` cannot be read as separators. */
14
+ export declare function encodeAssetKey(key: string): string;
15
+ /**
16
+ * `aippy-asset:<encodeURIComponent(key)>:<url>`.
17
+ * The key ends at the first raw colon. `hero:mobile` must be `hero%3Amobile`;
18
+ * splitting that unencoded form would name `hero` and is rejected because the
19
+ * remainder `mobile:https://...` is not a resource reference.
20
+ */
21
+ export declare function parseAssetToken(value: string): {
22
+ key: string;
23
+ url: string;
24
+ } | undefined;
25
+ /**
26
+ * Drop the `aippy-asset:<key>:` prefix from any token the key rewrite did not
27
+ * reach (template literals, CSS, HTML, unknown keys), leaving the url itself.
28
+ * A literal token is never a loadable resource.
29
+ */
30
+ export declare function stripAssetTokens(code: string): string;
4
31
  /**
5
32
  * Write the flat dictionary. Existing keys and non-string values stay; scanned
6
33
  * resources only add a key when that URL is not already present.