@burdenoff/microfe-devportal 2026.1102.1 → 2026.1107.2
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/devportal/constants/appCategories.d.ts +26 -0
- package/dist/devportal/constants/appCategories.d.ts.map +1 -0
- package/dist/devportal/constants/appCategories.js +45 -0
- package/dist/devportal/constants/appCategories.js.map +1 -0
- package/dist/devportal/hooks/usePackageUpload.d.ts +3 -0
- package/dist/devportal/hooks/usePackageUpload.d.ts.map +1 -1
- package/dist/devportal/hooks/usePackageUpload.js +41 -32
- package/dist/devportal/hooks/usePackageUpload.js.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.d.ts.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.js +233 -232
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/CreateApplicationPage.d.ts.map +1 -1
- package/dist/devportal/pages/CreateApplicationPage.js +100 -128
- package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.d.ts.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.js +24 -52
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/SetupPage.d.ts.map +1 -1
- package/dist/devportal/pages/SetupPage.js +20 -69
- package/dist/devportal/pages/SetupPage.js.map +1 -1
- package/dist/devportal/utils/uploadErrors.d.ts +46 -0
- package/dist/devportal/utils/uploadErrors.d.ts.map +1 -0
- package/dist/devportal/utils/uploadErrors.js +62 -0
- package/dist/devportal/utils/uploadErrors.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application categories offered by the developer portal.
|
|
3
|
+
*
|
|
4
|
+
* These are the canonical FluidGrids store categories, so an app maps onto
|
|
5
|
+
* the store's browse panel. Every form that sets an application's category
|
|
6
|
+
* (the setup wizard, Create Application and Edit Application) must use this
|
|
7
|
+
* one list: when the setup wizard used its own list, an app created there
|
|
8
|
+
* opened in Edit Application with an empty, required Category field, and the
|
|
9
|
+
* developer had to replace the category just to change visibility
|
|
10
|
+
* (BOFF-10636).
|
|
11
|
+
*/
|
|
12
|
+
export interface AppCategoryOption {
|
|
13
|
+
value: string;
|
|
14
|
+
label: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const APP_CATEGORY_OPTIONS: readonly AppCategoryOption[];
|
|
17
|
+
/**
|
|
18
|
+
* Category options for a form editing an existing application.
|
|
19
|
+
*
|
|
20
|
+
* Apps created before the categories were aligned can still carry an older
|
|
21
|
+
* value (for example "productivity"). That value is kept as a selectable
|
|
22
|
+
* option so opening the edit form neither blanks the field nor silently
|
|
23
|
+
* changes the app's category on save.
|
|
24
|
+
*/
|
|
25
|
+
export declare function appCategoryOptionsFor(currentCategory: string | null | undefined): AppCategoryOption[];
|
|
26
|
+
//# sourceMappingURL=appCategories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appCategories.d.ts","sourceRoot":"","sources":["../../../src/devportal/constants/appCategories.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,oBAAoB,EAAE,SAAS,iBAAiB,EAQ5D,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACzC,iBAAiB,EAAE,CAOrB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/devportal/constants/appCategories.ts
|
|
2
|
+
var e = [
|
|
3
|
+
{
|
|
4
|
+
value: "ai",
|
|
5
|
+
label: "AI & Automation"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
value: "integration",
|
|
9
|
+
label: "Action in an App"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
value: "data",
|
|
13
|
+
label: "Data & Transform"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
value: "flow",
|
|
17
|
+
label: "Flow & Logic"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: "core",
|
|
21
|
+
label: "Core"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
value: "communication",
|
|
25
|
+
label: "Communication"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
value: "trigger",
|
|
29
|
+
label: "Triggers"
|
|
30
|
+
}
|
|
31
|
+
];
|
|
32
|
+
function t(t) {
|
|
33
|
+
let r = [...e], i = t?.trim();
|
|
34
|
+
return i && !r.some((e) => e.value === i) && r.push({
|
|
35
|
+
value: i,
|
|
36
|
+
label: n(i)
|
|
37
|
+
}), r;
|
|
38
|
+
}
|
|
39
|
+
function n(e) {
|
|
40
|
+
return e.split(/[-_\s]+/).filter(Boolean).map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join(" ");
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { e as APP_CATEGORY_OPTIONS, t as appCategoryOptionsFor };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=appCategories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appCategories.js","names":[],"sources":["../../../src/devportal/constants/appCategories.ts"],"sourcesContent":["/**\n * Application categories offered by the developer portal.\n *\n * These are the canonical FluidGrids store categories, so an app maps onto\n * the store's browse panel. Every form that sets an application's category\n * (the setup wizard, Create Application and Edit Application) must use this\n * one list: when the setup wizard used its own list, an app created there\n * opened in Edit Application with an empty, required Category field, and the\n * developer had to replace the category just to change visibility\n * (BOFF-10636).\n */\n\nexport interface AppCategoryOption {\n value: string;\n label: string;\n}\n\nexport const APP_CATEGORY_OPTIONS: readonly AppCategoryOption[] = [\n { value: \"ai\", label: \"AI & Automation\" },\n { value: \"integration\", label: \"Action in an App\" },\n { value: \"data\", label: \"Data & Transform\" },\n { value: \"flow\", label: \"Flow & Logic\" },\n { value: \"core\", label: \"Core\" },\n { value: \"communication\", label: \"Communication\" },\n { value: \"trigger\", label: \"Triggers\" },\n];\n\n/**\n * Category options for a form editing an existing application.\n *\n * Apps created before the categories were aligned can still carry an older\n * value (for example \"productivity\"). That value is kept as a selectable\n * option so opening the edit form neither blanks the field nor silently\n * changes the app's category on save.\n */\nexport function appCategoryOptionsFor(\n currentCategory: string | null | undefined,\n): AppCategoryOption[] {\n const options = [...APP_CATEGORY_OPTIONS];\n const current = currentCategory?.trim();\n if (current && !options.some((option) => option.value === current)) {\n options.push({ value: current, label: humanizeCategory(current) });\n }\n return options;\n}\n\nfunction humanizeCategory(value: string): string {\n return value\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n"],"mappings":";AAiBA,IAAa,IAAqD;CAChE;EAAE,OAAO;EAAM,OAAO;EAAmB;CACzC;EAAE,OAAO;EAAe,OAAO;EAAoB;CACnD;EAAE,OAAO;EAAQ,OAAO;EAAoB;CAC5C;EAAE,OAAO;EAAQ,OAAO;EAAgB;CACxC;EAAE,OAAO;EAAQ,OAAO;EAAQ;CAChC;EAAE,OAAO;EAAiB,OAAO;EAAiB;CAClD;EAAE,OAAO;EAAW,OAAO;EAAY;CACxC;AAUD,SAAgB,EACd,GACqB;CACrB,IAAM,IAAU,CAAC,GAAG,EAAqB,EACnC,IAAU,GAAiB,MAAM;AAIvC,QAHI,KAAW,CAAC,EAAQ,MAAM,MAAW,EAAO,UAAU,EAAQ,IAChE,EAAQ,KAAK;EAAE,OAAO;EAAS,OAAO,EAAiB,EAAQ;EAAE,CAAC,EAE7D;;AAGT,SAAS,EAAiB,GAAuB;AAC/C,QAAO,EACJ,MAAM,UAAU,CAChB,OAAO,QAAQ,CACf,KAAK,MAAS,EAAK,OAAO,EAAE,CAAC,aAAa,GAAG,EAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,IAAI"}
|
|
@@ -10,10 +10,13 @@ export interface PackageUploadState {
|
|
|
10
10
|
progress: number;
|
|
11
11
|
error: string | null;
|
|
12
12
|
}
|
|
13
|
+
type GqlFetcher = (query: string, variables: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
14
|
+
export declare function makeFetcher(workspacesGatewayUrl: string, authToken: string | undefined, workspaceId: string | undefined, workspaceToken: string | undefined): GqlFetcher;
|
|
13
15
|
export declare function usePackageUpload(workspacesGatewayUrl: string | undefined, authToken: string | undefined, workspaceId: string | undefined, getWorkspaceToken: (() => Promise<string | null>) | undefined, folderPath?: string): {
|
|
14
16
|
uploading: boolean;
|
|
15
17
|
progress: number;
|
|
16
18
|
error: string | null;
|
|
17
19
|
upload: (file: File) => Promise<PackageUploadResult>;
|
|
18
20
|
};
|
|
21
|
+
export {};
|
|
19
22
|
//# sourceMappingURL=usePackageUpload.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePackageUpload.d.ts","sourceRoot":"","sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"usePackageUpload.d.ts","sourceRoot":"","sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf;;qDAEiD;IACjD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,KAAK,UAAU,GAAG,CAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC/B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAWtC,wBAAgB,WAAW,CACzB,oBAAoB,EAAE,MAAM,EAC5B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,UAAU,CAoCZ;AAsHD,wBAAgB,gBAAgB,CAC9B,oBAAoB,EAAE,MAAM,GAAG,SAAS,EACxC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,iBAAiB,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,SAAS,EAC7D,UAAU,SAAwB;eAvLvB,OAAO;cACR,MAAM;WACT,MAAM,GAAG,IAAI;mBA8LL,IAAI,KAAG,OAAO,CAAC,mBAAmB,CAAC;EAoDnD"}
|
|
@@ -1,30 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { graphQLErrorFrom as e, httpErrorFrom as t } from "../utils/uploadErrors.js";
|
|
2
|
+
import { useCallback as n, useState as r } from "react";
|
|
2
3
|
//#region src/devportal/hooks/usePackageUpload.ts
|
|
3
|
-
function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
async function i(e) {
|
|
5
|
+
try {
|
|
6
|
+
return await e.json();
|
|
7
|
+
} catch {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function a(n, r, a, o) {
|
|
12
|
+
return async (s, c) => {
|
|
13
|
+
let l = { "Content-Type": "application/json" };
|
|
14
|
+
r && (l.Authorization = `Bearer ${r}`), o && (l["X-Workspace-Authorization"] = `Bearer ${o}`), a && (l["x-workspace-id"] = a);
|
|
15
|
+
let u = n.endsWith("/graphql") ? n : `${n}/workspaces/graphql`, d = await fetch(u, {
|
|
8
16
|
method: "POST",
|
|
9
|
-
headers:
|
|
17
|
+
headers: l,
|
|
10
18
|
credentials: "include",
|
|
11
19
|
body: JSON.stringify({
|
|
12
|
-
query:
|
|
13
|
-
variables:
|
|
20
|
+
query: s,
|
|
21
|
+
variables: c
|
|
14
22
|
})
|
|
15
|
-
});
|
|
16
|
-
if (!
|
|
17
|
-
let
|
|
18
|
-
if (
|
|
19
|
-
|
|
23
|
+
}), f = await i(d);
|
|
24
|
+
if (!d.ok) throw t(d.status, d.statusText, f);
|
|
25
|
+
let p = e(f);
|
|
26
|
+
if (p) throw p;
|
|
27
|
+
let m = f?.data;
|
|
28
|
+
return typeof m == "object" && m ? m : {};
|
|
20
29
|
};
|
|
21
30
|
}
|
|
22
|
-
async function
|
|
31
|
+
async function o(e, t) {
|
|
23
32
|
let n = (await e("mutation DevPortal_EnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }", { path: t })).ensureFolderByPath?.id;
|
|
24
33
|
if (!n) throw Error("ensureFolderByPath returned no id");
|
|
25
34
|
return n;
|
|
26
35
|
}
|
|
27
|
-
async function
|
|
36
|
+
async function s(e, t, n) {
|
|
28
37
|
let r = t.name.includes(".") ? t.name.substring(t.name.lastIndexOf(".") + 1) : "zip", i = (await e("mutation DevPortal_InitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }", { input: {
|
|
29
38
|
fileName: t.name,
|
|
30
39
|
fileSize: String(t.size),
|
|
@@ -39,7 +48,7 @@ async function i(e, t, n) {
|
|
|
39
48
|
uploadUrl: o
|
|
40
49
|
};
|
|
41
50
|
}
|
|
42
|
-
async function
|
|
51
|
+
async function c(e, t, n) {
|
|
43
52
|
await new Promise((r, i) => {
|
|
44
53
|
let a = new XMLHttpRequest();
|
|
45
54
|
a.upload.addEventListener("progress", (e) => {
|
|
@@ -49,7 +58,7 @@ async function a(e, t, n) {
|
|
|
49
58
|
}), a.addEventListener("error", () => i(/* @__PURE__ */ Error("S3 PUT network error"))), a.addEventListener("abort", () => i(/* @__PURE__ */ Error("S3 PUT aborted"))), a.open("PUT", t), a.setRequestHeader("Content-Type", e.type || "application/zip"), a.send(e);
|
|
50
59
|
});
|
|
51
60
|
}
|
|
52
|
-
async function
|
|
61
|
+
async function l(e, t, n, r) {
|
|
53
62
|
let i = (await e("mutation DevPortal_CompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n file { id downloadUrl publicUrl }\n }\n }", { input: {
|
|
54
63
|
sessionId: t,
|
|
55
64
|
folderId: n,
|
|
@@ -61,44 +70,44 @@ async function o(e, t, n, r) {
|
|
|
61
70
|
downloadUrl: a
|
|
62
71
|
};
|
|
63
72
|
}
|
|
64
|
-
function
|
|
65
|
-
let [f, p] =
|
|
73
|
+
function u(e, t, i, u, d = "/DevPortal/Packages") {
|
|
74
|
+
let [f, p] = r({
|
|
66
75
|
uploading: !1,
|
|
67
76
|
progress: 0,
|
|
68
77
|
error: null
|
|
69
78
|
});
|
|
70
79
|
return {
|
|
71
|
-
upload:
|
|
72
|
-
if (!
|
|
73
|
-
let
|
|
80
|
+
upload: n(async (n) => {
|
|
81
|
+
if (!e) throw Error("workspacesGatewayUrl is not configured. Pass it as a prop to DevportalRoot.");
|
|
82
|
+
let r = a(e, t, i, (u ? await u() : void 0) ?? void 0);
|
|
74
83
|
p({
|
|
75
84
|
uploading: !0,
|
|
76
85
|
progress: 0,
|
|
77
86
|
error: null
|
|
78
87
|
});
|
|
79
88
|
try {
|
|
80
|
-
let
|
|
89
|
+
let e = await o(r, d);
|
|
81
90
|
p((e) => ({
|
|
82
91
|
...e,
|
|
83
92
|
progress: 5
|
|
84
93
|
}));
|
|
85
|
-
let { sessionId:
|
|
94
|
+
let { sessionId: t, uploadUrl: i } = await s(r, n, e);
|
|
86
95
|
p((e) => ({
|
|
87
96
|
...e,
|
|
88
97
|
progress: 10
|
|
89
|
-
})), await
|
|
98
|
+
})), await c(n, i, (e) => p((t) => ({
|
|
90
99
|
...t,
|
|
91
100
|
progress: e
|
|
92
101
|
}))), p((e) => ({
|
|
93
102
|
...e,
|
|
94
103
|
progress: 90
|
|
95
104
|
}));
|
|
96
|
-
let
|
|
105
|
+
let a = await l(r, t, e, n.size);
|
|
97
106
|
return p({
|
|
98
107
|
uploading: !1,
|
|
99
108
|
progress: 100,
|
|
100
109
|
error: null
|
|
101
|
-
}),
|
|
110
|
+
}), a;
|
|
102
111
|
} catch (e) {
|
|
103
112
|
throw p({
|
|
104
113
|
uploading: !1,
|
|
@@ -107,15 +116,15 @@ function s(s, c, l, u, d = "/DevPortal/Packages") {
|
|
|
107
116
|
}), e;
|
|
108
117
|
}
|
|
109
118
|
}, [
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
119
|
+
e,
|
|
120
|
+
t,
|
|
121
|
+
i,
|
|
113
122
|
u
|
|
114
123
|
]),
|
|
115
124
|
...f
|
|
116
125
|
};
|
|
117
126
|
}
|
|
118
127
|
//#endregion
|
|
119
|
-
export {
|
|
128
|
+
export { u as usePackageUpload };
|
|
120
129
|
|
|
121
130
|
//# sourceMappingURL=usePackageUpload.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePackageUpload.js","names":[],"sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"sourcesContent":["/**\n * usePackageUpload — uploads a file to wspace-files-svc and returns a URL to\n * persist (ApplicationVersion.packageUrl, a physical item's image, an app\n * icon — this hook is generic despite its name; folderPath picks the\n * destination). Every upload here is visibility: PUBLIC (see\n * initiateUpload below), so the returned URL prefers the permanent CDN\n * publicUrl over the presigned downloadUrl (~1 hour TTL, wrong for anything\n * persisted and displayed later — see wspace-files-svc's FieldResolvers.ts\n * doc comment on `downloadUrl`). Falls back to downloadUrl when publicUrl\n * is null (the public-assets CDN infra isn't provisioned in every\n * environment yet), so this keeps working exactly as before until it is.\n *\n * Flow (mirrors uploadBlobToFiles in @burdenoff/fe-libs/storage):\n * 1. ensureFolderByPath(folderPath) → folderId\n * 2. initiateUpload → { sessionId, presignedUrl }\n * 3. PUT blob to presignedUrl (progress tracked via XHR)\n * 4. completeUpload → { file.publicUrl ?? file.downloadUrl }\n *\n * Uses raw fetch for GraphQL mutations so we can target the workspace gateway\n * without a second ApolloProvider.\n */\nimport { useCallback, useState } from \"react\";\n\nexport interface PackageUploadResult {\n fileId: string;\n /** The permanent publicUrl when available, else the presigned downloadUrl\n * (field name kept as `downloadUrl` so existing callers don't need to\n * change — see the module doc comment above). */\n downloadUrl: string;\n}\n\nexport interface PackageUploadState {\n uploading: boolean;\n progress: number;\n error: string | null;\n}\n\ntype GqlFetcher = (\n query: string,\n variables: Record<string, unknown>,\n) => Promise<Record<string, unknown>>;\n\nfunction makeFetcher(\n workspacesGatewayUrl: string,\n authToken: string | undefined,\n workspaceId: string | undefined,\n workspaceToken: string | undefined,\n): GqlFetcher {\n return async (query, variables) => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authToken) headers[\"Authorization\"] = `Bearer ${authToken}`;\n if (workspaceToken)\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n if (workspaceId) headers[\"x-workspace-id\"] = workspaceId;\n\n const endpoint = workspacesGatewayUrl.endsWith(\"/graphql\")\n ? workspacesGatewayUrl\n : `${workspacesGatewayUrl}/workspaces/graphql`;\n\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n throw new Error(\n `GraphQL request failed: ${res.status} ${res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n data?: Record<string, unknown>;\n errors?: Array<{ message: string }>;\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data ?? {};\n };\n}\n\nasync function ensureFolder(gql: GqlFetcher, path: string): Promise<string> {\n const data = await gql(\n `mutation DevPortal_EnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }`,\n { path },\n );\n const id = (data.ensureFolderByPath as { id?: string } | undefined)?.id;\n if (!id) throw new Error(\"ensureFolderByPath returned no id\");\n return id;\n}\n\nasync function initiateUpload(\n gql: GqlFetcher,\n file: File,\n folderId: string,\n): Promise<{ sessionId: string; uploadUrl: string }> {\n const ext = file.name.includes(\".\")\n ? file.name.substring(file.name.lastIndexOf(\".\") + 1)\n : \"zip\";\n const data = await gql(\n `mutation DevPortal_InitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }`,\n {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n mimeType: file.type || \"application/zip\",\n extension: ext,\n folderId,\n visibility: \"PUBLIC\",\n },\n },\n );\n const result = data.initiateUpload as\n | { uploadUrl?: string; session?: { id?: string } }\n | undefined;\n const sessionId = result?.session?.id;\n const uploadUrl = result?.uploadUrl;\n if (!sessionId || !uploadUrl) {\n throw new Error(\"initiateUpload did not return sessionId or uploadUrl\");\n }\n return { sessionId, uploadUrl };\n}\n\nasync function putToPresignedUrl(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.upload.addEventListener(\"progress\", (evt) => {\n if (evt.lengthComputable) {\n onProgress(10 + Math.round((evt.loaded / evt.total) * 80));\n }\n });\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`S3 PUT failed: ${xhr.status}`));\n }\n });\n xhr.addEventListener(\"error\", () =>\n reject(new Error(\"S3 PUT network error\")),\n );\n xhr.addEventListener(\"abort\", () => reject(new Error(\"S3 PUT aborted\")));\n xhr.open(\"PUT\", uploadUrl);\n xhr.setRequestHeader(\"Content-Type\", file.type || \"application/zip\");\n xhr.send(file);\n });\n}\n\nasync function completeUpload(\n gql: GqlFetcher,\n sessionId: string,\n folderId: string,\n fileSize: number,\n): Promise<PackageUploadResult> {\n const data = await gql(\n `mutation DevPortal_CompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n file { id downloadUrl publicUrl }\n }\n }`,\n {\n input: {\n sessionId,\n folderId,\n fileSize: String(fileSize),\n },\n },\n );\n const file = (\n data.completeUpload as\n | {\n file?: {\n id?: string;\n downloadUrl?: string;\n publicUrl?: string | null;\n };\n }\n | undefined\n )?.file;\n const url = file?.publicUrl || file?.downloadUrl;\n if (!file?.id || !url) {\n throw new Error(\"completeUpload did not return file id or a usable URL\");\n }\n return { fileId: file.id, downloadUrl: url };\n}\n\nexport function usePackageUpload(\n workspacesGatewayUrl: string | undefined,\n authToken: string | undefined,\n workspaceId: string | undefined,\n getWorkspaceToken: (() => Promise<string | null>) | undefined,\n folderPath = \"/DevPortal/Packages\",\n) {\n const [state, setState] = useState<PackageUploadState>({\n uploading: false,\n progress: 0,\n error: null,\n });\n\n const upload = useCallback(\n async (file: File): Promise<PackageUploadResult> => {\n if (!workspacesGatewayUrl) {\n throw new Error(\n \"workspacesGatewayUrl is not configured. Pass it as a prop to DevportalRoot.\",\n );\n }\n\n const freshToken = getWorkspaceToken\n ? await getWorkspaceToken()\n : undefined;\n const gql = makeFetcher(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n freshToken ?? undefined,\n );\n\n setState({ uploading: true, progress: 0, error: null });\n try {\n const folderId = await ensureFolder(gql, folderPath);\n setState((s) => ({ ...s, progress: 5 }));\n\n const { sessionId, uploadUrl } = await initiateUpload(\n gql,\n file,\n folderId,\n );\n setState((s) => ({ ...s, progress: 10 }));\n\n await putToPresignedUrl(file, uploadUrl, (pct) =>\n setState((s) => ({ ...s, progress: pct })),\n );\n setState((s) => ({ ...s, progress: 90 }));\n\n const result = await completeUpload(\n gql,\n sessionId,\n folderId,\n file.size,\n );\n setState({ uploading: false, progress: 100, error: null });\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState({ uploading: false, progress: 0, error: message });\n throw err;\n }\n },\n [workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken],\n );\n\n return { upload, ...state };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"usePackageUpload.js","names":[],"sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"sourcesContent":["/**\n * usePackageUpload — uploads a file to wspace-files-svc and returns a URL to\n * persist (ApplicationVersion.packageUrl, a physical item's image, an app\n * icon — this hook is generic despite its name; folderPath picks the\n * destination). Every upload here is visibility: PUBLIC (see\n * initiateUpload below), so the returned URL prefers the permanent CDN\n * publicUrl over the presigned downloadUrl (~1 hour TTL, wrong for anything\n * persisted and displayed later — see wspace-files-svc's FieldResolvers.ts\n * doc comment on `downloadUrl`). Falls back to downloadUrl when publicUrl\n * is null (the public-assets CDN infra isn't provisioned in every\n * environment yet), so this keeps working exactly as before until it is.\n *\n * Flow (mirrors uploadBlobToFiles in @burdenoff/fe-libs/storage):\n * 1. ensureFolderByPath(folderPath) → folderId\n * 2. initiateUpload → { sessionId, presignedUrl }\n * 3. PUT blob to presignedUrl (progress tracked via XHR)\n * 4. completeUpload → { file.publicUrl ?? file.downloadUrl }\n *\n * Uses raw fetch for GraphQL mutations so we can target the workspace gateway\n * without a second ApolloProvider.\n */\nimport { useCallback, useState } from \"react\";\nimport { graphQLErrorFrom, httpErrorFrom } from \"../utils/uploadErrors\";\n\nexport interface PackageUploadResult {\n fileId: string;\n /** The permanent publicUrl when available, else the presigned downloadUrl\n * (field name kept as `downloadUrl` so existing callers don't need to\n * change — see the module doc comment above). */\n downloadUrl: string;\n}\n\nexport interface PackageUploadState {\n uploading: boolean;\n progress: number;\n error: string | null;\n}\n\ntype GqlFetcher = (\n query: string,\n variables: Record<string, unknown>,\n) => Promise<Record<string, unknown>>;\n\n/** The parsed JSON body, or undefined when it is missing or not JSON. */\nasync function readJsonBody(res: Response): Promise<unknown> {\n try {\n return await res.json();\n } catch {\n return undefined;\n }\n}\n\nexport function makeFetcher(\n workspacesGatewayUrl: string,\n authToken: string | undefined,\n workspaceId: string | undefined,\n workspaceToken: string | undefined,\n): GqlFetcher {\n return async (query, variables) => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authToken) headers[\"Authorization\"] = `Bearer ${authToken}`;\n if (workspaceToken)\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n if (workspaceId) headers[\"x-workspace-id\"] = workspaceId;\n\n const endpoint = workspacesGatewayUrl.endsWith(\"/graphql\")\n ? workspacesGatewayUrl\n : `${workspacesGatewayUrl}/workspaces/graphql`;\n\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n // Read the body even on a non-2xx status: the gateway explains a quota\n // refusal (HTTP 429) in the GraphQL errors, and the status alone reads as\n // rate limiting (BOFF-10676).\n const body = await readJsonBody(res);\n if (!res.ok) {\n throw httpErrorFrom(res.status, res.statusText, body);\n }\n const inBand = graphQLErrorFrom(body);\n if (inBand) throw inBand;\n // Cast: `body` is untrusted JSON; only `data` is read, and its shape is checked below.\n const data = (body as { data?: unknown } | undefined)?.data;\n return typeof data === \"object\" && data !== null\n ? // A GraphQL `data` member is always an object keyed by root field.\n (data as Record<string, unknown>)\n : {};\n };\n}\n\nasync function ensureFolder(gql: GqlFetcher, path: string): Promise<string> {\n const data = await gql(\n `mutation DevPortal_EnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }`,\n { path },\n );\n const id = (data.ensureFolderByPath as { id?: string } | undefined)?.id;\n if (!id) throw new Error(\"ensureFolderByPath returned no id\");\n return id;\n}\n\nasync function initiateUpload(\n gql: GqlFetcher,\n file: File,\n folderId: string,\n): Promise<{ sessionId: string; uploadUrl: string }> {\n const ext = file.name.includes(\".\")\n ? file.name.substring(file.name.lastIndexOf(\".\") + 1)\n : \"zip\";\n const data = await gql(\n `mutation DevPortal_InitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }`,\n {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n mimeType: file.type || \"application/zip\",\n extension: ext,\n folderId,\n visibility: \"PUBLIC\",\n },\n },\n );\n const result = data.initiateUpload as\n | { uploadUrl?: string; session?: { id?: string } }\n | undefined;\n const sessionId = result?.session?.id;\n const uploadUrl = result?.uploadUrl;\n if (!sessionId || !uploadUrl) {\n throw new Error(\"initiateUpload did not return sessionId or uploadUrl\");\n }\n return { sessionId, uploadUrl };\n}\n\nasync function putToPresignedUrl(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.upload.addEventListener(\"progress\", (evt) => {\n if (evt.lengthComputable) {\n onProgress(10 + Math.round((evt.loaded / evt.total) * 80));\n }\n });\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`S3 PUT failed: ${xhr.status}`));\n }\n });\n xhr.addEventListener(\"error\", () =>\n reject(new Error(\"S3 PUT network error\")),\n );\n xhr.addEventListener(\"abort\", () => reject(new Error(\"S3 PUT aborted\")));\n xhr.open(\"PUT\", uploadUrl);\n xhr.setRequestHeader(\"Content-Type\", file.type || \"application/zip\");\n xhr.send(file);\n });\n}\n\nasync function completeUpload(\n gql: GqlFetcher,\n sessionId: string,\n folderId: string,\n fileSize: number,\n): Promise<PackageUploadResult> {\n const data = await gql(\n `mutation DevPortal_CompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n file { id downloadUrl publicUrl }\n }\n }`,\n {\n input: {\n sessionId,\n folderId,\n fileSize: String(fileSize),\n },\n },\n );\n const file = (\n data.completeUpload as\n | {\n file?: {\n id?: string;\n downloadUrl?: string;\n publicUrl?: string | null;\n };\n }\n | undefined\n )?.file;\n const url = file?.publicUrl || file?.downloadUrl;\n if (!file?.id || !url) {\n throw new Error(\"completeUpload did not return file id or a usable URL\");\n }\n return { fileId: file.id, downloadUrl: url };\n}\n\nexport function usePackageUpload(\n workspacesGatewayUrl: string | undefined,\n authToken: string | undefined,\n workspaceId: string | undefined,\n getWorkspaceToken: (() => Promise<string | null>) | undefined,\n folderPath = \"/DevPortal/Packages\",\n) {\n const [state, setState] = useState<PackageUploadState>({\n uploading: false,\n progress: 0,\n error: null,\n });\n\n const upload = useCallback(\n async (file: File): Promise<PackageUploadResult> => {\n if (!workspacesGatewayUrl) {\n throw new Error(\n \"workspacesGatewayUrl is not configured. Pass it as a prop to DevportalRoot.\",\n );\n }\n\n const freshToken = getWorkspaceToken\n ? await getWorkspaceToken()\n : undefined;\n const gql = makeFetcher(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n freshToken ?? undefined,\n );\n\n setState({ uploading: true, progress: 0, error: null });\n try {\n const folderId = await ensureFolder(gql, folderPath);\n setState((s) => ({ ...s, progress: 5 }));\n\n const { sessionId, uploadUrl } = await initiateUpload(\n gql,\n file,\n folderId,\n );\n setState((s) => ({ ...s, progress: 10 }));\n\n await putToPresignedUrl(file, uploadUrl, (pct) =>\n setState((s) => ({ ...s, progress: pct })),\n );\n setState((s) => ({ ...s, progress: 90 }));\n\n const result = await completeUpload(\n gql,\n sessionId,\n folderId,\n file.size,\n );\n setState({ uploading: false, progress: 100, error: null });\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState({ uploading: false, progress: 0, error: message });\n throw err;\n }\n },\n [workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken],\n );\n\n return { upload, ...state };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApplicationDetailPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/ApplicationDetailPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ApplicationDetailPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/ApplicationDetailPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAkUhC;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAigCnC,CAAC"}
|