@basementstudio/mitt-next-plugin 0.0.0-stage → 0.2.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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright (c) 2026 basement.studio. All rights reserved.
2
+
3
+ This software and its source code are proprietary and confidential to
4
+ basement.studio. No license, express or implied, is granted to use, copy,
5
+ modify, merge, publish, distribute, sublicense or sell this software, in
6
+ whole or in part, except under a separate written agreement with
7
+ basement.studio.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
12
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
13
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
14
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
15
+ SOFTWARE.
@@ -0,0 +1,44 @@
1
+ export declare const CONFIG_FILE = "mitt.config.json";
2
+ export declare const DEFAULT_SECRET_ENV = "MITT_SECRET";
3
+ /**
4
+ * Used whenever no secret is configured outside production builds. It is public by definition, so
5
+ * IDs made with it only hide paths from casual readers: real deployments must set their own.
6
+ */
7
+ export declare const DEV_SECRET = "mitt-dev-secret-not-for-production";
8
+ /**
9
+ * Short one-way digest of the secret. Loader options carry it instead of the secret because Next
10
+ * writes the whole config to `.next/required-server-files.json`; it still changes Turbopack's cache
11
+ * key whenever the secret changes.
12
+ */
13
+ export declare function secretFingerprint(secret: string): string;
14
+ export type Env = Readonly<Record<string, string | undefined>>;
15
+ export interface ResolvedSecret {
16
+ /** Null only when the variable is unset and the dev fallback isn't allowed. */
17
+ secret: string | null;
18
+ secretEnv: string;
19
+ /** The variable is unset and the public dev secret stands in. */
20
+ devFallback: boolean;
21
+ }
22
+ /**
23
+ * The one rule for which secret keys the IDs, shared by withMitt, the loader and the CLI so they
24
+ * can never disagree. An empty variable counts as unset.
25
+ */
26
+ export declare function resolveSecret(secretEnv: string, env: Env, options: {
27
+ allowDev: true;
28
+ }): ResolvedSecret & {
29
+ secret: string;
30
+ };
31
+ export declare function resolveSecret(secretEnv: string, env: Env, options: {
32
+ allowDev: boolean;
33
+ }): ResolvedSecret;
34
+ export interface MittConfig {
35
+ /** Name of the environment variable holding the HMAC secret. */
36
+ secretEnv: string;
37
+ /** The raw `"project"` value, unvalidated: only the CLI uses it, and validates it itself. */
38
+ project: unknown;
39
+ }
40
+ /**
41
+ * Reads `mitt.config.json` from the app root, the one place both the plugin and the CLI take their
42
+ * settings from so they always derive the same IDs. A missing file means defaults.
43
+ */
44
+ export declare function readMittConfig(appRoot: string): MittConfig;
package/dist/config.js ADDED
@@ -0,0 +1,63 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ export const CONFIG_FILE = 'mitt.config.json';
5
+ export const DEFAULT_SECRET_ENV = 'MITT_SECRET';
6
+ const DEFAULT_INCLUDE = '**/*.{tsx,jsx}';
7
+ /**
8
+ * Used whenever no secret is configured outside production builds. It is public by definition, so
9
+ * IDs made with it only hide paths from casual readers: real deployments must set their own.
10
+ */
11
+ export const DEV_SECRET = 'mitt-dev-secret-not-for-production';
12
+ /**
13
+ * Short one-way digest of the secret. Loader options carry it instead of the secret because Next
14
+ * writes the whole config to `.next/required-server-files.json`; it still changes Turbopack's cache
15
+ * key whenever the secret changes.
16
+ */
17
+ export function secretFingerprint(secret) {
18
+ return createHash('sha256').update(`mitt-fp\0${secret}`).digest('hex').slice(0, 16);
19
+ }
20
+ export function resolveSecret(secretEnv, env, { allowDev }) {
21
+ const fromEnv = env[secretEnv];
22
+ if (fromEnv)
23
+ return { secret: fromEnv, secretEnv, devFallback: false };
24
+ return allowDev
25
+ ? { secret: DEV_SECRET, secretEnv, devFallback: true }
26
+ : { secret: null, secretEnv, devFallback: false };
27
+ }
28
+ function invalid(file, reason) {
29
+ return new Error(`[mitt] ${file}: ${reason}`);
30
+ }
31
+ /**
32
+ * Reads `mitt.config.json` from the app root, the one place both the plugin and the CLI take their
33
+ * settings from so they always derive the same IDs. A missing file means defaults.
34
+ */
35
+ export function readMittConfig(appRoot) {
36
+ const file = join(appRoot, CONFIG_FILE);
37
+ let raw;
38
+ try {
39
+ raw = readFileSync(file, 'utf8');
40
+ }
41
+ catch (error) {
42
+ if (error.code === 'ENOENT') {
43
+ return { secretEnv: DEFAULT_SECRET_ENV, project: undefined };
44
+ }
45
+ throw error;
46
+ }
47
+ const parsed = JSON.parse(raw);
48
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
49
+ throw invalid(file, 'expected a JSON object');
50
+ }
51
+ const { secretEnv = DEFAULT_SECRET_ENV, include, project } = parsed;
52
+ if (typeof secretEnv !== 'string' || !/^[A-Z_][A-Z0-9_]*$/.test(secretEnv)) {
53
+ throw invalid(file, '"secretEnv" must be an environment variable name');
54
+ }
55
+ // The loader is registered for `*.tsx`/`*.jsx` and the manifest scans the same set. Custom globs
56
+ // would have to be honored identically by both, so until then only the default is accepted.
57
+ if (include !== undefined &&
58
+ !(Array.isArray(include) && include.length === 1 && include[0] === DEFAULT_INCLUDE)) {
59
+ throw invalid(file, `"include" only supports ["${DEFAULT_INCLUDE}"] for now`);
60
+ }
61
+ return { secretEnv, project };
62
+ }
63
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/git.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { DIRTY_SUFFIX } from '@basementstudio/mitt-shared/formats';
2
+ export { DIRTY_SUFFIX };
3
+ export interface GitOptions {
4
+ input?: string;
5
+ /** `buffer` returns raw bytes, for output that mixes headers and blob contents. */
6
+ encoding?: 'utf8' | 'buffer';
7
+ }
8
+ export declare function git(cwd: string, args: string[], options?: GitOptions & {
9
+ encoding?: 'utf8';
10
+ }): string;
11
+ export declare function git(cwd: string, args: string[], options: GitOptions & {
12
+ encoding: 'buffer';
13
+ }): Buffer;
14
+ export interface RepoLayout {
15
+ /** The repository's top level, realpath'd. */
16
+ root: string;
17
+ /** The app's directory relative to the repo root, POSIX; empty when they are the same. */
18
+ appPrefix: string;
19
+ }
20
+ /** Throws when `appRoot` is not inside a git repository. */
21
+ export declare function repoLayout(appRoot: string): RepoLayout;
22
+ export interface ResolveCommitOptions {
23
+ /**
24
+ * The served code is the live working tree (e.g. `next dev`), so a clean SHA would point the
25
+ * manifest at a tree that stops matching on the first edit.
26
+ */
27
+ live?: boolean;
28
+ }
29
+ /**
30
+ * `VERCEL_GIT_COMMIT_SHA`, else `HEAD` plus `-dirty` when the app's tree has changes, else null.
31
+ * Never throws: a missing git only means comments can't be pinned to a commit.
32
+ */
33
+ export declare function resolveCommit(appRoot: string, options?: ResolveCommitOptions): string | null;
package/dist/git.js ADDED
@@ -0,0 +1,52 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { realpathSync } from 'node:fs';
3
+ import { relative, sep } from 'node:path';
4
+ import { DIRTY_SUFFIX, SHA_PATTERN } from '@basementstudio/mitt-shared/formats';
5
+ export { DIRTY_SUFFIX };
6
+ export function git(cwd, args, options = {}) {
7
+ return execFileSync('git', args, {
8
+ cwd,
9
+ // `undefined` returns a Buffer; Node rejects 'buffer' once there is `input` to encode.
10
+ encoding: options.encoding === 'buffer' ? undefined : 'utf8',
11
+ input: options.input,
12
+ maxBuffer: 1024 * 1024 * 1024,
13
+ stdio: ['pipe', 'pipe', 'ignore'],
14
+ });
15
+ }
16
+ /** Throws when `appRoot` is not inside a git repository. */
17
+ export function repoLayout(appRoot) {
18
+ const root = realpathSync(git(appRoot, ['rev-parse', '--show-toplevel']).trim());
19
+ return { root, appPrefix: relative(root, realpathSync(appRoot)).split(sep).join('/') };
20
+ }
21
+ /**
22
+ * `VERCEL_GIT_COMMIT_SHA`, else `HEAD` plus `-dirty` when the app's tree has changes, else null.
23
+ * Never throws: a missing git only means comments can't be pinned to a commit.
24
+ */
25
+ export function resolveCommit(appRoot, options = {}) {
26
+ const suffix = options.live ? DIRTY_SUFFIX : '';
27
+ const vercel = process.env.VERCEL_GIT_COMMIT_SHA?.trim();
28
+ if (vercel && SHA_PATTERN.test(vercel))
29
+ return `${vercel}${suffix}`;
30
+ let sha;
31
+ try {
32
+ sha = git(appRoot, ['rev-parse', 'HEAD']).trim();
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ if (!SHA_PATTERN.test(sha))
38
+ return null;
39
+ if (options.live)
40
+ return `${sha}${DIRTY_SUFFIX}`;
41
+ let dirty;
42
+ try {
43
+ // Scoped to the app: changes elsewhere in a monorepo don't alter the files the manifest reads.
44
+ dirty = git(appRoot, ['status', '--porcelain', '--', '.']).trim() !== '';
45
+ }
46
+ catch {
47
+ // Unknown state: the working tree is the only source guaranteed to match this build.
48
+ dirty = true;
49
+ }
50
+ return dirty ? `${sha}${DIRTY_SUFFIX}` : sha;
51
+ }
52
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAA;AAE/E,OAAO,EAAE,YAAY,EAAE,CAAA;AAkBvB,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,IAAc,EAAE,OAAO,GAAe,EAAE;IACvE,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE;QAC/B,GAAG;QACH,uFAAuF;QACvF,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;QAC5D,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,SAAS,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI;QAC7B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;KAClC,CAAC,CAAA;AACJ,CAAC;AASD,4DAA4D;AAC5D,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IAChF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;AACxF,CAAC;AAUD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,OAAO,GAAyB,EAAE;IAC/E,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAA;IAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,IAAI,EAAE,CAAA;IACxD,IAAI,MAAM,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,GAAG,MAAM,GAAG,MAAM,EAAE,CAAA;IAEnE,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACH,GAAG,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IACvC,IAAI,OAAO,CAAC,IAAI;QAAE,OAAO,GAAG,GAAG,GAAG,YAAY,EAAE,CAAA;IAEhD,IAAI,KAAc,CAAA;IAClB,IAAI,CAAC;QACH,+FAA+F;QAC/F,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAA;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,qFAAqF;QACrF,KAAK,GAAG,IAAI,CAAA;IACd,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,YAAY,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA;AAC9C,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { NextConfig } from 'next';
2
+ import { type ReactElement } from 'react';
3
+ import type { MittOptions, NextConfigFunction } from './with-mitt.js';
4
+ export type { Entry, TransformOptions, TransformResult } from './transform.js';
5
+ export type { MittOptions, NextConfigContext, NextConfigFunction } from './with-mitt.js';
6
+ export interface MittProps {
7
+ /** Renders nothing: an off switch for conditions known at render, like a CMS draft mode. */
8
+ disabled?: boolean;
9
+ /** Project id. Defaults to `"project"` in mitt.config.json, inlined by withMitt. */
10
+ project?: string;
11
+ /** Origin of the Mitt API. Defaults to `MITT_API_URL` at build time, inlined by withMitt. */
12
+ api?: string;
13
+ /** Loader URL. Defaults to `<api>/w/loader.js`. */
14
+ src?: string;
15
+ }
16
+ /**
17
+ * The reviewer-only loader as a plain async `<script>` (not next/script): the same tag as embedding
18
+ * it by hand, server-rendered, with no client JavaScript of its own (#5).
19
+ */
20
+ export declare function Mitt({ disabled, project, api, src, }: MittProps): ReactElement | null;
21
+ /**
22
+ * Adds opaque `data-fb` IDs to every host element, in dev and in builds alike (#7): production
23
+ * comments need them to resolve (#2). No path, line or stega ever reaches the output (#3, #4).
24
+ *
25
+ * Returns a config function because the secret policy depends on the phase, and Next only passes
26
+ * the phase to config functions (`NEXT_PHASE` is set too late in `next build` to rely on).
27
+ */
28
+ export declare function withMitt(nextConfig?: NextConfig | NextConfigFunction, options?: MittOptions): NextConfigFunction;
package/dist/index.js ADDED
@@ -0,0 +1,41 @@
1
+ import { createElement } from 'react';
2
+ let warned = false;
3
+ /**
4
+ * The reviewer-only loader as a plain async `<script>` (not next/script): the same tag as embedding
5
+ * it by hand, server-rendered, with no client JavaScript of its own (#5).
6
+ */
7
+ export function Mitt({ disabled = false, project = process.env.MITT_PROJECT, api = process.env.MITT_API_URL, src, }) {
8
+ if (disabled)
9
+ return null;
10
+ if (!project || !api) {
11
+ // Never throws: a missing setting must not take the host page down (#4).
12
+ if (!warned && process.env.NODE_ENV !== 'production') {
13
+ warned = true;
14
+ console.warn(`[mitt] <Mitt /> needs ${project ? 'an API URL' : 'a project'}: wrap next.config in withMitt() and set MITT_API_URL, or pass it as a prop.`);
15
+ }
16
+ return null;
17
+ }
18
+ return createElement('script', {
19
+ src: src ?? `${api.replace(/\/+$/, '')}/w/loader.js`,
20
+ async: true,
21
+ 'data-project': project,
22
+ 'data-api': api,
23
+ });
24
+ }
25
+ /**
26
+ * Adds opaque `data-fb` IDs to every host element, in dev and in builds alike (#7): production
27
+ * comments need them to resolve (#2). No path, line or stega ever reaches the output (#3, #4).
28
+ *
29
+ * Returns a config function because the secret policy depends on the phase, and Next only passes
30
+ * the phase to config functions (`NEXT_PHASE` is set too late in `next build` to rely on).
31
+ */
32
+ export function withMitt(nextConfig = {}, options = {}) {
33
+ return async (phase, context) => {
34
+ // Bundlers must not follow this import into app bundles (node:fs, oxc-parser, git); Node
35
+ // resolves it when next.config runs.
36
+ const { applyMitt } = (await import(
37
+ /* webpackIgnore: true */ /* turbopackIgnore: true */ './with-mitt.js'));
38
+ return applyMitt(nextConfig, options, phase, context);
39
+ };
40
+ }
41
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAqB,MAAM,OAAO,CAAA;AAoBxD,IAAI,MAAM,GAAG,KAAK,CAAA;AAElB;;;GAGG;AACH,MAAM,UAAU,IAAI,CAAC,EACnB,QAAQ,GAAG,KAAK,EAChB,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,EAClC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,EAC9B,GAAG,GACO;IACV,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAA;IACzB,IAAI,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;QACrB,yEAAyE;QACzE,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;YACrD,MAAM,GAAG,IAAI,CAAA;YACb,OAAO,CAAC,IAAI,CACV,yBAAyB,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,8EAA8E,CAC5I,CAAA;QACH,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,aAAa,CAAC,QAAQ,EAAE;QAC7B,GAAG,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc;QACpD,KAAK,EAAE,IAAI;QACX,cAAc,EAAE,OAAO;QACvB,UAAU,EAAE,GAAG;KAChB,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CACtB,UAAU,GAAoC,EAAE,EAChD,OAAO,GAAgB,EAAE;IAEzB,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC9B,yFAAyF;QACzF,qCAAqC;QACrC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,MAAM;QACjC,yBAAyB,CAAC,2BAA2B,CAAC,gBAAgB,CACvE,CAAoC,CAAA;QACrC,OAAO,SAAS,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IACvD,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { SourceMap } from 'magic-string';
2
+ export interface MittLoaderOptions {
3
+ /** Environment variable the secret is read from, in the loader's own process. */
4
+ secretEnv: string;
5
+ /** `secretFingerprint` of the secret withMitt saw; absent when it saw none. */
6
+ fingerprint?: string;
7
+ /** Whether the public dev secret may stand in when `secretEnv` is unset. */
8
+ allowDevSecret: boolean;
9
+ commit?: string;
10
+ /** Absolute path of the Next app root; IDs are keyed by paths relative to it. */
11
+ root: string;
12
+ }
13
+ export interface MittLoaderContext {
14
+ resourcePath: string;
15
+ /** Whether the bundler consumes loader sourcemaps (Turbopack drops them when false). */
16
+ sourceMap?: boolean;
17
+ getOptions?: () => unknown;
18
+ emitWarning?: (warning: Error) => void;
19
+ callback?: (error: Error | null, content?: string, sourceMap?: SourceMap | unknown) => void;
20
+ }
21
+ export default function mittLoader(this: MittLoaderContext, source: string, inputMap?: unknown): string | undefined;
package/dist/loader.js ADDED
@@ -0,0 +1,71 @@
1
+ import { basename, isAbsolute, relative, sep } from 'node:path';
2
+ import { resolveSecret, secretFingerprint } from './config.js';
3
+ import { transform } from './transform.js';
4
+ function readOptions(raw) {
5
+ const invalid = new Error('[mitt] invalid loader options: register the loader with withMitt()');
6
+ if (typeof raw !== 'object' || raw === null)
7
+ throw invalid;
8
+ const { secretEnv, fingerprint, allowDevSecret, commit, root } = raw;
9
+ if (typeof secretEnv !== 'string' || secretEnv === '' || typeof root !== 'string')
10
+ throw invalid;
11
+ const options = { secretEnv, allowDevSecret: allowDevSecret === true, root };
12
+ if (typeof fingerprint === 'string')
13
+ options.fingerprint = fingerprint;
14
+ if (typeof commit === 'string')
15
+ options.commit = commit;
16
+ return options;
17
+ }
18
+ let lastVerified;
19
+ // Here rather than at config load, so `next typegen` and `next start` (which load the config but
20
+ // never compile) work without the secret; a build that would emit IDs fails instead.
21
+ function loaderSecret({ secretEnv, fingerprint, allowDevSecret }) {
22
+ const { secret } = resolveSecret(secretEnv, process.env, { allowDev: allowDevSecret });
23
+ if (secret === null) {
24
+ throw new Error(`[mitt] ${secretEnv} is not set. Production builds need a per-project secret to key the ` +
25
+ 'data-fb IDs; without one, anyone could recover file paths from the public HTML.');
26
+ }
27
+ if (fingerprint && `${fingerprint}:${secret}` !== lastVerified) {
28
+ // Turbopack caches output by options, which only carry the fingerprint: a secret that differs
29
+ // from the config's would mix IDs from two secrets in one build.
30
+ if (secretFingerprint(secret) !== fingerprint) {
31
+ throw new Error(`[mitt] ${secretEnv} changed after the config loaded. Restart Next so every data-fb ID ` +
32
+ 'uses the same secret.');
33
+ }
34
+ lastVerified = `${fingerprint}:${secret}`;
35
+ }
36
+ return secret;
37
+ }
38
+ // A function of (source, options) plus the secret, which the fingerprint in the options pins:
39
+ // Turbopack caches loader output by those inputs, so any other input or side effect would make
40
+ // the cached output and the git-rebuilt manifest diverge.
41
+ // Keep it a default export: loader-runner `require()`s Turbopack loaders and reads `.default`.
42
+ export default function mittLoader(source, inputMap) {
43
+ const done = (code, map) => {
44
+ if (!this.callback)
45
+ return code;
46
+ this.callback(null, code, map);
47
+ return undefined;
48
+ };
49
+ const options = readOptions(this.getOptions?.());
50
+ const rel = relative(options.root, this.resourcePath);
51
+ // Defensive: the rules already exclude foreign code, but an ID for a path outside the app could
52
+ // never be resolved by the manifest.
53
+ if (rel === '' || rel.startsWith('..') || isAbsolute(rel))
54
+ return done(source, inputMap);
55
+ const segments = rel.split(sep);
56
+ if (segments.includes('node_modules'))
57
+ return done(source, inputMap);
58
+ const result = transform(source, {
59
+ relPath: segments.join('/'),
60
+ secret: loaderSecret(options),
61
+ commit: options.commit,
62
+ mapSource: basename(this.resourcePath),
63
+ sourceMap: this.sourceMap !== false,
64
+ });
65
+ if (result.warning)
66
+ this.emitWarning?.(new Error(result.warning));
67
+ if (result.code === source)
68
+ return done(source, inputMap);
69
+ return done(result.code, result.map ?? undefined);
70
+ }
71
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { type Entry } from './transform.js';
2
+ export * from './node.js';
3
+ export type { Entry } from './transform.js';
4
+ export interface BuildManifestOptions {
5
+ /** The Next app directory, the same root `withMitt` keys paths by. */
6
+ appRoot: string;
7
+ /** The commit a page reported in `data-fb-commit`; `-dirty` or null reads the working tree. */
8
+ commit: string | null;
9
+ secret: string;
10
+ }
11
+ /**
12
+ * Rebuilds the ID -> source map for a commit by running the loader's own transform over the files
13
+ * of that commit. Nothing is collected from the build, so a cached build can't make it wrong.
14
+ */
15
+ export declare function buildManifest({ appRoot, commit, secret, }: BuildManifestOptions): Map<string, Entry>;
@@ -0,0 +1,88 @@
1
+ // Node-only, and private by design (#3): it maps opaque IDs back to files and lines. Client code
2
+ // must never import it, and its output must never be served or bundled.
3
+ import { readFileSync, realpathSync } from 'node:fs';
4
+ import { DIRTY_SUFFIX, git, repoLayout } from './git.js';
5
+ import { transform } from './transform.js';
6
+ export * from './node.js';
7
+ const JSX_FILE = /\.[jt]sx$/;
8
+ function isSource(path) {
9
+ return JSX_FILE.test(path) && !path.split('/').includes('node_modules');
10
+ }
11
+ function workingTreeFiles(appRoot) {
12
+ // Paths come back relative to the cwd, i.e. the app root. Untracked files count: a dev server or
13
+ // dirty build serves them too.
14
+ const listed = git(appRoot, ['ls-files', '-z', '--cached', '--others', '--exclude-standard']);
15
+ const files = [];
16
+ for (const relPath of new Set(listed.split('\0'))) {
17
+ if (!relPath || !isSource(relPath))
18
+ continue;
19
+ let source;
20
+ try {
21
+ source = readFileSync(`${appRoot}/${relPath}`, 'utf8');
22
+ }
23
+ catch (error) {
24
+ // Tracked but deleted in the working tree: it isn't served either.
25
+ if (error.code === 'ENOENT')
26
+ continue;
27
+ throw error;
28
+ }
29
+ files.push({ relPath, source });
30
+ }
31
+ return files;
32
+ }
33
+ function commitFiles(repoRoot, appPrefix, sha) {
34
+ const pathspec = appPrefix ? [`${appPrefix}/`] : [];
35
+ const listed = git(repoRoot, ['ls-tree', '-r', '-z', '--name-only', sha, '--', ...pathspec]);
36
+ const paths = listed.split('\0').filter((path) => path && isSource(path));
37
+ if (paths.length === 0)
38
+ return [];
39
+ // One `cat-file --batch` process for every blob instead of a `git show` per file.
40
+ const output = git(repoRoot, ['cat-file', '--batch'], {
41
+ input: paths.map((path) => `${sha}:${path}\n`).join(''),
42
+ encoding: 'buffer',
43
+ });
44
+ const files = [];
45
+ let offset = 0;
46
+ for (const path of paths) {
47
+ const headerEnd = output.indexOf(0x0a, offset);
48
+ const header = output.toString('utf8', offset, headerEnd);
49
+ const size = Number(header.split(' ')[2]);
50
+ if (!header.includes(' blob ') || !Number.isInteger(size)) {
51
+ throw new Error(`[mitt] could not read ${path} at ${sha}: ${header}`);
52
+ }
53
+ const start = headerEnd + 1;
54
+ const source = output.toString('utf8', start, start + size);
55
+ offset = start + size + 1;
56
+ files.push({ relPath: appPrefix ? path.slice(appPrefix.length + 1) : path, source });
57
+ }
58
+ return files;
59
+ }
60
+ /**
61
+ * Rebuilds the ID -> source map for a commit by running the loader's own transform over the files
62
+ * of that commit. Nothing is collected from the build, so a cached build can't make it wrong.
63
+ */
64
+ export function buildManifest({ appRoot, commit, secret, }) {
65
+ const root = realpathSync(appRoot);
66
+ let files;
67
+ if (commit === null || commit.endsWith(DIRTY_SUFFIX)) {
68
+ files = workingTreeFiles(root);
69
+ }
70
+ else {
71
+ const repo = repoLayout(root);
72
+ files = commitFiles(repo.root, repo.appPrefix, commit);
73
+ }
74
+ const manifest = new Map();
75
+ for (const { relPath, source } of files) {
76
+ for (const entry of transform(source, { relPath, secret, sourceMap: false }).entries) {
77
+ const existing = manifest.get(entry.id);
78
+ if (existing) {
79
+ // Would mean a truncated-HMAC collision; resolving either entry could misplace a comment.
80
+ throw new Error(`[mitt] duplicate data-fb ID ${entry.id}: ${existing.file}:${existing.line} and ` +
81
+ `${entry.file}:${entry.line}`);
82
+ }
83
+ manifest.set(entry.id, entry);
84
+ }
85
+ }
86
+ return manifest;
87
+ }
88
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
package/dist/node.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { CONFIG_FILE, DEFAULT_SECRET_ENV, DEV_SECRET, type Env, type MittConfig, type ResolvedSecret, readMittConfig, resolveSecret, secretFingerprint, } from './config.js';
2
+ export { DIRTY_SUFFIX, type GitOptions, git, type RepoLayout, repoLayout } from './git.js';
package/dist/node.js ADDED
@@ -0,0 +1,5 @@
1
+ // Node-only and light: settings, secret and git plumbing shared by the plugin and the CLI, without
2
+ // the transform (oxc, magic-string) that `./manifest` adds on top.
3
+ export { CONFIG_FILE, DEFAULT_SECRET_ENV, DEV_SECRET, readMittConfig, resolveSecret, secretFingerprint, } from './config.js';
4
+ export { DIRTY_SUFFIX, git, repoLayout } from './git.js';
5
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,mEAAmE;AACnE,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,UAAU,EAIV,cAAc,EACd,aAAa,EACb,iBAAiB,GAClB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,YAAY,EAAmB,GAAG,EAAmB,UAAU,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,27 @@
1
+ import type { NextConfig } from 'next';
2
+ import { type Env } from './config.js';
3
+ export declare const SETUP_PATH = "/__mitt";
4
+ type Fetch = (input: string, init?: RequestInit) => Promise<Response>;
5
+ export interface SetupOptions {
6
+ root: string;
7
+ env?: Env;
8
+ fetch?: Fetch;
9
+ }
10
+ export interface SetupServer {
11
+ /** What Next rewrites `/__mitt` to: a loopback URL with a per-process secret path. */
12
+ readonly target: string;
13
+ close(): Promise<void>;
14
+ }
15
+ interface EmbedTag {
16
+ src: string;
17
+ project: string | undefined;
18
+ api: string | undefined;
19
+ }
20
+ /** The first `<script>` that loads Mitt's loader (or the widget directly), from server HTML. */
21
+ export declare function findEmbed(html: string, pageUrl: string): EmbedTag | null;
22
+ export declare function startSetupServer(options: SetupOptions): Promise<SetupServer>;
23
+ /** Puts `/__mitt` first, before the app's own rewrites and routes. */
24
+ export declare function withSetupRewrites(config: NextConfig, target: string): NextConfig;
25
+ /** One setup server per app root and process, however often Next loads the config. */
26
+ export declare function setupServerFor(root: string): Promise<SetupServer>;
27
+ export {};