@omg-dev/vite-plugin 0.4.29 → 0.4.31

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/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import fs from "node:fs";
3
3
  import { schemaToCollections, schemaToDrizzle, schemaToZod } from "@omg-dev/schema";
4
4
  import { scanTriggers } from "@omg-dev/server/trigger-scan";
5
5
  //#region src/auth-bridge.ts
6
- const AUTH_RELAY_SKIP_HEADERS = new Set([
6
+ const AUTH_RELAY_SKIP_HEADERS = /* @__PURE__ */ new Set([
7
7
  "set-cookie",
8
8
  "content-encoding",
9
9
  "content-length",
@@ -48,7 +48,7 @@ async function fetchAuthUpstream(kind, init, qs = "") {
48
48
  }
49
49
  //#endregion
50
50
  //#region src/scanner.ts
51
- const HTTP_METHODS = new Set([
51
+ const HTTP_METHODS = /* @__PURE__ */ new Set([
52
52
  "GET",
53
53
  "POST",
54
54
  "PUT",
@@ -180,6 +180,8 @@ function extractExports(source) {
180
180
  const EXPORT_WORKFLOW_RE = /^export\s+const\s+(\w+)\s*(?::\s*[^=]+)?\s*=\s*workflow\s*\(\s*(.+?)\s*,/gm;
181
181
  const NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
182
182
  var WorkflowScanError = class extends Error {
183
+ file;
184
+ detail;
183
185
  constructor(file, detail) {
184
186
  super(`[vibes:workflows] ${file}: ${detail}`);
185
187
  this.file = file;
@@ -457,29 +459,6 @@ function isAppEntry(id, root) {
457
459
  }
458
460
  //#endregion
459
461
  //#region src/index.ts
460
- async function pwaDeclaresSubpathExport(resolve, importer, subpath) {
461
- try {
462
- const rootEntry = await resolve("@omg-dev/pwa", importer);
463
- if (!rootEntry?.id) return false;
464
- let dir = path.dirname(rootEntry.id.split("?")[0]);
465
- for (let i = 0; i < 12; i++) {
466
- const pkgPath = path.join(dir, "package.json");
467
- if (fs.existsSync(pkgPath)) try {
468
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
469
- if (pkg.name === "@omg-dev/pwa") {
470
- const exp = pkg.exports;
471
- return !!exp && typeof exp === "object" && subpath in exp;
472
- }
473
- } catch {}
474
- const parent = path.dirname(dir);
475
- if (parent === dir) break;
476
- dir = parent;
477
- }
478
- return false;
479
- } catch {
480
- return false;
481
- }
482
- }
483
462
  function vibes(opts = {}) {
484
463
  let resolvedRoot;
485
464
  let config;
@@ -487,9 +466,6 @@ function vibes(opts = {}) {
487
466
  const pwaEnabled = opts.pwa !== false;
488
467
  const pwaOpts = typeof opts.pwa === "object" ? opts.pwa : {};
489
468
  let pwaAutoInjected = false;
490
- let remixCtaInjected = false;
491
- const feedbackEnabled = opts.feedback !== false;
492
- let feedbackInjected = false;
493
469
  const brandBadgeEnabled = opts.brandBadge !== false;
494
470
  let brandBadgeInjected = false;
495
471
  function existingIconFiles() {
@@ -508,10 +484,7 @@ function vibes(opts = {}) {
508
484
  name: "vibes",
509
485
  enforce: "pre",
510
486
  config() {
511
- return {
512
- define: { __VIBES_BRAND_FEEDBACK__: JSON.stringify(brandBadgeEnabled && feedbackEnabled) },
513
- server: { watch: { ignored: ["**/.vibes/**"] } }
514
- };
487
+ return { server: { watch: { ignored: ["**/.vibes/**"] } } };
515
488
  },
516
489
  configResolved(resolvedConfig) {
517
490
  config = resolvedConfig;
@@ -795,7 +768,8 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
795
768
  if (bearerProto) {
796
769
  const token = bearerProto.slice(13);
797
770
  try {
798
- userId = (await subAuthMW(new Request("http://localhost/__vibes_sub", { headers: { authorization: `Bearer ${token}` } })))?.userId ?? null;
771
+ const fakeReq = new Request("http://localhost/__vibes_sub", { headers: { authorization: `Bearer ${token}` } });
772
+ userId = (await subAuthMW(fakeReq))?.userId ?? null;
799
773
  } catch {}
800
774
  }
801
775
  wss.handleUpgrade(req, socket, head, (ws) => {
@@ -810,7 +784,8 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
810
784
  };
811
785
  addSubClient(subClient);
812
786
  ws.on("message", (data) => {
813
- handleSubMessage(subClient, typeof data === "string" ? data : Buffer.isBuffer(data) ? data.toString("utf8") : Array.isArray(data) ? Buffer.concat(data).toString("utf8") : Buffer.from(data).toString("utf8"));
787
+ const raw = typeof data === "string" ? data : Buffer.isBuffer(data) ? data.toString("utf8") : Array.isArray(data) ? Buffer.concat(data).toString("utf8") : Buffer.from(data).toString("utf8");
788
+ handleSubMessage(subClient, raw);
814
789
  });
815
790
  const closeOut = () => {
816
791
  if (subClient.readyState === 3) return;
@@ -1014,7 +989,7 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
1014
989
  },
1015
990
  async transform(code, id) {
1016
991
  if (config?.command !== "build") return null;
1017
- if (!pwaEnabled && !feedbackEnabled && !brandBadgeEnabled) return null;
992
+ if (!pwaEnabled && !brandBadgeEnabled) return null;
1018
993
  if (!isAppEntry(id, resolvedRoot)) return null;
1019
994
  const imports = [];
1020
995
  if (brandBadgeEnabled && !brandBadgeInjected) if (!await this.resolve("@omg-dev/sdk/brand/auto", id)) this.warn("[vibes] brand badge: @omg-dev/sdk/brand/auto is not available — badge skipped. Update @omg-dev/sdk, or silence this with vibes({ brandBadge: false }).");
@@ -1022,22 +997,11 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
1022
997
  brandBadgeInjected = true;
1023
998
  imports.push(`import "@omg-dev/sdk/brand/auto";`);
1024
999
  }
1025
- if (feedbackEnabled && !brandBadgeEnabled && !feedbackInjected) if (!await this.resolve("@omg-dev/sdk/feedback/auto", id)) this.warn("[vibes] feedback: @omg-dev/sdk is not installed — shake-to-report skipped. Add @omg-dev/sdk to dependencies, or silence this with vibes({ feedback: false }).");
1026
- else {
1027
- feedbackInjected = true;
1028
- imports.push(`import "@omg-dev/sdk/feedback/auto";`);
1029
- }
1030
1000
  if (pwaEnabled && pwaOpts.autoPrompt !== false && !pwaAutoInjected) if (!await this.resolve("@omg-dev/pwa/auto", id)) this.warn("[vibes] pwa: @omg-dev/pwa is not installed — soft install prompt skipped. Add @omg-dev/pwa to dependencies, or silence this with vibes({ pwa: { autoPrompt: false } }).");
1031
1001
  else {
1032
1002
  pwaAutoInjected = true;
1033
1003
  imports.push(`import "@omg-dev/pwa/auto";`);
1034
1004
  }
1035
- if (pwaEnabled && !brandBadgeEnabled && !remixCtaInjected) {
1036
- if (await pwaDeclaresSubpathExport((source, importer) => this.resolve(source, importer), id, "./remix-cta")) {
1037
- remixCtaInjected = true;
1038
- imports.push(`import "@omg-dev/pwa/remix-cta";`);
1039
- }
1040
- }
1041
1005
  if (imports.length === 0) return null;
1042
1006
  return {
1043
1007
  code: `${code}\n${imports.join("\n")}\n`,
@@ -1048,7 +1012,8 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
1048
1012
  if (!pwaEnabled) return;
1049
1013
  if (userManifestHref()) return;
1050
1014
  const indexPath = path.join(resolvedRoot, "index.html");
1051
- const cfg = resolvePwaConfig(pwaOpts, fs.existsSync(indexPath) ? fs.readFileSync(indexPath, "utf-8") : "");
1015
+ const indexHtml = fs.existsSync(indexPath) ? fs.readFileSync(indexPath, "utf-8") : "";
1016
+ const cfg = resolvePwaConfig(pwaOpts, indexHtml);
1052
1017
  this.emitFile({
1053
1018
  type: "asset",
1054
1019
  fileName: MANIFEST_FILE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omg-dev/vite-plugin",
3
- "version": "0.4.29",
3
+ "version": "0.4.31",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -15,8 +15,8 @@
15
15
  "build": "vp pack src/index.ts --no-fail-on-warn"
16
16
  },
17
17
  "dependencies": {
18
- "@omg-dev/server": "0.4.29",
19
- "@omg-dev/schema": "0.4.29",
18
+ "@omg-dev/server": "0.4.31",
19
+ "@omg-dev/schema": "0.4.31",
20
20
  "ws": "^8.18.0"
21
21
  },
22
22
  "devDependencies": {
@@ -29,7 +29,7 @@ async function runTransform(
29
29
  }
30
30
 
31
31
  function buildPlugin(opts?: Parameters<typeof vibes>[0]) {
32
- const plugin = vibes({ root: ROOT, pwa: false, feedback: false, ...opts })
32
+ const plugin = vibes({ root: ROOT, pwa: false, ...opts })
33
33
  const configResolved = plugin.configResolved as (cfg: unknown) => void
34
34
  configResolved({ command: "build", root: ROOT })
35
35
  return plugin
@@ -71,19 +71,6 @@ describe("brand badge injection", () => {
71
71
  expect(context.warnings.some((w) => w.includes("brand badge"))).toBe(true)
72
72
  })
73
73
 
74
- test("badge ON bundles feedback + remix: no standalone imports injected", async () => {
75
- // brand badge default ON, with feedback + pwa also on. The badge carries
76
- // feedback (button-less) and the "Make it mine" CTA itself, so the
77
- // standalone feedback/auto + remix-cta imports must NOT be appended.
78
- const plugin = vibes({ root: ROOT })
79
- const configResolved = plugin.configResolved as (cfg: unknown) => void
80
- configResolved({ command: "build", root: ROOT })
81
- const out = await runTransform(plugin, "export default 1", ENTRY, ctx())
82
- expect(out?.code).toContain(BRAND_IMPORT)
83
- expect(out?.code).not.toContain(`import "@omg-dev/sdk/feedback/auto";`)
84
- expect(out?.code).not.toContain(`import "@omg-dev/pwa/remix-cta";`)
85
- })
86
-
87
74
  test("serve mode never injects the badge", async () => {
88
75
  const plugin = vibes({ root: ROOT })
89
76
  const configResolved = plugin.configResolved as (cfg: unknown) => void
package/src/index.ts CHANGED
@@ -34,59 +34,12 @@ export interface VibesOptions {
34
34
  * install prompt. Default ON; pass `false` to opt out, or an options
35
35
  * object to customize (see PwaOptions). */
36
36
  pwa?: boolean | PwaOptions
37
- /** Shake-to-report feedback widget for published builds — auto-mounts
38
- * <VibesFeedback/> outside the app's React tree (shake / two-finger press
39
- * summons it; report → agent /_report → LFG triage). Default ON; pass
40
- * `false` to opt out and place <VibesFeedback/> from @omg-dev/sdk/feedback
41
- * manually. */
42
- feedback?: boolean
43
37
  /** Small "omg" badge for published apps. Auto-mounts outside the app's React
44
38
  * tree and opens a built-in "What is omg?" dialog. Default ON; pass `false`
45
39
  * to omit it for a specific app while this rolls out. */
46
40
  brandBadge?: boolean
47
41
  }
48
42
 
49
- // Resolve @omg-dev/pwa for `importer` and report whether its package.json declares
50
- // the given subpath (e.g. "./remix-cta") in its `exports` map. Used to keep the
51
- // build-time CTA injection skew-safe: an older baked @omg-dev/pwa that predates a
52
- // subpath export must NOT get the import appended, or the bundle fails to resolve.
53
- // Returns false on any resolution / parse hiccup — the CTA is additive, so when
54
- // in doubt we skip it rather than risk breaking the user's build.
55
- async function pwaDeclaresSubpathExport(
56
- resolve: (source: string, importer: string) => Promise<{ id: string } | null>,
57
- importer: string,
58
- subpath: string,
59
- ): Promise<boolean> {
60
- try {
61
- // Resolve the always-present root export, then walk up to the owning
62
- // package.json. We can't resolve "@omg-dev/pwa/package.json" directly because
63
- // an older exports map may not expose "./package.json".
64
- const rootEntry = await resolve("@omg-dev/pwa", importer)
65
- if (!rootEntry?.id) return false
66
- let dir = path.dirname(rootEntry.id.split("?")[0])
67
- for (let i = 0; i < 12; i++) {
68
- const pkgPath = path.join(dir, "package.json")
69
- if (fs.existsSync(pkgPath)) {
70
- try {
71
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
72
- if (pkg.name === "@omg-dev/pwa") {
73
- const exp = pkg.exports
74
- return !!exp && typeof exp === "object" && subpath in exp
75
- }
76
- } catch {
77
- // Unreadable/!JSON package.json — keep climbing toward the real one.
78
- }
79
- }
80
- const parent = path.dirname(dir)
81
- if (parent === dir) break
82
- dir = parent
83
- }
84
- return false
85
- } catch {
86
- return false
87
- }
88
- }
89
-
90
43
  // ── Plugin ────────────────────────────────────────────────────────────────────
91
44
 
92
45
  export default function vibes(opts: VibesOptions = {}): Plugin {
@@ -97,10 +50,7 @@ export default function vibes(opts: VibesOptions = {}): Plugin {
97
50
  const pwaEnabled = opts.pwa !== false
98
51
  const pwaOpts: PwaOptions = typeof opts.pwa === "object" ? opts.pwa : {}
99
52
  let pwaAutoInjected = false
100
- let remixCtaInjected = false
101
53
 
102
- const feedbackEnabled = opts.feedback !== false
103
- let feedbackInjected = false
104
54
  const brandBadgeEnabled = opts.brandBadge !== false
105
55
  let brandBadgeInjected = false
106
56
 
@@ -137,13 +87,6 @@ export default function vibes(opts: VibesOptions = {}): Plugin {
137
87
  // browser, so blanket-ignore it.
138
88
  config() {
139
89
  return {
140
- // When the brand badge is on (default), it bundles the feedback sheet
141
- // (no separate floating button). Expose whether feedback is actually
142
- // enabled so the badge can omit it when the app opts out via
143
- // vibes({ feedback: false }).
144
- define: {
145
- __VIBES_BRAND_FEEDBACK__: JSON.stringify(brandBadgeEnabled && feedbackEnabled),
146
- },
147
90
  server: {
148
91
  watch: {
149
92
  ignored: ["**/.vibes/**"],
@@ -813,16 +756,13 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
813
756
  },
814
757
 
815
758
  // ── transform ─────────────────────────────────────────────────────────────
816
- // Published builds get two side-effect imports appended to the app entry:
759
+ // Published builds get platform side-effect imports appended to the app entry:
817
760
  // - @omg-dev/pwa/auto — the soft install prompt (gated by autoPrompt)
818
- // - @omg-dev/pwa/remix-cta — the "Make your own" remix bridge (always on
819
- // when pwa is enabled; self-gates on the baked
820
- // VITE_APP_SLUG, so it's inert if not published)
821
761
  // Build-time injection (not template source) so both survive agent rewrites
822
762
  // of main.tsx and never load in dev preview.
823
763
  async transform(code: string, id: string) {
824
764
  if (config?.command !== "build") return null
825
- if (!pwaEnabled && !feedbackEnabled && !brandBadgeEnabled) return null
765
+ if (!pwaEnabled && !brandBadgeEnabled) return null
826
766
  if (!isAppEntry(id, resolvedRoot)) return null
827
767
 
828
768
  const imports: string[] = []
@@ -843,28 +783,6 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
843
783
  }
844
784
  }
845
785
 
846
- // Shake-to-report feedback widget — opt-out via vibes({ feedback: false }).
847
- // Build-only + outside the React tree, so it survives agent rewrites of
848
- // main.tsx and never arms a gesture listener in the dev preview iframe.
849
- //
850
- // When the brand badge is on (default) it ALREADY mounts a button-less
851
- // feedback sheet and summons it from its dialog, so injecting the
852
- // standalone widget here would double-mount it (two floating controls —
853
- // the exact duplication we're collapsing). Only inject standalone feedback
854
- // when the brand badge is disabled.
855
- if (feedbackEnabled && !brandBadgeEnabled && !feedbackInjected) {
856
- const resolvedFb = await this.resolve("@omg-dev/sdk/feedback/auto", id)
857
- if (!resolvedFb) {
858
- this.warn(
859
- "[vibes] feedback: @omg-dev/sdk is not installed — shake-to-report skipped. " +
860
- "Add @omg-dev/sdk to dependencies, or silence this with vibes({ feedback: false }).",
861
- )
862
- } else {
863
- feedbackInjected = true
864
- imports.push(`import "@omg-dev/sdk/feedback/auto";`)
865
- }
866
- }
867
-
868
786
  // Soft install prompt — opt-out via vibes({ pwa: { autoPrompt: false } }).
869
787
  if (pwaEnabled && pwaOpts.autoPrompt !== false && !pwaAutoInjected) {
870
788
  const resolved = await this.resolve("@omg-dev/pwa/auto", id)
@@ -879,33 +797,6 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
879
797
  }
880
798
  }
881
799
 
882
- // "Make your own" remix CTA — independent of autoPrompt. It self-gates at
883
- // runtime on import.meta.env.VITE_APP_SLUG (baked by the orchestrator on
884
- // deploy), so it renders nothing on a non-published build.
885
- //
886
- // Skew guard: the remix-cta subpath shipped in @omg-dev/pwa >= 0.4.16. An app
887
- // whose baked @omg-dev/pwa predates it (e.g. 0.4.12) has no "./remix-cta" in
888
- // its exports map, so injecting the import hard-fails the build with
889
- // "Errored while resolving @omg-dev/pwa/remix-cta". this.resolve() alone is not
890
- // a reliable gate across this version skew, so only inject once we've
891
- // confirmed the resolved package actually declares the subpath export.
892
- //
893
- // The brand badge (default on) now hosts "Make it mine" itself: same omg
894
- // mark, remix CTA first, and it collapses to the plain branding badge on
895
- // dismiss. So only inject the standalone remix-cta when the brand badge is
896
- // off — otherwise we'd render two corner pills with the same intent.
897
- if (pwaEnabled && !brandBadgeEnabled && !remixCtaInjected) {
898
- const exported = await pwaDeclaresSubpathExport(
899
- (source, importer) => this.resolve(source, importer),
900
- id,
901
- "./remix-cta",
902
- )
903
- if (exported) {
904
- remixCtaInjected = true
905
- imports.push(`import "@omg-dev/pwa/remix-cta";`)
906
- }
907
- }
908
-
909
800
  if (imports.length === 0) return null
910
801
  return { code: `${code}\n${imports.join("\n")}\n`, map: null }
911
802
  },
@@ -1,79 +0,0 @@
1
- // Verifies the build-time injection of the shake-to-report feedback widget
2
- // (@omg-dev/sdk/feedback/auto) into the app entry. The brand badge bundles
3
- // feedback when it's on (the default), so the STANDALONE widget only injects
4
- // when the brand badge is disabled — these tests run with brandBadge:false.
5
- // Opt-out via vibes({ feedback: false }), build-only, app-entry-only.
6
-
7
- import { describe, expect, test } from "bun:test"
8
- import vibes from "./index.ts"
9
-
10
- const ROOT = "/app"
11
- const ENTRY = `${ROOT}/src/main.tsx`
12
- const FEEDBACK_IMPORT = `import "@omg-dev/sdk/feedback/auto";`
13
-
14
- // Minimal Rollup plugin-context stub: resolve() succeeds for everything,
15
- // warn() is captured. transform() is a plain async fn we bind `this` onto.
16
- function ctx() {
17
- const warnings: string[] = []
18
- return {
19
- warnings,
20
- resolve: async (src: string) => ({ id: src }),
21
- warn: (msg: string) => warnings.push(msg),
22
- }
23
- }
24
-
25
- async function runTransform(
26
- plugin: ReturnType<typeof vibes>,
27
- code: string,
28
- id: string,
29
- context: ReturnType<typeof ctx>,
30
- ) {
31
- const fn = plugin.transform as (this: unknown, code: string, id: string) => Promise<unknown>
32
- return fn.call(context, code, id) as Promise<{ code: string } | null>
33
- }
34
-
35
- function buildPlugin(opts?: Parameters<typeof vibes>[0]) {
36
- const plugin = vibes({ root: ROOT, brandBadge: false, ...opts })
37
- // configResolved sets command + root that transform gates on.
38
- const configResolved = plugin.configResolved as (cfg: unknown) => void
39
- configResolved({ command: "build", root: ROOT })
40
- return plugin
41
- }
42
-
43
- describe("feedback widget injection", () => {
44
- test("default ON: appends the auto-mount import to the app entry", async () => {
45
- const plugin = buildPlugin()
46
- const out = await runTransform(plugin, "export default 1", ENTRY, ctx())
47
- expect(out?.code).toContain(FEEDBACK_IMPORT)
48
- })
49
-
50
- test("opt-out via feedback:false omits the import", async () => {
51
- const plugin = buildPlugin({ feedback: false, pwa: false })
52
- const out = await runTransform(plugin, "export default 1", ENTRY, ctx())
53
- // Nothing to inject → transform returns null.
54
- expect(out).toBeNull()
55
- })
56
-
57
- test("injected exactly once across multiple entry transforms", async () => {
58
- const plugin = buildPlugin({ pwa: false })
59
- const first = await runTransform(plugin, "export default 1", ENTRY, ctx())
60
- const second = await runTransform(plugin, "export default 2", ENTRY, ctx())
61
- expect(first?.code).toContain(FEEDBACK_IMPORT)
62
- expect(second).toBeNull() // already injected
63
- })
64
-
65
- test("non-entry modules are untouched", async () => {
66
- const plugin = buildPlugin()
67
- const out = await runTransform(plugin, "export const x = 1", `${ROOT}/src/util.ts`, ctx())
68
- expect(out).toBeNull()
69
- })
70
-
71
- test("warns (and skips) when @omg-dev/sdk cannot be resolved", async () => {
72
- const plugin = buildPlugin({ pwa: false })
73
- const context = ctx()
74
- context.resolve = async () => null // simulate missing dependency
75
- const out = await runTransform(plugin, "export default 1", ENTRY, context)
76
- expect(out).toBeNull()
77
- expect(context.warnings.some((w) => w.includes("shake-to-report skipped"))).toBe(true)
78
- })
79
- })