@omg-dev/vite-plugin 0.4.30 → 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 +12 -17
- package/package.json +3 -3
- package/src/brand-badge-inject.test.ts +1 -12
- package/src/index.ts +1 -38
- package/src/feedback-inject.test.ts +0 -79
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;
|
|
@@ -464,8 +466,6 @@ function vibes(opts = {}) {
|
|
|
464
466
|
const pwaEnabled = opts.pwa !== false;
|
|
465
467
|
const pwaOpts = typeof opts.pwa === "object" ? opts.pwa : {};
|
|
466
468
|
let pwaAutoInjected = false;
|
|
467
|
-
const feedbackEnabled = opts.feedback !== false;
|
|
468
|
-
let feedbackInjected = false;
|
|
469
469
|
const brandBadgeEnabled = opts.brandBadge !== false;
|
|
470
470
|
let brandBadgeInjected = false;
|
|
471
471
|
function existingIconFiles() {
|
|
@@ -484,10 +484,7 @@ function vibes(opts = {}) {
|
|
|
484
484
|
name: "vibes",
|
|
485
485
|
enforce: "pre",
|
|
486
486
|
config() {
|
|
487
|
-
return {
|
|
488
|
-
define: { __VIBES_BRAND_FEEDBACK__: JSON.stringify(brandBadgeEnabled && feedbackEnabled) },
|
|
489
|
-
server: { watch: { ignored: ["**/.vibes/**"] } }
|
|
490
|
-
};
|
|
487
|
+
return { server: { watch: { ignored: ["**/.vibes/**"] } } };
|
|
491
488
|
},
|
|
492
489
|
configResolved(resolvedConfig) {
|
|
493
490
|
config = resolvedConfig;
|
|
@@ -771,7 +768,8 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
|
|
|
771
768
|
if (bearerProto) {
|
|
772
769
|
const token = bearerProto.slice(13);
|
|
773
770
|
try {
|
|
774
|
-
|
|
771
|
+
const fakeReq = new Request("http://localhost/__vibes_sub", { headers: { authorization: `Bearer ${token}` } });
|
|
772
|
+
userId = (await subAuthMW(fakeReq))?.userId ?? null;
|
|
775
773
|
} catch {}
|
|
776
774
|
}
|
|
777
775
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
@@ -786,7 +784,8 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
|
|
|
786
784
|
};
|
|
787
785
|
addSubClient(subClient);
|
|
788
786
|
ws.on("message", (data) => {
|
|
789
|
-
|
|
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);
|
|
790
789
|
});
|
|
791
790
|
const closeOut = () => {
|
|
792
791
|
if (subClient.readyState === 3) return;
|
|
@@ -990,7 +989,7 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
|
|
|
990
989
|
},
|
|
991
990
|
async transform(code, id) {
|
|
992
991
|
if (config?.command !== "build") return null;
|
|
993
|
-
if (!pwaEnabled && !
|
|
992
|
+
if (!pwaEnabled && !brandBadgeEnabled) return null;
|
|
994
993
|
if (!isAppEntry(id, resolvedRoot)) return null;
|
|
995
994
|
const imports = [];
|
|
996
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 }).");
|
|
@@ -998,11 +997,6 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
|
|
|
998
997
|
brandBadgeInjected = true;
|
|
999
998
|
imports.push(`import "@omg-dev/sdk/brand/auto";`);
|
|
1000
999
|
}
|
|
1001
|
-
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 }).");
|
|
1002
|
-
else {
|
|
1003
|
-
feedbackInjected = true;
|
|
1004
|
-
imports.push(`import "@omg-dev/sdk/feedback/auto";`);
|
|
1005
|
-
}
|
|
1006
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 } }).");
|
|
1007
1001
|
else {
|
|
1008
1002
|
pwaAutoInjected = true;
|
|
@@ -1018,7 +1012,8 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
|
|
|
1018
1012
|
if (!pwaEnabled) return;
|
|
1019
1013
|
if (userManifestHref()) return;
|
|
1020
1014
|
const indexPath = path.join(resolvedRoot, "index.html");
|
|
1021
|
-
const
|
|
1015
|
+
const indexHtml = fs.existsSync(indexPath) ? fs.readFileSync(indexPath, "utf-8") : "";
|
|
1016
|
+
const cfg = resolvePwaConfig(pwaOpts, indexHtml);
|
|
1022
1017
|
this.emitFile({
|
|
1023
1018
|
type: "asset",
|
|
1024
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.
|
|
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.
|
|
19
|
-
"@omg-dev/schema": "0.4.
|
|
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,
|
|
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,17 +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: no standalone feedback import injected", async () => {
|
|
75
|
-
// brand badge default ON, with feedback + pwa also on. The badge carries
|
|
76
|
-
// feedback (button-less), so standalone feedback/auto must not be appended.
|
|
77
|
-
const plugin = vibes({ root: ROOT })
|
|
78
|
-
const configResolved = plugin.configResolved as (cfg: unknown) => void
|
|
79
|
-
configResolved({ command: "build", root: ROOT })
|
|
80
|
-
const out = await runTransform(plugin, "export default 1", ENTRY, ctx())
|
|
81
|
-
expect(out?.code).toContain(BRAND_IMPORT)
|
|
82
|
-
expect(out?.code).not.toContain(`import "@omg-dev/sdk/feedback/auto";`)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
74
|
test("serve mode never injects the badge", async () => {
|
|
86
75
|
const plugin = vibes({ root: ROOT })
|
|
87
76
|
const configResolved = plugin.configResolved as (cfg: unknown) => void
|
package/src/index.ts
CHANGED
|
@@ -34,12 +34,6 @@ 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. */
|
|
@@ -57,8 +51,6 @@ export default function vibes(opts: VibesOptions = {}): Plugin {
|
|
|
57
51
|
const pwaOpts: PwaOptions = typeof opts.pwa === "object" ? opts.pwa : {}
|
|
58
52
|
let pwaAutoInjected = false
|
|
59
53
|
|
|
60
|
-
const feedbackEnabled = opts.feedback !== false
|
|
61
|
-
let feedbackInjected = false
|
|
62
54
|
const brandBadgeEnabled = opts.brandBadge !== false
|
|
63
55
|
let brandBadgeInjected = false
|
|
64
56
|
|
|
@@ -95,13 +87,6 @@ export default function vibes(opts: VibesOptions = {}): Plugin {
|
|
|
95
87
|
// browser, so blanket-ignore it.
|
|
96
88
|
config() {
|
|
97
89
|
return {
|
|
98
|
-
// When the brand badge is on (default), it bundles the feedback sheet
|
|
99
|
-
// (no separate floating button). Expose whether feedback is actually
|
|
100
|
-
// enabled so the badge can omit it when the app opts out via
|
|
101
|
-
// vibes({ feedback: false }).
|
|
102
|
-
define: {
|
|
103
|
-
__VIBES_BRAND_FEEDBACK__: JSON.stringify(brandBadgeEnabled && feedbackEnabled),
|
|
104
|
-
},
|
|
105
90
|
server: {
|
|
106
91
|
watch: {
|
|
107
92
|
ignored: ["**/.vibes/**"],
|
|
@@ -777,7 +762,7 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
|
|
|
777
762
|
// of main.tsx and never load in dev preview.
|
|
778
763
|
async transform(code: string, id: string) {
|
|
779
764
|
if (config?.command !== "build") return null
|
|
780
|
-
if (!pwaEnabled && !
|
|
765
|
+
if (!pwaEnabled && !brandBadgeEnabled) return null
|
|
781
766
|
if (!isAppEntry(id, resolvedRoot)) return null
|
|
782
767
|
|
|
783
768
|
const imports: string[] = []
|
|
@@ -798,28 +783,6 @@ window.__VIBES_AUTH_SESSION_URL = "/__vibes/auth/session";
|
|
|
798
783
|
}
|
|
799
784
|
}
|
|
800
785
|
|
|
801
|
-
// Shake-to-report feedback widget — opt-out via vibes({ feedback: false }).
|
|
802
|
-
// Build-only + outside the React tree, so it survives agent rewrites of
|
|
803
|
-
// main.tsx and never arms a gesture listener in the dev preview iframe.
|
|
804
|
-
//
|
|
805
|
-
// When the brand badge is on (default) it ALREADY mounts a button-less
|
|
806
|
-
// feedback sheet and summons it from its dialog, so injecting the
|
|
807
|
-
// standalone widget here would double-mount it (two floating controls —
|
|
808
|
-
// the exact duplication we're collapsing). Only inject standalone feedback
|
|
809
|
-
// when the brand badge is disabled.
|
|
810
|
-
if (feedbackEnabled && !brandBadgeEnabled && !feedbackInjected) {
|
|
811
|
-
const resolvedFb = await this.resolve("@omg-dev/sdk/feedback/auto", id)
|
|
812
|
-
if (!resolvedFb) {
|
|
813
|
-
this.warn(
|
|
814
|
-
"[vibes] feedback: @omg-dev/sdk is not installed — shake-to-report skipped. " +
|
|
815
|
-
"Add @omg-dev/sdk to dependencies, or silence this with vibes({ feedback: false }).",
|
|
816
|
-
)
|
|
817
|
-
} else {
|
|
818
|
-
feedbackInjected = true
|
|
819
|
-
imports.push(`import "@omg-dev/sdk/feedback/auto";`)
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
|
|
823
786
|
// Soft install prompt — opt-out via vibes({ pwa: { autoPrompt: false } }).
|
|
824
787
|
if (pwaEnabled && pwaOpts.autoPrompt !== false && !pwaAutoInjected) {
|
|
825
788
|
const resolved = await this.resolve("@omg-dev/pwa/auto", id)
|
|
@@ -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
|
-
})
|