@01.works/visual-review 0.1.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.
package/dist/vite.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { Plugin } from 'vite';
2
+
3
+ export interface VisualReviewBuildIdentityOptions {
4
+ /** Browser-visible deployment-unique build identifier. Never reuse it for different output. */
5
+ buildId?: string;
6
+ /** Browser-visible full Git SHA. `null` explicitly disables automatic discovery. */
7
+ gitCommit?: string | null;
8
+ }
9
+
10
+ export interface VisualReviewViteSourceMapOptions {
11
+ /** Enables upload explicitly. A source-map upload token is still required. */
12
+ enabled?: boolean;
13
+ /** Node-only build secret. Prefer VISUAL_REVIEW_SOURCE_MAP_TOKEN in CI. */
14
+ token?: string;
15
+ /** Hosted service origin. Primarily useful for local/self-hosted build services. */
16
+ serviceUrl?: string;
17
+ }
18
+
19
+ export interface VisualReviewViteOptions extends VisualReviewBuildIdentityOptions {
20
+ /** Uploads private hidden source maps and removes them from the deployment output. */
21
+ sourceMaps?: boolean | VisualReviewViteSourceMapOptions;
22
+ /** Adds opaque production JSX markers by default. Set `false` for identity-only output. */
23
+ sourceMetadata?: boolean;
24
+ }
25
+
26
+ /** Injects public build provenance and optionally uploads private hidden source maps. */
27
+ export declare function visualReview(
28
+ options?: VisualReviewViteOptions,
29
+ ): Plugin;
package/dist/vite.js ADDED
@@ -0,0 +1,141 @@
1
+ import { t as resolveVisualReviewBuildIdentity } from "./build-identity-CgoY3-jy.js";
2
+ import { a as visualReviewSourceIndexFilename, f as cleanupViteSourceMapOutput, l as writeViteVisualReviewSourceIndex, p as uploadViteSourceMaps, s as viteSourceIndexUrl } from "./source-index-BdWeiNHZ.js";
3
+ import { t as instrumentVisualReviewSource } from "./source-instrumentation-transform-BwKYKlp0.js";
4
+ import { randomBytes } from "node:crypto";
5
+ import { resolve } from "node:path";
6
+ //#region src/vite.ts
7
+ const BUILD_ID_LITERAL = "process.env.VISUAL_REVIEW_PUBLIC_BUILD_ID";
8
+ const GIT_COMMIT_LITERAL = "process.env.VISUAL_REVIEW_PUBLIC_GIT_COMMIT";
9
+ const BUILD_MODE_LITERAL = "process.env.VISUAL_REVIEW_PUBLIC_BUILD_MODE";
10
+ /** Injects public build provenance and optionally uploads private hidden source maps. */
11
+ function visualReview(options = {}) {
12
+ let identity;
13
+ let resolvedConfig;
14
+ let sourceMapUpload;
15
+ let sourceMetadataEnabled = false;
16
+ let sourceIndexFilename;
17
+ let sourceIndexUrl;
18
+ let sourceMarkerSalt;
19
+ let releaseMode;
20
+ return {
21
+ name: "01works:visual-review-build-identity",
22
+ enforce: "pre",
23
+ config(config, environment) {
24
+ identity = resolveVisualReviewBuildIdentity(options, {
25
+ cwd: resolve(process.cwd(), config.root ?? "."),
26
+ env: process.env
27
+ });
28
+ releaseMode = typeof environment?.mode === "string" && environment.mode.length > 0 ? normalizeReleaseMode(environment.mode) : void 0;
29
+ const define = publicLiteralDefinitions(identity, releaseMode);
30
+ assertReservedLiterals(config.define, define, "Vite define");
31
+ if (environment?.command === "serve") {
32
+ sourceMapUpload = void 0;
33
+ sourceMetadataEnabled = false;
34
+ sourceMarkerSalt = void 0;
35
+ return { define };
36
+ }
37
+ sourceMapUpload = resolveSourceMapUpload(options.sourceMaps, process.env);
38
+ sourceMetadataEnabled = options.sourceMetadata ?? true;
39
+ sourceMarkerSalt = sourceMetadataEnabled ? randomBytes(32).toString("hex") : void 0;
40
+ if (!sourceMapUpload) return { define };
41
+ if (config.build?.sourcemap === "inline") throw new Error("Visual Review source-map upload refuses inline Vite source maps");
42
+ return {
43
+ define,
44
+ build: { sourcemap: "hidden" }
45
+ };
46
+ },
47
+ configResolved(config) {
48
+ resolvedConfig = config;
49
+ if (releaseMode !== void 0 && normalizeReleaseMode(config.mode) !== releaseMode) throw new Error("Visual Review build mode changed after the public literals were injected");
50
+ if (sourceMetadataEnabled) {
51
+ if (!identity || !sourceMarkerSalt) throw new Error("Visual Review source metadata identity is unavailable");
52
+ sourceIndexFilename = visualReviewSourceIndexFilename(identity.buildId, sourceMarkerSalt);
53
+ sourceIndexUrl = viteSourceIndexUrl(config.base, sourceIndexFilename);
54
+ }
55
+ if (!sourceMapUpload) return;
56
+ if (config.build.sourcemap !== "hidden") throw new Error("Visual Review source-map upload requires build.sourcemap=\"hidden\"");
57
+ if (config.build.ssr) throw new Error("Visual Review Vite source-map upload currently supports browser builds only");
58
+ },
59
+ transform(source, id) {
60
+ if (!sourceMetadataEnabled) return null;
61
+ if (!resolvedConfig || !sourceIndexUrl || !sourceMarkerSalt) throw new Error("Visual Review source metadata was not initialized by Vite");
62
+ const transformed = instrumentVisualReviewSource(source, {
63
+ id,
64
+ indexUrl: sourceIndexUrl,
65
+ markerSalt: sourceMarkerSalt,
66
+ projectRoot: resolvedConfig.root
67
+ });
68
+ return transformed ? {
69
+ code: transformed.code,
70
+ map: transformed.map
71
+ } : null;
72
+ },
73
+ async closeBundle() {
74
+ if (!identity || !resolvedConfig) throw new Error("Visual Review build adapter was not initialized by Vite");
75
+ const outDir = resolve(resolvedConfig.root, resolvedConfig.build.outDir);
76
+ if (sourceMetadataEnabled) {
77
+ if (!sourceIndexFilename) throw new Error("Visual Review source metadata was not initialized by Vite");
78
+ try {
79
+ await writeViteVisualReviewSourceIndex({
80
+ projectRoot: resolvedConfig.root,
81
+ outDir,
82
+ base: resolvedConfig.base,
83
+ filename: sourceIndexFilename
84
+ });
85
+ } catch (sourceIndexError) {
86
+ if (!sourceMapUpload) throw sourceIndexError;
87
+ try {
88
+ await cleanupViteSourceMapOutput(resolvedConfig.root, outDir);
89
+ } catch (cleanupError) {
90
+ throw new AggregateError([sourceIndexError, cleanupError], "Visual Review source index failed and Vite source maps could not be cleaned");
91
+ }
92
+ throw sourceIndexError;
93
+ }
94
+ }
95
+ if (!sourceMapUpload) return;
96
+ await uploadViteSourceMaps({
97
+ projectRoot: resolvedConfig.root,
98
+ outDir,
99
+ base: resolvedConfig.base,
100
+ token: sourceMapUpload.token,
101
+ ...sourceMapUpload.serviceUrl ? { serviceUrl: sourceMapUpload.serviceUrl } : {},
102
+ buildId: identity.buildId,
103
+ gitCommit: identity.gitCommit,
104
+ mode: normalizeReleaseMode(resolvedConfig.mode)
105
+ });
106
+ }
107
+ };
108
+ }
109
+ function resolveSourceMapUpload(option, env) {
110
+ if (option === false || typeof option === "object" && option.enabled === false) return;
111
+ const configured = typeof option === "object" ? option : void 0;
112
+ const token = configured?.token ?? env.VISUAL_REVIEW_SOURCE_MAP_TOKEN;
113
+ if (!(option === true || configured !== void 0 || token !== void 0)) return void 0;
114
+ if (typeof token !== "string" || token.length === 0 || /\s/.test(token)) throw new Error("Visual Review source-map upload requires VISUAL_REVIEW_SOURCE_MAP_TOKEN in the Node build environment");
115
+ return {
116
+ token,
117
+ ...configured?.serviceUrl ? { serviceUrl: configured.serviceUrl } : {}
118
+ };
119
+ }
120
+ function normalizeReleaseMode(mode) {
121
+ if (mode === "production") return "production";
122
+ if (mode === "development") return "development";
123
+ return "preview";
124
+ }
125
+ function publicLiteralDefinitions(identity, releaseMode) {
126
+ return {
127
+ [BUILD_ID_LITERAL]: JSON.stringify(identity.buildId),
128
+ [GIT_COMMIT_LITERAL]: JSON.stringify(identity.gitCommit ?? ""),
129
+ ...releaseMode ? { [BUILD_MODE_LITERAL]: JSON.stringify(releaseMode) } : {}
130
+ };
131
+ }
132
+ function assertReservedLiterals(current, expected, location) {
133
+ if (!current) return;
134
+ for (const key of [
135
+ BUILD_ID_LITERAL,
136
+ GIT_COMMIT_LITERAL,
137
+ BUILD_MODE_LITERAL
138
+ ]) if (Object.prototype.hasOwnProperty.call(current, key) && current[key] !== expected[key]) throw new Error(`${location} already defines reserved ${key}; configure it through visualReview() options`);
139
+ }
140
+ //#endregion
141
+ export { visualReview };
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "@01.works/visual-review",
3
+ "version": "0.1.0",
4
+ "description": "Invitation-only visual feedback for staging websites, hosted by 01.works.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/01works/visual-review.git",
8
+ "directory": "packages/review-client"
9
+ },
10
+ "license": "UNLICENSED",
11
+ "type": "module",
12
+ "sideEffects": false,
13
+ "files": [
14
+ "dist",
15
+ "LICENSE",
16
+ "README.md",
17
+ "THIRD_PARTY_NOTICES.md",
18
+ "LICENSES"
19
+ ],
20
+ "types": "./dist/index.d.ts",
21
+ "module": "./dist/index.js",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js",
26
+ "default": "./dist/index.js"
27
+ },
28
+ "./react": {
29
+ "types": "./dist/react.d.ts",
30
+ "import": "./dist/react.js",
31
+ "default": "./dist/react.js"
32
+ },
33
+ "./next": {
34
+ "types": "./dist/next.d.ts",
35
+ "import": "./dist/next.js",
36
+ "default": "./dist/next.js"
37
+ },
38
+ "./vite": {
39
+ "types": "./dist/vite.d.ts",
40
+ "import": "./dist/vite.js",
41
+ "default": "./dist/vite.js"
42
+ },
43
+ "./next-config": {
44
+ "types": "./dist/next-config.d.ts",
45
+ "import": "./dist/next-config.js",
46
+ "default": "./dist/next-config.js"
47
+ }
48
+ },
49
+ "scripts": {
50
+ "build": "tsdown --config tsdown.config.ts && tsdown --config tsdown.node.config.ts && node scripts/emit-public-types.mjs",
51
+ "prepack": "npm run build"
52
+ },
53
+ "engines": {
54
+ "node": ">=18.18"
55
+ },
56
+ "peerDependencies": {
57
+ "next": ">=15.5.0",
58
+ "react": ">=18.2.0",
59
+ "vite": ">=5.0.0"
60
+ },
61
+ "peerDependenciesMeta": {
62
+ "next": {
63
+ "optional": true
64
+ },
65
+ "vite": {
66
+ "optional": true
67
+ },
68
+ "react": {
69
+ "optional": true
70
+ }
71
+ },
72
+ "devDependencies": {
73
+ "@types/react": "^19.2.0",
74
+ "react": "^19.2.8",
75
+ "react-dom": "^19.2.8"
76
+ },
77
+ "publishConfig": {
78
+ "access": "public",
79
+ "registry": "https://registry.npmjs.org/"
80
+ },
81
+ "dependencies": {
82
+ "@babel/core": "^7.28.5",
83
+ "@babel/parser": "^7.28.5",
84
+ "@babel/plugin-transform-react-jsx": "^7.27.1",
85
+ "@babel/plugin-transform-typescript": "^7.28.5",
86
+ "@instantdb/core": "^1.0.52",
87
+ "magic-string": "^0.30.21"
88
+ }
89
+ }