@merittdev/horus-lens 0.0.1 → 0.0.3
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 +129 -0
- package/dist/browser.cjs +1 -1
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +2 -2
- package/dist/{chunk-DU7HNRF4.js → chunk-CTBY5XIN.js} +2 -2
- package/dist/{chunk-S6S3ZZQA.js → chunk-XRONR4GR.js} +2 -2
- package/dist/{chunk-S6S3ZZQA.js.map → chunk-XRONR4GR.js.map} +1 -1
- package/dist/dashboard.cjs +784 -434
- package/dist/dashboard.cjs.map +1 -1
- package/dist/dashboard.css +224 -1
- package/dist/dashboard.d.cts +85 -1
- package/dist/dashboard.d.ts +85 -1
- package/dist/dashboard.js +648 -301
- package/dist/dashboard.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lens.min.js +1 -1
- package/dist/lens.min.js.map +1 -1
- package/dist/react.cjs +6 -3
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +18 -6
- package/dist/react.d.ts +18 -6
- package/dist/react.js +6 -4
- package/dist/react.js.map +1 -1
- package/dist/server.cjs +109 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +59 -0
- package/dist/server.d.ts +59 -0
- package/dist/server.js +82 -0
- package/dist/server.js.map +1 -0
- package/package.json +30 -4
- /package/dist/{chunk-DU7HNRF4.js.map → chunk-CTBY5XIN.js.map} +0 -0
package/dist/react.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { ReactNode, Component, ContextType } from 'react';
|
|
3
|
+
import { L as LensConfig, a as LensClient } from './index.d-DOCLVJGS.cjs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import './index.d-Bzgtl7-B.cjs';
|
|
6
6
|
|
|
@@ -49,13 +49,25 @@ declare const AppContextSchema: z.ZodObject<{
|
|
|
49
49
|
}, z.core.$strip>;
|
|
50
50
|
type AppContext = z.infer<typeof AppContextSchema>;
|
|
51
51
|
|
|
52
|
-
interface
|
|
52
|
+
interface LensProps extends Omit<BootOptions, 'force'> {
|
|
53
53
|
accessId: string;
|
|
54
54
|
/** Activate regardless of ?lens=true — e.g. pass your preview-env flag. */
|
|
55
55
|
enabled?: boolean;
|
|
56
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Optional: <Lens /> mounts standalone anywhere in the tree. Wrap children
|
|
58
|
+
* only when descendants need the `useLens()` context.
|
|
59
|
+
*/
|
|
60
|
+
children?: ReactNode;
|
|
57
61
|
}
|
|
58
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Mounts the Lens issue-capture overlay. Renders nothing itself — drop
|
|
64
|
+
* `<Lens accessId="lai_…" />` into your root layout and the overlay appears
|
|
65
|
+
* for sessions gated in via `?lens=true` (or force it with `enabled`).
|
|
66
|
+
*/
|
|
67
|
+
declare function Lens(props: LensProps): react.JSX.Element;
|
|
68
|
+
/** Back-compat alias — `<Lens />` is the canonical name. */
|
|
69
|
+
declare const LensProvider: typeof Lens;
|
|
70
|
+
type LensProviderProps = LensProps;
|
|
59
71
|
|
|
60
72
|
interface Identity {
|
|
61
73
|
id?: string;
|
|
@@ -97,4 +109,4 @@ declare class LensErrorBoundary extends Component<Props, State> {
|
|
|
97
109
|
/** Walk the React fiber tree from `el` upward, collecting component names. */
|
|
98
110
|
declare function getComponentStack(el: Element): string[];
|
|
99
111
|
|
|
100
|
-
export { LensErrorBoundary, LensProvider, type LensProviderProps, getComponentStack, isGateActive, useLens };
|
|
112
|
+
export { Lens, LensErrorBoundary, type LensProps, LensProvider, type LensProviderProps, getComponentStack, isGateActive, useLens };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { ReactNode, Component, ContextType } from 'react';
|
|
3
|
+
import { L as LensConfig, a as LensClient } from './index.d-BA540Fae.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import './index.d-Bzgtl7-B.js';
|
|
6
6
|
|
|
@@ -49,13 +49,25 @@ declare const AppContextSchema: z.ZodObject<{
|
|
|
49
49
|
}, z.core.$strip>;
|
|
50
50
|
type AppContext = z.infer<typeof AppContextSchema>;
|
|
51
51
|
|
|
52
|
-
interface
|
|
52
|
+
interface LensProps extends Omit<BootOptions, 'force'> {
|
|
53
53
|
accessId: string;
|
|
54
54
|
/** Activate regardless of ?lens=true — e.g. pass your preview-env flag. */
|
|
55
55
|
enabled?: boolean;
|
|
56
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Optional: <Lens /> mounts standalone anywhere in the tree. Wrap children
|
|
58
|
+
* only when descendants need the `useLens()` context.
|
|
59
|
+
*/
|
|
60
|
+
children?: ReactNode;
|
|
57
61
|
}
|
|
58
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Mounts the Lens issue-capture overlay. Renders nothing itself — drop
|
|
64
|
+
* `<Lens accessId="lai_…" />` into your root layout and the overlay appears
|
|
65
|
+
* for sessions gated in via `?lens=true` (or force it with `enabled`).
|
|
66
|
+
*/
|
|
67
|
+
declare function Lens(props: LensProps): react.JSX.Element;
|
|
68
|
+
/** Back-compat alias — `<Lens />` is the canonical name. */
|
|
69
|
+
declare const LensProvider: typeof Lens;
|
|
70
|
+
type LensProviderProps = LensProps;
|
|
59
71
|
|
|
60
72
|
interface Identity {
|
|
61
73
|
id?: string;
|
|
@@ -97,4 +109,4 @@ declare class LensErrorBoundary extends Component<Props, State> {
|
|
|
97
109
|
/** Walk the React fiber tree from `el` upward, collecting component names. */
|
|
98
110
|
declare function getComponentStack(el: Element): string[];
|
|
99
111
|
|
|
100
|
-
export { LensErrorBoundary, LensProvider, type LensProviderProps, getComponentStack, isGateActive, useLens };
|
|
112
|
+
export { Lens, LensErrorBoundary, type LensProps, LensProvider, type LensProviderProps, getComponentStack, isGateActive, useLens };
|
package/dist/react.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
boot,
|
|
3
3
|
isGateActive
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-CTBY5XIN.js";
|
|
5
|
+
import "./chunk-XRONR4GR.js";
|
|
6
6
|
import "./chunk-DWOH2ENF.js";
|
|
7
7
|
import "./chunk-PZ5AY32C.js";
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ import { useContext } from "react";
|
|
|
14
14
|
import { Component } from "react";
|
|
15
15
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
16
16
|
var LensContext = createContext(null);
|
|
17
|
-
function
|
|
17
|
+
function Lens(props) {
|
|
18
18
|
const { children, enabled, ...bootOptions } = props;
|
|
19
19
|
const [browser, setBrowser] = useState(null);
|
|
20
20
|
useEffect(() => {
|
|
@@ -28,8 +28,9 @@ function LensProvider(props) {
|
|
|
28
28
|
setBrowser(null);
|
|
29
29
|
};
|
|
30
30
|
}, [props.accessId]);
|
|
31
|
-
return /* @__PURE__ */ jsx(LensContext.Provider, { value: browser, children });
|
|
31
|
+
return /* @__PURE__ */ jsx(LensContext.Provider, { value: browser, children: children ?? null });
|
|
32
32
|
}
|
|
33
|
+
var LensProvider = Lens;
|
|
33
34
|
function useLens() {
|
|
34
35
|
const browser = useContext(LensContext);
|
|
35
36
|
return {
|
|
@@ -99,6 +100,7 @@ function getComponentStack(el) {
|
|
|
99
100
|
return names;
|
|
100
101
|
}
|
|
101
102
|
export {
|
|
103
|
+
Lens,
|
|
102
104
|
LensErrorBoundary,
|
|
103
105
|
LensProvider,
|
|
104
106
|
getComponentStack,
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../react/src/provider.tsx","../../react/src/context.ts","../../react/src/use-lens.ts","../../react/src/error-boundary.tsx","../../react/src/fiber.ts","../../react/src/index.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { boot } from '@horus-lens/browser';\nimport type { BootOptions, LensBrowser } from '@horus-lens/browser';\nimport { LensContext } from './context.js';\n\nexport interface
|
|
1
|
+
{"version":3,"sources":["../../react/src/provider.tsx","../../react/src/context.ts","../../react/src/use-lens.ts","../../react/src/error-boundary.tsx","../../react/src/fiber.ts","../../react/src/index.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { boot } from '@horus-lens/browser';\nimport type { BootOptions, LensBrowser } from '@horus-lens/browser';\nimport { LensContext } from './context.js';\n\nexport interface LensProps extends Omit<BootOptions, 'force'> {\n accessId: string;\n /** Activate regardless of ?lens=true — e.g. pass your preview-env flag. */\n enabled?: boolean;\n /**\n * Optional: <Lens /> mounts standalone anywhere in the tree. Wrap children\n * only when descendants need the `useLens()` context.\n */\n children?: ReactNode;\n}\n\n/**\n * Mounts the Lens issue-capture overlay. Renders nothing itself — drop\n * `<Lens accessId=\"lai_…\" />` into your root layout and the overlay appears\n * for sessions gated in via `?lens=true` (or force it with `enabled`).\n */\nexport function Lens(props: LensProps) {\n const { children, enabled, ...bootOptions } = props;\n const [browser, setBrowser] = useState<LensBrowser | null>(null);\n\n useEffect(() => {\n const instance = boot({ ...bootOptions, force: enabled });\n if (instance) {\n instance.client.setApp({ extra: { framework: 'react' } });\n }\n setBrowser(instance);\n return () => {\n instance?.destroy();\n setBrowser(null);\n };\n }, [props.accessId]);\n\n return <LensContext.Provider value={browser}>{children ?? null}</LensContext.Provider>;\n}\n\n/** Back-compat alias — `<Lens />` is the canonical name. */\nexport const LensProvider = Lens;\nexport type LensProviderProps = LensProps;\n","import { createContext } from 'react';\nimport type { LensBrowser } from '@horus-lens/browser';\n\nexport const LensContext = createContext<LensBrowser | null>(null);\n","import { useContext } from 'react';\nimport type { AppContext } from '@horus-lens/shared';\nimport { LensContext } from './context.js';\n\ninterface Identity {\n id?: string;\n email?: string;\n name?: string;\n organization?: string;\n}\n\nexport function useLens() {\n const browser = useContext(LensContext);\n\n return {\n active: browser !== null,\n open: () => browser?.open(),\n close: () => browser?.close(),\n identify: (user: Identity) => browser?.client.setApp({ user }),\n setApp: (app: Partial<AppContext>) => browser?.client.setApp(app),\n report: async (input?: { comment?: string }): Promise<{ reportId: string } | null> => {\n if (!browser) return null;\n return browser.client.submitReport({ comment: input?.comment });\n },\n };\n}\n","import { Component } from 'react';\nimport type { ContextType, ReactNode } from 'react';\nimport { LensContext } from './context.js';\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n}\n\nexport class LensErrorBoundary extends Component<Props, State> {\n static contextType = LensContext;\n declare context: ContextType<typeof LensContext>;\n\n state: State = { hasError: false };\n\n static getDerivedStateFromError(): State {\n return { hasError: true };\n }\n\n componentDidCatch(error: Error) {\n const browser = this.context;\n if (browser) {\n browser.client.setApp({ extra: { lastBoundaryError: error.message } });\n browser.open();\n }\n }\n\n render() {\n if (this.state.hasError) {\n return (\n this.props.fallback ?? (\n <div\n role=\"alert\"\n style={{\n padding: '12px 16px',\n borderRadius: 8,\n border: '1px solid rgba(0,0,0,0.1)',\n font: '14px system-ui, sans-serif',\n }}\n >\n Something went wrong.\n </div>\n )\n );\n }\n return this.props.children;\n }\n}\n","interface FiberType {\n displayName?: string;\n name?: string;\n render?: { displayName?: string; name?: string };\n}\n\ninterface FiberNode {\n type?: FiberType | string | null;\n return?: FiberNode | null;\n}\n\nfunction nameOf(type: FiberNode['type']): string | undefined {\n if (!type || typeof type === 'string') return undefined;\n return type.displayName ?? type.name ?? type.render?.displayName ?? type.render?.name;\n}\n\n/** Walk the React fiber tree from `el` upward, collecting component names. */\nexport function getComponentStack(el: Element): string[] {\n const names: string[] = [];\n try {\n const key = Object.keys(el).find((k) => k.startsWith('__reactFiber$'));\n if (!key) return names;\n\n let fiber = (el as unknown as Record<string, FiberNode | undefined>)[key] ?? null;\n const seen = new Set<FiberNode>();\n while (fiber && !seen.has(fiber)) {\n seen.add(fiber);\n const name = nameOf(fiber.type);\n if (name) names.push(name);\n fiber = fiber.return ?? null;\n }\n } catch {\n return names;\n }\n return names;\n}\n","export { Lens, LensProvider } from './provider.js';\nexport type { LensProps, LensProviderProps } from './provider.js';\nexport { useLens } from './use-lens.js';\nexport { LensErrorBoundary } from './error-boundary.js';\nexport { getComponentStack } from './fiber.js';\nexport { isGateActive } from '@horus-lens/browser';\n"],"mappings":";;;;;;;;;AAAA,SAAS,WAAW,gBAAgB;ACApC,SAAS,qBAAqB;ADsCrB,SAAA,WAAA;AEtCT,SAAS,kBAAkB;ACA3B,SAAS,iBAAiB;AAmChB,SAAA,OAAAA,YAAA;AFhCH,IAAM,cAAc,cAAkC,IAAI;ADmB1D,SAAS,KAAK,OAAkB;AACrC,QAAM,EAAE,UAAU,SAAS,GAAG,YAAY,IAAI;AAC9C,QAAM,CAAC,SAAS,UAAU,IAAI,SAA6B,IAAI;AAE/D,YAAU,MAAM;AACd,UAAM,WAAW,KAAK,EAAE,GAAG,aAAa,OAAO,QAAQ,CAAC;AACxD,QAAI,UAAU;AACZ,eAAS,OAAO,OAAO,EAAE,OAAO,EAAE,WAAW,QAAQ,EAAE,CAAC;IAC1D;AACA,eAAW,QAAQ;AACnB,WAAO,MAAM;AACX,gBAAU,QAAQ;AAClB,iBAAW,IAAI;IACjB;EACF,GAAG,CAAC,MAAM,QAAQ,CAAC;AAEnB,SAAO,oBAAC,YAAY,UAAZ,EAAqB,OAAO,SAAU,UAAA,YAAY,KAAA,CAAK;AACjE;AAGO,IAAM,eAAe;AE/BrB,SAAS,UAAU;AACxB,QAAM,UAAU,WAAW,WAAW;AAEtC,SAAO;IACL,QAAQ,YAAY;IACpB,MAAM,MAAM,SAAS,KAAK;IAC1B,OAAO,MAAM,SAAS,MAAM;IAC5B,UAAU,CAAC,SAAmB,SAAS,OAAO,OAAO,EAAE,KAAK,CAAC;IAC7D,QAAQ,CAAC,QAA6B,SAAS,OAAO,OAAO,GAAG;IAChE,QAAQ,OAAO,UAAuE;AACpF,UAAI,CAAC,QAAS,QAAO;AACrB,aAAO,QAAQ,OAAO,aAAa,EAAE,SAAS,OAAO,QAAQ,CAAC;IAChE;EACF;AACF;ACZO,IAAM,oBAAN,cAAgC,UAAwB;EAC7D,OAAO,cAAc;EAGrB,QAAe,EAAE,UAAU,MAAM;EAEjC,OAAO,2BAAkC;AACvC,WAAO,EAAE,UAAU,KAAK;EAC1B;EAEA,kBAAkB,OAAc;AAC9B,UAAM,UAAU,KAAK;AACrB,QAAI,SAAS;AACX,cAAQ,OAAO,OAAO,EAAE,OAAO,EAAE,mBAAmB,MAAM,QAAQ,EAAE,CAAC;AACrE,cAAQ,KAAK;IACf;EACF;EAEA,SAAS;AACP,QAAI,KAAK,MAAM,UAAU;AACvB,aACE,KAAK,MAAM,YACTC;QAAC;QAAA;UACC,MAAK;UACL,OAAO;YACL,SAAS;YACT,cAAc;YACd,QAAQ;YACR,MAAM;UACR;UACD,UAAA;QAAA;MAED;IAGN;AACA,WAAO,KAAK,MAAM;EACpB;AACF;ACxCA,SAAS,OAAO,MAA6C;AAC3D,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,SAAO,KAAK,eAAe,KAAK,QAAQ,KAAK,QAAQ,eAAe,KAAK,QAAQ;AACnF;AAGO,SAAS,kBAAkB,IAAuB;AACvD,QAAM,QAAkB,CAAC;AACzB,MAAI;AACF,UAAM,MAAM,OAAO,KAAK,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,eAAe,CAAC;AACrE,QAAI,CAAC,IAAK,QAAO;AAEjB,QAAI,QAAS,GAAwD,GAAG,KAAK;AAC7E,UAAM,OAAO,oBAAI,IAAe;AAChC,WAAO,SAAS,CAAC,KAAK,IAAI,KAAK,GAAG;AAChC,WAAK,IAAI,KAAK;AACd,YAAM,OAAO,OAAO,MAAM,IAAI;AAC9B,UAAI,KAAM,OAAM,KAAK,IAAI;AACzB,cAAQ,MAAM,UAAU;IAC1B;EACF,QAAQ;AACN,WAAO;EACT;AACA,SAAO;AACT;","names":["jsx","jsx"]}
|
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/server.ts
|
|
21
|
+
var server_exports = {};
|
|
22
|
+
__export(server_exports, {
|
|
23
|
+
HOSTED_LENS_API_URL: () => HOSTED_LENS_API_URL,
|
|
24
|
+
createLensProjectsRoute: () => createLensProjectsRoute,
|
|
25
|
+
createLensProxy: () => createLensProxy
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(server_exports);
|
|
28
|
+
|
|
29
|
+
// ../dashboard/dist/server.js
|
|
30
|
+
var HOSTED_LENS_API_URL = "https://api-lens.meritt.app";
|
|
31
|
+
function envProjects() {
|
|
32
|
+
const raw = process.env.LENS_PROJECTS;
|
|
33
|
+
if (raw) {
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(raw);
|
|
36
|
+
if (Array.isArray(parsed)) {
|
|
37
|
+
return parsed.filter(
|
|
38
|
+
(p) => !!p && typeof p.name === "string" && typeof p.accessId === "string" && typeof p.secretKey === "string"
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
console.warn("[lens] LENS_PROJECTS is not valid JSON \u2014 ignoring");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const accessId = process.env.LENS_ACCESS_ID;
|
|
46
|
+
const secretKey = process.env.LENS_SECRET_KEY;
|
|
47
|
+
if (accessId && secretKey) return [{ name: "default", accessId, secretKey }];
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
function resolveOptions(options) {
|
|
51
|
+
return {
|
|
52
|
+
apiUrl: (options?.apiUrl ?? process.env.LENS_API_URL ?? HOSTED_LENS_API_URL).replace(/\/+$/, ""),
|
|
53
|
+
projects: options?.projects ?? envProjects(),
|
|
54
|
+
adminToken: options?.adminToken ?? process.env.LENS_ADMIN_TOKEN ?? ""
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function pickProject(projects, selector) {
|
|
58
|
+
if (projects.length === 0) return null;
|
|
59
|
+
if (selector) {
|
|
60
|
+
const match = projects.find((p) => p.name === selector || p.accessId === selector);
|
|
61
|
+
if (match) return match;
|
|
62
|
+
}
|
|
63
|
+
return projects[0] ?? null;
|
|
64
|
+
}
|
|
65
|
+
function createLensProxy(options) {
|
|
66
|
+
const handler = async (req, ctx) => {
|
|
67
|
+
const { apiUrl, projects, adminToken } = resolveOptions(options);
|
|
68
|
+
const { path } = await ctx.params;
|
|
69
|
+
const search = new URL(req.url).search;
|
|
70
|
+
const target = `${apiUrl}/${path.join("/")}${search}`;
|
|
71
|
+
const isProjectCreate = req.method === "POST" && path.join("/") === "v1/projects";
|
|
72
|
+
const wizardToken = req.headers.get("x-lens-token");
|
|
73
|
+
const project = pickProject(projects, req.headers.get("x-lens-project"));
|
|
74
|
+
const token = isProjectCreate ? adminToken : wizardToken?.startsWith("las_") ? wizardToken : project?.secretKey ?? "";
|
|
75
|
+
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : await req.text();
|
|
76
|
+
const res = await fetch(target, {
|
|
77
|
+
method: req.method,
|
|
78
|
+
headers: {
|
|
79
|
+
authorization: `Bearer ${token}`,
|
|
80
|
+
...body ? { "content-type": req.headers.get("content-type") ?? "application/json" } : {}
|
|
81
|
+
},
|
|
82
|
+
body,
|
|
83
|
+
cache: "no-store"
|
|
84
|
+
});
|
|
85
|
+
const buffer = await res.arrayBuffer();
|
|
86
|
+
return new Response(buffer, {
|
|
87
|
+
status: res.status,
|
|
88
|
+
headers: { "content-type": res.headers.get("content-type") ?? "application/json" }
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
return { GET: handler, POST: handler, PUT: handler, PATCH: handler, DELETE: handler };
|
|
92
|
+
}
|
|
93
|
+
function createLensProjectsRoute(options) {
|
|
94
|
+
return {
|
|
95
|
+
GET: () => {
|
|
96
|
+
const { projects } = resolveOptions(options);
|
|
97
|
+
return Response.json({
|
|
98
|
+
projects: projects.map(({ name, accessId }) => ({ name, accessId }))
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
104
|
+
0 && (module.exports = {
|
|
105
|
+
HOSTED_LENS_API_URL,
|
|
106
|
+
createLensProjectsRoute,
|
|
107
|
+
createLensProxy
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=server.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../../dashboard/src/server/proxy.ts"],"sourcesContent":["export * from '@horus-lens/dashboard/server';\n","/**\n * Server-side glue for hosting <LensDashboard> — framework-agnostic (standard\n * Request/Response, so it drops into Next.js App Router route files, Remix\n * resource routes, etc.).\n *\n * This is the ONLY part of the package that reads environment variables, and\n * it only ever executes on the host's server:\n *\n * LENS_PROJECTS JSON array: [{\"name\",\"accessId\",\"secretKey\"}, …]\n * LENS_ACCESS_ID /\n * LENS_SECRET_KEY legacy single-project fallback\n * LENS_ADMIN_TOKEN optional — enables project creation from the wizard\n * LENS_API_URL optional override (self-hosting / local dev)\n *\n * Usage (Next.js):\n * // app/api/lens/[...path]/route.ts\n * import { createLensProxy } from '@merittdev/horus-lens/server'\n * export const { GET, POST, PUT, PATCH, DELETE } = createLensProxy()\n *\n * // app/api/lens-projects/route.ts\n * import { createLensProjectsRoute } from '@merittdev/horus-lens/server'\n * export const { GET } = createLensProjectsRoute()\n */\n\n// Runs under Node on the host's server; typed locally to keep @types/node out\n// of this browser-focused package.\ndeclare const process: { env: Record<string, string | undefined> };\n\nexport const HOSTED_LENS_API_URL = 'https://api-lens.meritt.app';\n\nexport interface LensProjectEntry {\n name: string;\n accessId: string;\n secretKey: string;\n}\n\nexport interface LensServerOptions {\n /** Lens API base. Default: env LENS_API_URL, else the hosted API. */\n apiUrl?: string;\n /** Project registry. Default: env LENS_PROJECTS (JSON), else the legacy pair. */\n projects?: LensProjectEntry[];\n /** Admin token for POST /v1/projects. Default: env LENS_ADMIN_TOKEN. */\n adminToken?: string;\n}\n\nfunction envProjects(): LensProjectEntry[] {\n const raw = process.env.LENS_PROJECTS;\n if (raw) {\n try {\n const parsed: unknown = JSON.parse(raw);\n if (Array.isArray(parsed)) {\n return parsed.filter(\n (p): p is LensProjectEntry =>\n !!p &&\n typeof (p as LensProjectEntry).name === 'string' &&\n typeof (p as LensProjectEntry).accessId === 'string' &&\n typeof (p as LensProjectEntry).secretKey === 'string',\n );\n }\n } catch {\n console.warn('[lens] LENS_PROJECTS is not valid JSON — ignoring');\n }\n }\n const accessId = process.env.LENS_ACCESS_ID;\n const secretKey = process.env.LENS_SECRET_KEY;\n if (accessId && secretKey) return [{ name: 'default', accessId, secretKey }];\n return [];\n}\n\nfunction resolveOptions(options?: LensServerOptions) {\n return {\n apiUrl: (options?.apiUrl ?? process.env.LENS_API_URL ?? HOSTED_LENS_API_URL).replace(/\\/+$/, ''),\n projects: options?.projects ?? envProjects(),\n adminToken: options?.adminToken ?? process.env.LENS_ADMIN_TOKEN ?? '',\n };\n}\n\nfunction pickProject(projects: LensProjectEntry[], selector: string | null): LensProjectEntry | null {\n if (projects.length === 0) return null;\n if (selector) {\n const match = projects.find((p) => p.name === selector || p.accessId === selector);\n if (match) return match;\n }\n return projects[0] ?? null;\n}\n\n/** Next.js App Router passes ctx.params as a Promise (15+) or plain object. */\ntype RouteContext = { params: { path: string[] } | Promise<{ path: string[] }> };\ntype ProxyHandler = (req: Request, ctx: RouteContext) => Promise<Response>;\n\n/**\n * Authenticated pass-through to the Lens API. Credentials attach HERE, on the\n * server: the browser only ever names a project via `x-lens-project`. During\n * first-run provisioning the wizard forwards its just-minted secret via\n * `x-lens-token`, which wins over the registry.\n */\nexport function createLensProxy(options?: LensServerOptions): Record<\n 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n ProxyHandler\n> {\n const handler: ProxyHandler = async (req, ctx) => {\n const { apiUrl, projects, adminToken } = resolveOptions(options);\n const { path } = await ctx.params;\n const search = new URL(req.url).search;\n const target = `${apiUrl}/${path.join('/')}${search}`;\n\n const isProjectCreate = req.method === 'POST' && path.join('/') === 'v1/projects';\n const wizardToken = req.headers.get('x-lens-token');\n const project = pickProject(projects, req.headers.get('x-lens-project'));\n\n const token = isProjectCreate\n ? adminToken\n : wizardToken?.startsWith('las_')\n ? wizardToken\n : (project?.secretKey ?? '');\n\n const body = req.method === 'GET' || req.method === 'HEAD' ? undefined : await req.text();\n\n const res = await fetch(target, {\n method: req.method,\n headers: {\n authorization: `Bearer ${token}`,\n ...(body ? { 'content-type': req.headers.get('content-type') ?? 'application/json' } : {}),\n },\n body,\n cache: 'no-store',\n });\n\n const buffer = await res.arrayBuffer();\n return new Response(buffer, {\n status: res.status,\n headers: { 'content-type': res.headers.get('content-type') ?? 'application/json' },\n });\n };\n\n return { GET: handler, POST: handler, PUT: handler, PATCH: handler, DELETE: handler };\n}\n\n/** Names + access ids for the project switcher — never the secrets. */\nexport function createLensProjectsRoute(options?: LensServerOptions): { GET: () => Response } {\n return {\n GET: () => {\n const { projects } = resolveOptions(options);\n return Response.json({\n projects: projects.map(({ name, accessId }) => ({ name, accessId })),\n });\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC4BO,IAAM,sBAAsB;AAiBnC,SAAS,cAAkC;AACzC,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,KAAK;AACP,QAAI;AACF,YAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,eAAO,OAAO;UACZ,CAAC,MACC,CAAC,CAAC,KACF,OAAQ,EAAuB,SAAS,YACxC,OAAQ,EAAuB,aAAa,YAC5C,OAAQ,EAAuB,cAAc;QACjD;MACF;IACF,QAAQ;AACN,cAAQ,KAAK,wDAAmD;IAClE;EACF;AACA,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,YAAY,QAAQ,IAAI;AAC9B,MAAI,YAAY,UAAW,QAAO,CAAC,EAAE,MAAM,WAAW,UAAU,UAAU,CAAC;AAC3E,SAAO,CAAC;AACV;AAEA,SAAS,eAAe,SAA6B;AACnD,SAAO;IACL,SAAS,SAAS,UAAU,QAAQ,IAAI,gBAAgB,qBAAqB,QAAQ,QAAQ,EAAE;IAC/F,UAAU,SAAS,YAAY,YAAY;IAC3C,YAAY,SAAS,cAAc,QAAQ,IAAI,oBAAoB;EACrE;AACF;AAEA,SAAS,YAAY,UAA8B,UAAkD;AACnG,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,MAAI,UAAU;AACZ,UAAM,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,aAAa,QAAQ;AACjF,QAAI,MAAO,QAAO;EACpB;AACA,SAAO,SAAS,CAAC,KAAK;AACxB;AAYO,SAAS,gBAAgB,SAG9B;AACA,QAAM,UAAwB,OAAO,KAAK,QAAQ;AAChD,UAAM,EAAE,QAAQ,UAAU,WAAW,IAAI,eAAe,OAAO;AAC/D,UAAM,EAAE,KAAK,IAAI,MAAM,IAAI;AAC3B,UAAM,SAAS,IAAI,IAAI,IAAI,GAAG,EAAE;AAChC,UAAM,SAAS,GAAG,MAAM,IAAI,KAAK,KAAK,GAAG,CAAC,GAAG,MAAM;AAEnD,UAAM,kBAAkB,IAAI,WAAW,UAAU,KAAK,KAAK,GAAG,MAAM;AACpE,UAAM,cAAc,IAAI,QAAQ,IAAI,cAAc;AAClD,UAAM,UAAU,YAAY,UAAU,IAAI,QAAQ,IAAI,gBAAgB,CAAC;AAEvE,UAAM,QAAQ,kBACV,aACA,aAAa,WAAW,MAAM,IAC5B,cACC,SAAS,aAAa;AAE7B,UAAM,OAAO,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,SAAY,MAAM,IAAI,KAAK;AAExF,UAAM,MAAM,MAAM,MAAM,QAAQ;MAC9B,QAAQ,IAAI;MACZ,SAAS;QACP,eAAe,UAAU,KAAK;QAC9B,GAAI,OAAO,EAAE,gBAAgB,IAAI,QAAQ,IAAI,cAAc,KAAK,mBAAmB,IAAI,CAAC;MAC1F;MACA;MACA,OAAO;IACT,CAAC;AAED,UAAM,SAAS,MAAM,IAAI,YAAY;AACrC,WAAO,IAAI,SAAS,QAAQ;MAC1B,QAAQ,IAAI;MACZ,SAAS,EAAE,gBAAgB,IAAI,QAAQ,IAAI,cAAc,KAAK,mBAAmB;IACnF,CAAC;EACH;AAEA,SAAO,EAAE,KAAK,SAAS,MAAM,SAAS,KAAK,SAAS,OAAO,SAAS,QAAQ,QAAQ;AACtF;AAGO,SAAS,wBAAwB,SAAsD;AAC5F,SAAO;IACL,KAAK,MAAM;AACT,YAAM,EAAE,SAAS,IAAI,eAAe,OAAO;AAC3C,aAAO,SAAS,KAAK;QACnB,UAAU,SAAS,IAAI,CAAC,EAAE,MAAM,SAAS,OAAO,EAAE,MAAM,SAAS,EAAE;MACrE,CAAC;IACH;EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side glue for hosting <LensDashboard> — framework-agnostic (standard
|
|
3
|
+
* Request/Response, so it drops into Next.js App Router route files, Remix
|
|
4
|
+
* resource routes, etc.).
|
|
5
|
+
*
|
|
6
|
+
* This is the ONLY part of the package that reads environment variables, and
|
|
7
|
+
* it only ever executes on the host's server:
|
|
8
|
+
*
|
|
9
|
+
* LENS_PROJECTS JSON array: [{"name","accessId","secretKey"}, …]
|
|
10
|
+
* LENS_ACCESS_ID /
|
|
11
|
+
* LENS_SECRET_KEY legacy single-project fallback
|
|
12
|
+
* LENS_ADMIN_TOKEN optional — enables project creation from the wizard
|
|
13
|
+
* LENS_API_URL optional override (self-hosting / local dev)
|
|
14
|
+
*
|
|
15
|
+
* Usage (Next.js):
|
|
16
|
+
* // app/api/lens/[...path]/route.ts
|
|
17
|
+
* import { createLensProxy } from '@merittdev/horus-lens/server'
|
|
18
|
+
* export const { GET, POST, PUT, PATCH, DELETE } = createLensProxy()
|
|
19
|
+
*
|
|
20
|
+
* // app/api/lens-projects/route.ts
|
|
21
|
+
* import { createLensProjectsRoute } from '@merittdev/horus-lens/server'
|
|
22
|
+
* export const { GET } = createLensProjectsRoute()
|
|
23
|
+
*/
|
|
24
|
+
declare const HOSTED_LENS_API_URL = "https://api-lens.meritt.app";
|
|
25
|
+
interface LensProjectEntry {
|
|
26
|
+
name: string;
|
|
27
|
+
accessId: string;
|
|
28
|
+
secretKey: string;
|
|
29
|
+
}
|
|
30
|
+
interface LensServerOptions {
|
|
31
|
+
/** Lens API base. Default: env LENS_API_URL, else the hosted API. */
|
|
32
|
+
apiUrl?: string;
|
|
33
|
+
/** Project registry. Default: env LENS_PROJECTS (JSON), else the legacy pair. */
|
|
34
|
+
projects?: LensProjectEntry[];
|
|
35
|
+
/** Admin token for POST /v1/projects. Default: env LENS_ADMIN_TOKEN. */
|
|
36
|
+
adminToken?: string;
|
|
37
|
+
}
|
|
38
|
+
/** Next.js App Router passes ctx.params as a Promise (15+) or plain object. */
|
|
39
|
+
type RouteContext = {
|
|
40
|
+
params: {
|
|
41
|
+
path: string[];
|
|
42
|
+
} | Promise<{
|
|
43
|
+
path: string[];
|
|
44
|
+
}>;
|
|
45
|
+
};
|
|
46
|
+
type ProxyHandler = (req: Request, ctx: RouteContext) => Promise<Response>;
|
|
47
|
+
/**
|
|
48
|
+
* Authenticated pass-through to the Lens API. Credentials attach HERE, on the
|
|
49
|
+
* server: the browser only ever names a project via `x-lens-project`. During
|
|
50
|
+
* first-run provisioning the wizard forwards its just-minted secret via
|
|
51
|
+
* `x-lens-token`, which wins over the registry.
|
|
52
|
+
*/
|
|
53
|
+
declare function createLensProxy(options?: LensServerOptions): Record<'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', ProxyHandler>;
|
|
54
|
+
/** Names + access ids for the project switcher — never the secrets. */
|
|
55
|
+
declare function createLensProjectsRoute(options?: LensServerOptions): {
|
|
56
|
+
GET: () => Response;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export { HOSTED_LENS_API_URL, type LensProjectEntry, type LensServerOptions, createLensProjectsRoute, createLensProxy };
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side glue for hosting <LensDashboard> — framework-agnostic (standard
|
|
3
|
+
* Request/Response, so it drops into Next.js App Router route files, Remix
|
|
4
|
+
* resource routes, etc.).
|
|
5
|
+
*
|
|
6
|
+
* This is the ONLY part of the package that reads environment variables, and
|
|
7
|
+
* it only ever executes on the host's server:
|
|
8
|
+
*
|
|
9
|
+
* LENS_PROJECTS JSON array: [{"name","accessId","secretKey"}, …]
|
|
10
|
+
* LENS_ACCESS_ID /
|
|
11
|
+
* LENS_SECRET_KEY legacy single-project fallback
|
|
12
|
+
* LENS_ADMIN_TOKEN optional — enables project creation from the wizard
|
|
13
|
+
* LENS_API_URL optional override (self-hosting / local dev)
|
|
14
|
+
*
|
|
15
|
+
* Usage (Next.js):
|
|
16
|
+
* // app/api/lens/[...path]/route.ts
|
|
17
|
+
* import { createLensProxy } from '@merittdev/horus-lens/server'
|
|
18
|
+
* export const { GET, POST, PUT, PATCH, DELETE } = createLensProxy()
|
|
19
|
+
*
|
|
20
|
+
* // app/api/lens-projects/route.ts
|
|
21
|
+
* import { createLensProjectsRoute } from '@merittdev/horus-lens/server'
|
|
22
|
+
* export const { GET } = createLensProjectsRoute()
|
|
23
|
+
*/
|
|
24
|
+
declare const HOSTED_LENS_API_URL = "https://api-lens.meritt.app";
|
|
25
|
+
interface LensProjectEntry {
|
|
26
|
+
name: string;
|
|
27
|
+
accessId: string;
|
|
28
|
+
secretKey: string;
|
|
29
|
+
}
|
|
30
|
+
interface LensServerOptions {
|
|
31
|
+
/** Lens API base. Default: env LENS_API_URL, else the hosted API. */
|
|
32
|
+
apiUrl?: string;
|
|
33
|
+
/** Project registry. Default: env LENS_PROJECTS (JSON), else the legacy pair. */
|
|
34
|
+
projects?: LensProjectEntry[];
|
|
35
|
+
/** Admin token for POST /v1/projects. Default: env LENS_ADMIN_TOKEN. */
|
|
36
|
+
adminToken?: string;
|
|
37
|
+
}
|
|
38
|
+
/** Next.js App Router passes ctx.params as a Promise (15+) or plain object. */
|
|
39
|
+
type RouteContext = {
|
|
40
|
+
params: {
|
|
41
|
+
path: string[];
|
|
42
|
+
} | Promise<{
|
|
43
|
+
path: string[];
|
|
44
|
+
}>;
|
|
45
|
+
};
|
|
46
|
+
type ProxyHandler = (req: Request, ctx: RouteContext) => Promise<Response>;
|
|
47
|
+
/**
|
|
48
|
+
* Authenticated pass-through to the Lens API. Credentials attach HERE, on the
|
|
49
|
+
* server: the browser only ever names a project via `x-lens-project`. During
|
|
50
|
+
* first-run provisioning the wizard forwards its just-minted secret via
|
|
51
|
+
* `x-lens-token`, which wins over the registry.
|
|
52
|
+
*/
|
|
53
|
+
declare function createLensProxy(options?: LensServerOptions): Record<'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', ProxyHandler>;
|
|
54
|
+
/** Names + access ids for the project switcher — never the secrets. */
|
|
55
|
+
declare function createLensProjectsRoute(options?: LensServerOptions): {
|
|
56
|
+
GET: () => Response;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export { HOSTED_LENS_API_URL, type LensProjectEntry, type LensServerOptions, createLensProjectsRoute, createLensProxy };
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import "./chunk-PZ5AY32C.js";
|
|
2
|
+
|
|
3
|
+
// ../dashboard/dist/server.js
|
|
4
|
+
var HOSTED_LENS_API_URL = "https://api-lens.meritt.app";
|
|
5
|
+
function envProjects() {
|
|
6
|
+
const raw = process.env.LENS_PROJECTS;
|
|
7
|
+
if (raw) {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(raw);
|
|
10
|
+
if (Array.isArray(parsed)) {
|
|
11
|
+
return parsed.filter(
|
|
12
|
+
(p) => !!p && typeof p.name === "string" && typeof p.accessId === "string" && typeof p.secretKey === "string"
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
} catch {
|
|
16
|
+
console.warn("[lens] LENS_PROJECTS is not valid JSON \u2014 ignoring");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const accessId = process.env.LENS_ACCESS_ID;
|
|
20
|
+
const secretKey = process.env.LENS_SECRET_KEY;
|
|
21
|
+
if (accessId && secretKey) return [{ name: "default", accessId, secretKey }];
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
function resolveOptions(options) {
|
|
25
|
+
return {
|
|
26
|
+
apiUrl: (options?.apiUrl ?? process.env.LENS_API_URL ?? HOSTED_LENS_API_URL).replace(/\/+$/, ""),
|
|
27
|
+
projects: options?.projects ?? envProjects(),
|
|
28
|
+
adminToken: options?.adminToken ?? process.env.LENS_ADMIN_TOKEN ?? ""
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function pickProject(projects, selector) {
|
|
32
|
+
if (projects.length === 0) return null;
|
|
33
|
+
if (selector) {
|
|
34
|
+
const match = projects.find((p) => p.name === selector || p.accessId === selector);
|
|
35
|
+
if (match) return match;
|
|
36
|
+
}
|
|
37
|
+
return projects[0] ?? null;
|
|
38
|
+
}
|
|
39
|
+
function createLensProxy(options) {
|
|
40
|
+
const handler = async (req, ctx) => {
|
|
41
|
+
const { apiUrl, projects, adminToken } = resolveOptions(options);
|
|
42
|
+
const { path } = await ctx.params;
|
|
43
|
+
const search = new URL(req.url).search;
|
|
44
|
+
const target = `${apiUrl}/${path.join("/")}${search}`;
|
|
45
|
+
const isProjectCreate = req.method === "POST" && path.join("/") === "v1/projects";
|
|
46
|
+
const wizardToken = req.headers.get("x-lens-token");
|
|
47
|
+
const project = pickProject(projects, req.headers.get("x-lens-project"));
|
|
48
|
+
const token = isProjectCreate ? adminToken : wizardToken?.startsWith("las_") ? wizardToken : project?.secretKey ?? "";
|
|
49
|
+
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : await req.text();
|
|
50
|
+
const res = await fetch(target, {
|
|
51
|
+
method: req.method,
|
|
52
|
+
headers: {
|
|
53
|
+
authorization: `Bearer ${token}`,
|
|
54
|
+
...body ? { "content-type": req.headers.get("content-type") ?? "application/json" } : {}
|
|
55
|
+
},
|
|
56
|
+
body,
|
|
57
|
+
cache: "no-store"
|
|
58
|
+
});
|
|
59
|
+
const buffer = await res.arrayBuffer();
|
|
60
|
+
return new Response(buffer, {
|
|
61
|
+
status: res.status,
|
|
62
|
+
headers: { "content-type": res.headers.get("content-type") ?? "application/json" }
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
return { GET: handler, POST: handler, PUT: handler, PATCH: handler, DELETE: handler };
|
|
66
|
+
}
|
|
67
|
+
function createLensProjectsRoute(options) {
|
|
68
|
+
return {
|
|
69
|
+
GET: () => {
|
|
70
|
+
const { projects } = resolveOptions(options);
|
|
71
|
+
return Response.json({
|
|
72
|
+
projects: projects.map(({ name, accessId }) => ({ name, accessId }))
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
HOSTED_LENS_API_URL,
|
|
79
|
+
createLensProjectsRoute,
|
|
80
|
+
createLensProxy
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../dashboard/src/server/proxy.ts"],"sourcesContent":["/**\n * Server-side glue for hosting <LensDashboard> — framework-agnostic (standard\n * Request/Response, so it drops into Next.js App Router route files, Remix\n * resource routes, etc.).\n *\n * This is the ONLY part of the package that reads environment variables, and\n * it only ever executes on the host's server:\n *\n * LENS_PROJECTS JSON array: [{\"name\",\"accessId\",\"secretKey\"}, …]\n * LENS_ACCESS_ID /\n * LENS_SECRET_KEY legacy single-project fallback\n * LENS_ADMIN_TOKEN optional — enables project creation from the wizard\n * LENS_API_URL optional override (self-hosting / local dev)\n *\n * Usage (Next.js):\n * // app/api/lens/[...path]/route.ts\n * import { createLensProxy } from '@merittdev/horus-lens/server'\n * export const { GET, POST, PUT, PATCH, DELETE } = createLensProxy()\n *\n * // app/api/lens-projects/route.ts\n * import { createLensProjectsRoute } from '@merittdev/horus-lens/server'\n * export const { GET } = createLensProjectsRoute()\n */\n\n// Runs under Node on the host's server; typed locally to keep @types/node out\n// of this browser-focused package.\ndeclare const process: { env: Record<string, string | undefined> };\n\nexport const HOSTED_LENS_API_URL = 'https://api-lens.meritt.app';\n\nexport interface LensProjectEntry {\n name: string;\n accessId: string;\n secretKey: string;\n}\n\nexport interface LensServerOptions {\n /** Lens API base. Default: env LENS_API_URL, else the hosted API. */\n apiUrl?: string;\n /** Project registry. Default: env LENS_PROJECTS (JSON), else the legacy pair. */\n projects?: LensProjectEntry[];\n /** Admin token for POST /v1/projects. Default: env LENS_ADMIN_TOKEN. */\n adminToken?: string;\n}\n\nfunction envProjects(): LensProjectEntry[] {\n const raw = process.env.LENS_PROJECTS;\n if (raw) {\n try {\n const parsed: unknown = JSON.parse(raw);\n if (Array.isArray(parsed)) {\n return parsed.filter(\n (p): p is LensProjectEntry =>\n !!p &&\n typeof (p as LensProjectEntry).name === 'string' &&\n typeof (p as LensProjectEntry).accessId === 'string' &&\n typeof (p as LensProjectEntry).secretKey === 'string',\n );\n }\n } catch {\n console.warn('[lens] LENS_PROJECTS is not valid JSON — ignoring');\n }\n }\n const accessId = process.env.LENS_ACCESS_ID;\n const secretKey = process.env.LENS_SECRET_KEY;\n if (accessId && secretKey) return [{ name: 'default', accessId, secretKey }];\n return [];\n}\n\nfunction resolveOptions(options?: LensServerOptions) {\n return {\n apiUrl: (options?.apiUrl ?? process.env.LENS_API_URL ?? HOSTED_LENS_API_URL).replace(/\\/+$/, ''),\n projects: options?.projects ?? envProjects(),\n adminToken: options?.adminToken ?? process.env.LENS_ADMIN_TOKEN ?? '',\n };\n}\n\nfunction pickProject(projects: LensProjectEntry[], selector: string | null): LensProjectEntry | null {\n if (projects.length === 0) return null;\n if (selector) {\n const match = projects.find((p) => p.name === selector || p.accessId === selector);\n if (match) return match;\n }\n return projects[0] ?? null;\n}\n\n/** Next.js App Router passes ctx.params as a Promise (15+) or plain object. */\ntype RouteContext = { params: { path: string[] } | Promise<{ path: string[] }> };\ntype ProxyHandler = (req: Request, ctx: RouteContext) => Promise<Response>;\n\n/**\n * Authenticated pass-through to the Lens API. Credentials attach HERE, on the\n * server: the browser only ever names a project via `x-lens-project`. During\n * first-run provisioning the wizard forwards its just-minted secret via\n * `x-lens-token`, which wins over the registry.\n */\nexport function createLensProxy(options?: LensServerOptions): Record<\n 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n ProxyHandler\n> {\n const handler: ProxyHandler = async (req, ctx) => {\n const { apiUrl, projects, adminToken } = resolveOptions(options);\n const { path } = await ctx.params;\n const search = new URL(req.url).search;\n const target = `${apiUrl}/${path.join('/')}${search}`;\n\n const isProjectCreate = req.method === 'POST' && path.join('/') === 'v1/projects';\n const wizardToken = req.headers.get('x-lens-token');\n const project = pickProject(projects, req.headers.get('x-lens-project'));\n\n const token = isProjectCreate\n ? adminToken\n : wizardToken?.startsWith('las_')\n ? wizardToken\n : (project?.secretKey ?? '');\n\n const body = req.method === 'GET' || req.method === 'HEAD' ? undefined : await req.text();\n\n const res = await fetch(target, {\n method: req.method,\n headers: {\n authorization: `Bearer ${token}`,\n ...(body ? { 'content-type': req.headers.get('content-type') ?? 'application/json' } : {}),\n },\n body,\n cache: 'no-store',\n });\n\n const buffer = await res.arrayBuffer();\n return new Response(buffer, {\n status: res.status,\n headers: { 'content-type': res.headers.get('content-type') ?? 'application/json' },\n });\n };\n\n return { GET: handler, POST: handler, PUT: handler, PATCH: handler, DELETE: handler };\n}\n\n/** Names + access ids for the project switcher — never the secrets. */\nexport function createLensProjectsRoute(options?: LensServerOptions): { GET: () => Response } {\n return {\n GET: () => {\n const { projects } = resolveOptions(options);\n return Response.json({\n projects: projects.map(({ name, accessId }) => ({ name, accessId })),\n });\n },\n };\n}\n"],"mappings":";;;AA4BO,IAAM,sBAAsB;AAiBnC,SAAS,cAAkC;AACzC,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,KAAK;AACP,QAAI;AACF,YAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,eAAO,OAAO;UACZ,CAAC,MACC,CAAC,CAAC,KACF,OAAQ,EAAuB,SAAS,YACxC,OAAQ,EAAuB,aAAa,YAC5C,OAAQ,EAAuB,cAAc;QACjD;MACF;IACF,QAAQ;AACN,cAAQ,KAAK,wDAAmD;IAClE;EACF;AACA,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,YAAY,QAAQ,IAAI;AAC9B,MAAI,YAAY,UAAW,QAAO,CAAC,EAAE,MAAM,WAAW,UAAU,UAAU,CAAC;AAC3E,SAAO,CAAC;AACV;AAEA,SAAS,eAAe,SAA6B;AACnD,SAAO;IACL,SAAS,SAAS,UAAU,QAAQ,IAAI,gBAAgB,qBAAqB,QAAQ,QAAQ,EAAE;IAC/F,UAAU,SAAS,YAAY,YAAY;IAC3C,YAAY,SAAS,cAAc,QAAQ,IAAI,oBAAoB;EACrE;AACF;AAEA,SAAS,YAAY,UAA8B,UAAkD;AACnG,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,MAAI,UAAU;AACZ,UAAM,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,aAAa,QAAQ;AACjF,QAAI,MAAO,QAAO;EACpB;AACA,SAAO,SAAS,CAAC,KAAK;AACxB;AAYO,SAAS,gBAAgB,SAG9B;AACA,QAAM,UAAwB,OAAO,KAAK,QAAQ;AAChD,UAAM,EAAE,QAAQ,UAAU,WAAW,IAAI,eAAe,OAAO;AAC/D,UAAM,EAAE,KAAK,IAAI,MAAM,IAAI;AAC3B,UAAM,SAAS,IAAI,IAAI,IAAI,GAAG,EAAE;AAChC,UAAM,SAAS,GAAG,MAAM,IAAI,KAAK,KAAK,GAAG,CAAC,GAAG,MAAM;AAEnD,UAAM,kBAAkB,IAAI,WAAW,UAAU,KAAK,KAAK,GAAG,MAAM;AACpE,UAAM,cAAc,IAAI,QAAQ,IAAI,cAAc;AAClD,UAAM,UAAU,YAAY,UAAU,IAAI,QAAQ,IAAI,gBAAgB,CAAC;AAEvE,UAAM,QAAQ,kBACV,aACA,aAAa,WAAW,MAAM,IAC5B,cACC,SAAS,aAAa;AAE7B,UAAM,OAAO,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,SAAY,MAAM,IAAI,KAAK;AAExF,UAAM,MAAM,MAAM,MAAM,QAAQ;MAC9B,QAAQ,IAAI;MACZ,SAAS;QACP,eAAe,UAAU,KAAK;QAC9B,GAAI,OAAO,EAAE,gBAAgB,IAAI,QAAQ,IAAI,cAAc,KAAK,mBAAmB,IAAI,CAAC;MAC1F;MACA;MACA,OAAO;IACT,CAAC;AAED,UAAM,SAAS,MAAM,IAAI,YAAY;AACrC,WAAO,IAAI,SAAS,QAAQ;MAC1B,QAAQ,IAAI;MACZ,SAAS,EAAE,gBAAgB,IAAI,QAAQ,IAAI,cAAc,KAAK,mBAAmB;IACnF,CAAC;EACH;AAEA,SAAO,EAAE,KAAK,SAAS,MAAM,SAAS,KAAK,SAAS,OAAO,SAAS,QAAQ,QAAQ;AACtF;AAGO,SAAS,wBAAwB,SAAsD;AAC5F,SAAO;IACL,KAAK,MAAM;AACT,YAAM,EAAE,SAAS,IAAI,eAAe,OAAO;AAC3C,aAAO,SAAS,KAAK;QACnB,UAAU,SAAS,IAAI,CAAC,EAAE,MAAM,SAAS,OAAO,EAAE,MAAM,SAAS,EAAE;MACrE,CAAC;IACH;EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@merittdev/horus-lens",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Lens — context-rich issue capture for web apps: overlay, replay, and drop-in dashboard components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -34,7 +34,12 @@
|
|
|
34
34
|
},
|
|
35
35
|
"./dashboard/styles.css": "./dist/dashboard.css",
|
|
36
36
|
"./dist/dashboard.css": "./dist/dashboard.css",
|
|
37
|
-
"./cdn": "./dist/lens.min.js"
|
|
37
|
+
"./cdn": "./dist/lens.min.js",
|
|
38
|
+
"./server": {
|
|
39
|
+
"types": "./dist/server.d.ts",
|
|
40
|
+
"import": "./dist/server.js",
|
|
41
|
+
"require": "./dist/server.cjs"
|
|
42
|
+
}
|
|
38
43
|
},
|
|
39
44
|
"peerDependencies": {
|
|
40
45
|
"react": ">=18",
|
|
@@ -51,15 +56,36 @@
|
|
|
51
56
|
"devDependencies": {
|
|
52
57
|
"tsup": "^8.4.0",
|
|
53
58
|
"typescript": "^5.7.3",
|
|
54
|
-
"@horus-lens/core": "0.0.1",
|
|
55
59
|
"@horus-lens/browser": "0.0.1",
|
|
56
|
-
"@horus-lens/
|
|
60
|
+
"@horus-lens/core": "0.0.1",
|
|
57
61
|
"@horus-lens/dashboard": "0.0.1",
|
|
62
|
+
"@horus-lens/react": "0.0.1",
|
|
58
63
|
"@horus-lens/shared": "0.0.1"
|
|
59
64
|
},
|
|
60
65
|
"publishConfig": {
|
|
61
66
|
"access": "public"
|
|
62
67
|
},
|
|
68
|
+
"keywords": [
|
|
69
|
+
"bug-reporting",
|
|
70
|
+
"session-replay",
|
|
71
|
+
"rrweb",
|
|
72
|
+
"feedback",
|
|
73
|
+
"issue-tracking",
|
|
74
|
+
"linear",
|
|
75
|
+
"jira",
|
|
76
|
+
"github",
|
|
77
|
+
"screenshot",
|
|
78
|
+
"developer-tools"
|
|
79
|
+
],
|
|
80
|
+
"repository": {
|
|
81
|
+
"type": "git",
|
|
82
|
+
"url": "git+https://github.com/Meritt-dev/horus-lens.git",
|
|
83
|
+
"directory": "packages/lens"
|
|
84
|
+
},
|
|
85
|
+
"homepage": "https://github.com/Meritt-dev/horus-lens#readme",
|
|
86
|
+
"bugs": {
|
|
87
|
+
"url": "https://github.com/Meritt-dev/horus-lens/issues"
|
|
88
|
+
},
|
|
63
89
|
"scripts": {
|
|
64
90
|
"build": "tsup && node scripts/copy-assets.mjs",
|
|
65
91
|
"typecheck": "tsc --noEmit",
|
|
File without changes
|