@lumerahq/cli 0.30.12 → 0.30.14
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/{chunk-ROARJJVX.js → chunk-AEZLQMFF.js} +3 -3
- package/dist/{functions-G37WQWTG.js → functions-DZCNHDRJ.js} +1 -1
- package/dist/index.js +2 -2
- package/dist/{project-GAVODH6S.js → project-3HITJUM4.js} +5 -40
- package/package.json +1 -1
- package/templates/default/pyproject.toml +1 -1
- package/templates/default/src/lib/auth.ts +39 -1
- package/templates/default/src/main.tsx +127 -7
|
@@ -5,11 +5,11 @@ import { join, resolve } from "path";
|
|
|
5
5
|
import pc from "picocolors";
|
|
6
6
|
|
|
7
7
|
// src/lib/functions-sdk.ts
|
|
8
|
-
var FUNCTIONS_SDK_VERSION_RANGE = ">=0.
|
|
9
|
-
var FUNCTIONS_SDK_COMPATIBILITY_RANGE = ">=0.34.0,<0.
|
|
8
|
+
var FUNCTIONS_SDK_VERSION_RANGE = ">=0.38.1,<0.39.0";
|
|
9
|
+
var FUNCTIONS_SDK_COMPATIBILITY_RANGE = ">=0.34.0,<0.39.0";
|
|
10
10
|
var FUNCTIONS_SDK_REQUIREMENT = `lumera[functions]${FUNCTIONS_SDK_COMPATIBILITY_RANGE}`;
|
|
11
11
|
var DEFAULT_FUNCTIONS_SDK_REQUIREMENT = `lumera[functions]${FUNCTIONS_SDK_VERSION_RANGE}`;
|
|
12
|
-
var RELEASE_SDK_REQUIREMENT = `lumera[release]${
|
|
12
|
+
var RELEASE_SDK_REQUIREMENT = `lumera[release]${FUNCTIONS_SDK_VERSION_RANGE}`;
|
|
13
13
|
|
|
14
14
|
// src/commands/functions.ts
|
|
15
15
|
var SUPPORTED_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "inspect", "invoke", "test"]);
|
package/dist/index.js
CHANGED
|
@@ -268,12 +268,12 @@ async function main() {
|
|
|
268
268
|
await import("./run-EBTA3FKD.js").then((m) => m.run(args.slice(1)));
|
|
269
269
|
break;
|
|
270
270
|
case "functions":
|
|
271
|
-
await import("./functions-
|
|
271
|
+
await import("./functions-DZCNHDRJ.js").then(
|
|
272
272
|
(m) => m.functions(subcommand, args.slice(2))
|
|
273
273
|
);
|
|
274
274
|
break;
|
|
275
275
|
case "project":
|
|
276
|
-
await import("./project-
|
|
276
|
+
await import("./project-3HITJUM4.js").then(
|
|
277
277
|
(m) => m.project(subcommand, args.slice(2))
|
|
278
278
|
);
|
|
279
279
|
break;
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RELEASE_SDK_REQUIREMENT,
|
|
3
3
|
findFunctionsProjectRoot
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-AEZLQMFF.js";
|
|
5
5
|
import "./chunk-PNKVD2UK.js";
|
|
6
6
|
|
|
7
7
|
// src/commands/project.ts
|
|
8
8
|
import { spawn } from "child_process";
|
|
9
|
-
import { mkdirSync } from "fs";
|
|
10
|
-
import { join } from "path";
|
|
11
9
|
import pc from "picocolors";
|
|
12
10
|
var RELEASE_PROTOCOL_VERSION = 1;
|
|
13
11
|
var RELEASE_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -17,32 +15,6 @@ var RELEASE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
17
15
|
"deploy",
|
|
18
16
|
"deploy-runtime"
|
|
19
17
|
]);
|
|
20
|
-
function releaseCommandOutput(command, args, result) {
|
|
21
|
-
if (command !== "deploy" || !result.envelope.ok || result.envelope.activation_required === true) {
|
|
22
|
-
return result.raw;
|
|
23
|
-
}
|
|
24
|
-
const deployment = result.envelope.deployment;
|
|
25
|
-
const release = typeof deployment === "object" && deployment !== null ? deployment.release : void 0;
|
|
26
|
-
const releaseId = typeof release === "object" && release !== null ? String(release.id ?? "") : "";
|
|
27
|
-
const projectIndex = args.indexOf("--project");
|
|
28
|
-
const projectRef = projectIndex >= 0 ? String(args[projectIndex + 1] ?? "") : "";
|
|
29
|
-
if (!releaseId || !projectRef) return result.raw;
|
|
30
|
-
return `${JSON.stringify({
|
|
31
|
-
...result.envelope,
|
|
32
|
-
activation_required: true,
|
|
33
|
-
message: "Release uploaded but inactive; deploy it before invoking Functions.",
|
|
34
|
-
next_command: [
|
|
35
|
-
"lumera",
|
|
36
|
-
"project",
|
|
37
|
-
"release",
|
|
38
|
-
"deploy-runtime",
|
|
39
|
-
releaseId,
|
|
40
|
-
"--project",
|
|
41
|
-
projectRef
|
|
42
|
-
]
|
|
43
|
-
})}
|
|
44
|
-
`;
|
|
45
|
-
}
|
|
46
18
|
function showHelp() {
|
|
47
19
|
console.log(`
|
|
48
20
|
${pc.dim("Usage:")}
|
|
@@ -76,17 +48,11 @@ function buildReleaseRunnerArgs(command, args, projectRoot) {
|
|
|
76
48
|
...args
|
|
77
49
|
];
|
|
78
50
|
}
|
|
79
|
-
async function runReleaseRunner(runnerArgs, projectRoot,
|
|
80
|
-
let runnerEnvironment = process.env;
|
|
81
|
-
if (releaseCommand === "build") {
|
|
82
|
-
const releaseTempRoot = join(projectRoot, ".lumera", "tmp");
|
|
83
|
-
mkdirSync(releaseTempRoot, { recursive: true, mode: 448 });
|
|
84
|
-
runnerEnvironment = { ...process.env, TMPDIR: releaseTempRoot };
|
|
85
|
-
}
|
|
51
|
+
async function runReleaseRunner(runnerArgs, projectRoot, spawnRunner = (command, args, options) => spawn(command, args, options)) {
|
|
86
52
|
return await new Promise((resolvePromise, reject) => {
|
|
87
53
|
const child = spawnRunner("uv", runnerArgs, {
|
|
88
54
|
cwd: projectRoot,
|
|
89
|
-
env:
|
|
55
|
+
env: process.env,
|
|
90
56
|
shell: false,
|
|
91
57
|
stdio: ["inherit", "pipe", "inherit"]
|
|
92
58
|
});
|
|
@@ -130,10 +96,9 @@ async function project(subcommand, args, dependencies = {}) {
|
|
|
130
96
|
const projectRoot = findProjectRoot();
|
|
131
97
|
const result = await runRunner(
|
|
132
98
|
buildReleaseRunnerArgs(releaseCommand, releaseArgs, projectRoot),
|
|
133
|
-
projectRoot
|
|
134
|
-
releaseCommand
|
|
99
|
+
projectRoot
|
|
135
100
|
);
|
|
136
|
-
writeOutput(
|
|
101
|
+
writeOutput(result.raw);
|
|
137
102
|
if (result.code !== 0) process.exitCode = result.code;
|
|
138
103
|
} catch (error) {
|
|
139
104
|
writeOutput(
|
package/package.json
CHANGED
|
@@ -1,9 +1,47 @@
|
|
|
1
|
-
import { createContext } from 'react';
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
export type ViewerMemberTag = {
|
|
4
|
+
id: string;
|
|
5
|
+
key: string;
|
|
6
|
+
label: string;
|
|
7
|
+
color: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type ViewerContext = {
|
|
11
|
+
version: 1;
|
|
12
|
+
isSignedIn: boolean;
|
|
13
|
+
subjectType: 'anonymous' | 'org_member' | 'public_app_user';
|
|
14
|
+
belongsToOrganization: boolean;
|
|
15
|
+
identity: { id: string; name: string; email: string } | null;
|
|
16
|
+
organizationMembership: {
|
|
17
|
+
id: string;
|
|
18
|
+
role: string;
|
|
19
|
+
isCompanyAdmin: boolean;
|
|
20
|
+
department: ViewerMemberTag | null;
|
|
21
|
+
tags: ViewerMemberTag[];
|
|
22
|
+
} | null;
|
|
23
|
+
appUser: { id: string; role: string } | null;
|
|
24
|
+
};
|
|
2
25
|
|
|
3
26
|
export type AuthContextValue = {
|
|
4
27
|
company?: { id: string; name?: string; apiName?: string };
|
|
5
28
|
user?: { id: string; name: string; email: string; role?: string };
|
|
29
|
+
viewer: ViewerContext;
|
|
6
30
|
sessionToken?: string;
|
|
7
31
|
};
|
|
8
32
|
|
|
9
33
|
export const AuthContext = createContext<AuthContextValue | null>(null);
|
|
34
|
+
|
|
35
|
+
export const useAuth = (): AuthContextValue => {
|
|
36
|
+
const value = useContext(AuthContext);
|
|
37
|
+
if (!value) throw new Error('useAuth must be used inside AuthContext.Provider');
|
|
38
|
+
return value;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const hasMemberTag = (viewer: ViewerContext, key: string): boolean =>
|
|
42
|
+
viewer.organizationMembership?.tags.some(
|
|
43
|
+
(tag) => tag.key.trim().toLowerCase() === key.trim().toLowerCase()
|
|
44
|
+
) === true;
|
|
45
|
+
|
|
46
|
+
export const isDepartment = (viewer: ViewerContext, key: string): boolean =>
|
|
47
|
+
viewer.organizationMembership?.department?.key.trim().toLowerCase() === key.trim().toLowerCase();
|
|
@@ -11,7 +11,12 @@ import { StrictMode, useEffect, useRef, useState } from 'react';
|
|
|
11
11
|
import ReactDOM from 'react-dom/client';
|
|
12
12
|
import { Toaster } from 'sonner';
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
AuthContext,
|
|
16
|
+
type AuthContextValue,
|
|
17
|
+
type ViewerContext,
|
|
18
|
+
type ViewerMemberTag,
|
|
19
|
+
} from './lib/auth';
|
|
15
20
|
import { routeTree } from './routeTree.gen';
|
|
16
21
|
import './styles.css';
|
|
17
22
|
|
|
@@ -39,6 +44,117 @@ declare module '@tanstack/react-router' {
|
|
|
39
44
|
|
|
40
45
|
const ROUTE_STORAGE_KEY = '{{projectName}}-route';
|
|
41
46
|
|
|
47
|
+
type ViewerHostPayload = HostPayload & { viewer?: ViewerContext };
|
|
48
|
+
|
|
49
|
+
type ViewerResponse = {
|
|
50
|
+
version?: number;
|
|
51
|
+
is_signed_in?: boolean;
|
|
52
|
+
subject_type?: 'anonymous' | 'org_member' | 'public_app_user';
|
|
53
|
+
belongs_to_organization?: boolean;
|
|
54
|
+
identity?: { id?: string; name?: string; email?: string } | null;
|
|
55
|
+
organization_membership?: {
|
|
56
|
+
id?: string;
|
|
57
|
+
role?: string;
|
|
58
|
+
is_company_admin?: boolean;
|
|
59
|
+
department?: ViewerMemberTag | null;
|
|
60
|
+
tags?: ViewerMemberTag[];
|
|
61
|
+
} | null;
|
|
62
|
+
app_user?: { id?: string; role?: string } | null;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const viewerFromPayload = (payload: ViewerHostPayload): ViewerContext => {
|
|
66
|
+
if (payload.viewer) return payload.viewer;
|
|
67
|
+
const user = payload.user;
|
|
68
|
+
if (user && (payload.subjectType === 'app_user' || payload.subjectType === 'public_app_user')) {
|
|
69
|
+
return {
|
|
70
|
+
version: 1,
|
|
71
|
+
isSignedIn: true,
|
|
72
|
+
subjectType: 'public_app_user',
|
|
73
|
+
belongsToOrganization: false,
|
|
74
|
+
identity: { id: user.id, name: user.name, email: user.email },
|
|
75
|
+
organizationMembership: null,
|
|
76
|
+
appUser: { id: user.id, role: user.role ?? '' },
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (user) {
|
|
80
|
+
return {
|
|
81
|
+
version: 1,
|
|
82
|
+
isSignedIn: true,
|
|
83
|
+
subjectType: 'org_member',
|
|
84
|
+
belongsToOrganization: true,
|
|
85
|
+
identity: { id: user.id, name: user.name, email: user.email },
|
|
86
|
+
organizationMembership: {
|
|
87
|
+
id: user.id,
|
|
88
|
+
role: user.role ?? '',
|
|
89
|
+
isCompanyAdmin: user.role?.toLowerCase() === 'admin',
|
|
90
|
+
department: null,
|
|
91
|
+
tags: [],
|
|
92
|
+
},
|
|
93
|
+
appUser: null,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
version: 1,
|
|
98
|
+
isSignedIn: false,
|
|
99
|
+
subjectType: 'anonymous',
|
|
100
|
+
belongsToOrganization: false,
|
|
101
|
+
identity: null,
|
|
102
|
+
organizationMembership: null,
|
|
103
|
+
appUser: null,
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const viewerFromResponse = (response: ViewerResponse): ViewerContext => ({
|
|
108
|
+
version: 1,
|
|
109
|
+
isSignedIn: response.is_signed_in === true,
|
|
110
|
+
subjectType: response.subject_type ?? 'anonymous',
|
|
111
|
+
belongsToOrganization: response.belongs_to_organization === true,
|
|
112
|
+
identity: response.identity
|
|
113
|
+
? {
|
|
114
|
+
id: response.identity.id ?? '',
|
|
115
|
+
name: response.identity.name ?? '',
|
|
116
|
+
email: response.identity.email ?? '',
|
|
117
|
+
}
|
|
118
|
+
: null,
|
|
119
|
+
organizationMembership: response.organization_membership
|
|
120
|
+
? {
|
|
121
|
+
id: response.organization_membership.id ?? '',
|
|
122
|
+
role: response.organization_membership.role ?? '',
|
|
123
|
+
isCompanyAdmin: response.organization_membership.is_company_admin === true,
|
|
124
|
+
department: response.organization_membership.department ?? null,
|
|
125
|
+
tags: response.organization_membership.tags ?? [],
|
|
126
|
+
}
|
|
127
|
+
: null,
|
|
128
|
+
appUser: response.app_user
|
|
129
|
+
? {
|
|
130
|
+
id: response.app_user.id ?? '',
|
|
131
|
+
role: response.app_user.role ?? '',
|
|
132
|
+
}
|
|
133
|
+
: null,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const resolveViewer = async (payload: ViewerHostPayload): Promise<ViewerContext> => {
|
|
137
|
+
if (payload.viewer) return payload.viewer;
|
|
138
|
+
|
|
139
|
+
// The checked-in lockfile can briefly trail a newly published UI bridge.
|
|
140
|
+
// Keep standalone apps complete during that release window by reading the
|
|
141
|
+
// canonical block from the same authenticated /api/me endpoint.
|
|
142
|
+
if (!isEmbedded()) {
|
|
143
|
+
try {
|
|
144
|
+
const response = await fetch('/api/me', { credentials: 'same-origin' });
|
|
145
|
+
if (response.ok) {
|
|
146
|
+
const body = (await response.json()) as { viewer?: ViewerResponse };
|
|
147
|
+
if (body.viewer) return viewerFromResponse(body.viewer);
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
// Compatibility fallback below preserves the pre-viewer behavior when
|
|
151
|
+
// an older app host does not yet expose the canonical response.
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return viewerFromPayload(payload);
|
|
156
|
+
};
|
|
157
|
+
|
|
42
158
|
function RouteRestorer() {
|
|
43
159
|
const isFirstLoad = useRef(true);
|
|
44
160
|
|
|
@@ -75,7 +191,8 @@ const App = () => {
|
|
|
75
191
|
// in the Lumera shell, same-origin /api with the app-session cookie when
|
|
76
192
|
// served standalone on the app's own hostname, and direct dev fetch when
|
|
77
193
|
// VITE_DEV_API_BASE_URL is set.
|
|
78
|
-
const cleanup = onInitMessage((
|
|
194
|
+
const cleanup = onInitMessage((rawPayload?: HostPayload) => {
|
|
195
|
+
const payload = rawPayload as ViewerHostPayload | undefined;
|
|
79
196
|
if (!payload) {
|
|
80
197
|
// No identity and no app context: authentication is required
|
|
81
198
|
// (payload.user alone being undefined means an anonymous visitor on
|
|
@@ -83,12 +200,15 @@ const App = () => {
|
|
|
83
200
|
setStatus('unauthenticated');
|
|
84
201
|
return;
|
|
85
202
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
203
|
+
void resolveViewer(payload).then((viewer) => {
|
|
204
|
+
setHostContext({
|
|
205
|
+
company: payload.company,
|
|
206
|
+
user: payload.user,
|
|
207
|
+
viewer,
|
|
208
|
+
sessionToken: payload.session?.token,
|
|
209
|
+
});
|
|
210
|
+
setStatus('ready');
|
|
90
211
|
});
|
|
91
|
-
setStatus('ready');
|
|
92
212
|
});
|
|
93
213
|
|
|
94
214
|
postReadyMessage();
|