@fluid-app/fluid-cli-portal 0.1.58 → 0.1.59

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.
@@ -6,6 +6,150 @@ import { failure, success } from "@fluid-app/fluid-cli";
6
6
  import { execa } from "execa";
7
7
  import { createHash } from "node:crypto";
8
8
  import fs from "fs-extra";
9
+ const REMOTE_DOM_WIDGET_RUNTIME = "remote-dom";
10
+ const PACKAGE_FIELDS = new Set([
11
+ "manifestVersion",
12
+ "runtime",
13
+ "packageId",
14
+ "source",
15
+ "version",
16
+ "workerEntryUrl",
17
+ "cssUrls",
18
+ "assetUrls",
19
+ "widgets"
20
+ ]);
21
+ const WIDGET_FIELDS = new Set([
22
+ "type",
23
+ "displayName",
24
+ "description",
25
+ "icon",
26
+ "category",
27
+ "propertySchema",
28
+ "defaultProps",
29
+ "container",
30
+ "resizable",
31
+ "minSdkVersion",
32
+ "capabilities"
33
+ ]);
34
+ const CAPABILITY_FIELDS = new Set(["name", "version"]);
35
+ const UNSAFE_JSON_KEYS = new Set([
36
+ "__proto__",
37
+ "constructor",
38
+ "prototype"
39
+ ]);
40
+ function isRemoteDomWidgetPackageDescriptor(value) {
41
+ return getRemoteDomWidgetPackageInvalidReason(value) === void 0;
42
+ }
43
+ function getRemoteDomWidgetPackageInvalidReason(value) {
44
+ if (!isRecord$6(value)) return "descriptor must be an object";
45
+ const unexpectedFieldReason = getUnexpectedFieldReason(value, PACKAGE_FIELDS, "descriptor");
46
+ if (unexpectedFieldReason) return unexpectedFieldReason;
47
+ if (value.manifestVersion !== 1) return `manifestVersion must be 1`;
48
+ if (value.runtime !== "remote-dom") return `runtime must be ${REMOTE_DOM_WIDGET_RUNTIME}`;
49
+ const packageIdReason = getNonEmptyStringReason(value.packageId, "packageId");
50
+ if (packageIdReason) return packageIdReason;
51
+ if (value.source !== "company" && value.source !== "droplet") return "source must be company or droplet";
52
+ const versionReason = getNonEmptyStringReason(value.version, "version");
53
+ if (versionReason) return versionReason;
54
+ const workerEntryUrlReason = getHttpUrlReason(value.workerEntryUrl, "workerEntryUrl");
55
+ if (workerEntryUrlReason) return workerEntryUrlReason;
56
+ const cssUrlsReason = getUrlArrayReason(value.cssUrls, "cssUrls");
57
+ if (cssUrlsReason) return cssUrlsReason;
58
+ const assetUrlsReason = getUrlArrayReason(value.assetUrls, "assetUrls");
59
+ if (assetUrlsReason) return assetUrlsReason;
60
+ if (!Array.isArray(value.widgets) || value.widgets.length === 0) return "widgets must be a non-empty array";
61
+ const widgetTypes = /* @__PURE__ */ new Set();
62
+ for (let index = 0; index < value.widgets.length; index += 1) {
63
+ const widget = value.widgets[index];
64
+ const widgetReason = getWidgetReason(widget, `widgets[${index}]`);
65
+ if (widgetReason) return widgetReason;
66
+ const widgetType = widget.type;
67
+ if (widgetTypes.has(widgetType)) return `widgets contains duplicate type ${widgetType}`;
68
+ widgetTypes.add(widgetType);
69
+ }
70
+ }
71
+ function getWidgetReason(value, path) {
72
+ if (!isRecord$6(value)) return `${path} must be an object`;
73
+ const unexpectedFieldReason = getUnexpectedFieldReason(value, WIDGET_FIELDS, path);
74
+ if (unexpectedFieldReason) return unexpectedFieldReason;
75
+ for (const field of [
76
+ "type",
77
+ "displayName",
78
+ "description",
79
+ "icon",
80
+ "category",
81
+ "minSdkVersion"
82
+ ]) {
83
+ const reason = getNonEmptyStringReason(value[field], `${path}.${field}`);
84
+ if (reason) return reason;
85
+ }
86
+ if (!isRecord$6(value.propertySchema) || !isJsonValue(value.propertySchema)) return `${path}.propertySchema must be a JSON object`;
87
+ if (!isRecord$6(value.defaultProps) || !isJsonValue(value.defaultProps)) return `${path}.defaultProps must be a JSON object`;
88
+ if (value.container !== "inline" && value.container !== "block" && value.container !== "card" && value.container !== "fullscreen") return `${path}.container is unsupported`;
89
+ if (typeof value.resizable !== "boolean" && value.resizable !== "horizontal" && value.resizable !== "vertical" && value.resizable !== "both") return `${path}.resizable is unsupported`;
90
+ if (!Array.isArray(value.capabilities)) return `${path}.capabilities must be an array`;
91
+ const capabilityNames = /* @__PURE__ */ new Set();
92
+ for (let index = 0; index < value.capabilities.length; index += 1) {
93
+ const capability = value.capabilities[index];
94
+ const capabilityPath = `${path}.capabilities[${index}]`;
95
+ if (!isRecord$6(capability)) return `${capabilityPath} must be an object`;
96
+ const unexpectedFieldReason = getUnexpectedFieldReason(capability, CAPABILITY_FIELDS, capabilityPath);
97
+ if (unexpectedFieldReason) return unexpectedFieldReason;
98
+ const capabilityName = capability.name;
99
+ const nameReason = getNonEmptyStringReason(capabilityName, `${capabilityPath}.name`);
100
+ if (nameReason) return nameReason;
101
+ if (typeof capabilityName !== "string") return `${capabilityPath}.name must be a non-empty string`;
102
+ const versionReason = getNonEmptyStringReason(capability.version, `${capabilityPath}.version`);
103
+ if (versionReason) return versionReason;
104
+ if (capabilityNames.has(capabilityName)) return `${path}.capabilities contains duplicate name ${capabilityName}`;
105
+ capabilityNames.add(capabilityName);
106
+ }
107
+ }
108
+ function getUrlArrayReason(value, path) {
109
+ if (!Array.isArray(value)) return `${path} must be an array`;
110
+ for (let index = 0; index < value.length; index += 1) {
111
+ const reason = getHttpUrlReason(value[index], `${path}[${index}]`);
112
+ if (reason) return reason;
113
+ }
114
+ }
115
+ function getHttpUrlReason(value, path) {
116
+ const stringReason = getNonEmptyStringReason(value, path);
117
+ if (stringReason) return stringReason;
118
+ if (typeof value !== "string") return `${path} must be a non-empty string`;
119
+ try {
120
+ const url = new URL(value);
121
+ if (url.protocol !== "http:" && url.protocol !== "https:") return `${path} must use http or https`;
122
+ if (url.username || url.password) return `${path} must not contain credentials`;
123
+ } catch {
124
+ return `${path} must be an absolute URL`;
125
+ }
126
+ }
127
+ function getNonEmptyStringReason(value, path) {
128
+ if (typeof value !== "string" || value.trim().length === 0) return `${path} must be a non-empty string`;
129
+ if (value !== value.trim()) return `${path} must not have surrounding whitespace`;
130
+ if (/\p{Cc}/u.test(value)) return `${path} must not contain control characters`;
131
+ }
132
+ function isJsonValue(value) {
133
+ return isJsonValueAtDepth(value, /* @__PURE__ */ new WeakSet(), 0);
134
+ }
135
+ function isJsonValueAtDepth(value, ancestors, depth) {
136
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
137
+ if (typeof value === "number") return Number.isFinite(value);
138
+ if (typeof value !== "object" || depth > 50 || ancestors.has(value)) return false;
139
+ ancestors.add(value);
140
+ const prototype = Object.getPrototypeOf(value);
141
+ const isJson = Array.isArray(value) ? value.every((item) => isJsonValueAtDepth(item, ancestors, depth + 1)) : (prototype === Object.prototype || prototype === null) && isRecord$6(value) && Object.entries(value).every(([key, item]) => !UNSAFE_JSON_KEYS.has(key) && isJsonValueAtDepth(item, ancestors, depth + 1));
142
+ ancestors.delete(value);
143
+ return isJson;
144
+ }
145
+ function isRecord$6(value) {
146
+ return typeof value === "object" && value !== null && !Array.isArray(value);
147
+ }
148
+ function getUnexpectedFieldReason(value, allowedFields, path) {
149
+ const unexpectedField = Object.keys(value).find((field) => !allowedFields.has(field));
150
+ return unexpectedField ? `${path} contains unsupported field ${unexpectedField}` : void 0;
151
+ }
152
+ //#endregion
9
153
  //#region ../widget-tooling/src/errors.ts
10
154
  var WidgetToolingError = class extends Error {
11
155
  code;
@@ -121,29 +265,6 @@ function toPosixPath$1(value) {
121
265
  return value.split(path.sep).join("/");
122
266
  }
123
267
  //#endregion
124
- //#region ../../portal/core/src/remote-dom-widget-package.ts
125
- const UNSAFE_JSON_KEYS = new Set([
126
- "__proto__",
127
- "constructor",
128
- "prototype"
129
- ]);
130
- function isJsonValue(value) {
131
- return isJsonValueAtDepth(value, /* @__PURE__ */ new WeakSet(), 0);
132
- }
133
- function isJsonValueAtDepth(value, ancestors, depth) {
134
- if (value === null || typeof value === "string" || typeof value === "boolean") return true;
135
- if (typeof value === "number") return Number.isFinite(value);
136
- if (typeof value !== "object" || depth > 50 || ancestors.has(value)) return false;
137
- ancestors.add(value);
138
- const prototype = Object.getPrototypeOf(value);
139
- const isJson = Array.isArray(value) ? value.every((item) => isJsonValueAtDepth(item, ancestors, depth + 1)) : (prototype === Object.prototype || prototype === null) && isRecord$6(value) && Object.entries(value).every(([key, item]) => !UNSAFE_JSON_KEYS.has(key) && isJsonValueAtDepth(item, ancestors, depth + 1));
140
- ancestors.delete(value);
141
- return isJson;
142
- }
143
- function isRecord$6(value) {
144
- return typeof value === "object" && value !== null && !Array.isArray(value);
145
- }
146
- //#endregion
147
268
  //#region ../widget-tooling/src/internal/widget-package-validation.ts
148
269
  const URL_SAFE_SEGMENT_SOURCE = "[A-Za-z0-9][A-Za-z0-9_~-]*";
149
270
  const URL_SAFE_WIDGET_NAME_PATTERN = new RegExp(`^${URL_SAFE_SEGMENT_SOURCE}$`);
@@ -701,6 +822,7 @@ const WIDGET_CONFIG_MODULE_PATH = "/fluid.widget.config.ts";
701
822
  */
702
823
  function buildRuntimeEntryBootstrapScript(entryPath, origin) {
703
824
  const refreshRuntimeUrl = new URL("/@react-refresh", origin).toString();
825
+ const workerSdkUrl = new URL("/@id/@fluid-app/portal-sdk/widgets/worker", origin).toString();
704
826
  const entryUrl = new URL(entryPath, origin).toString();
705
827
  return [
706
828
  "// Dev-only worker bootstrap — see buildRuntimeEntryBootstrapScript",
@@ -711,6 +833,8 @@ function buildRuntimeEntryBootstrapScript(entryPath, origin) {
711
833
  "const __buf = (e) => { __q.push(e.data); };",
712
834
  "self.addEventListener(\"message\", __buf);",
713
835
  "if (typeof window === \"undefined\") { globalThis.window = globalThis; }",
836
+ `const { prepareRemoteDomWidgetWorker } = await import(${JSON.stringify(workerSdkUrl)});`,
837
+ "prepareRemoteDomWidgetWorker();",
714
838
  "try {",
715
839
  ` const { default: RefreshRuntime } = await import(${JSON.stringify(refreshRuntimeUrl)});`,
716
840
  " RefreshRuntime.injectIntoGlobalHook(window);",
@@ -1174,9 +1298,10 @@ async function buildSharedWidgetPackage(options) {
1174
1298
  }
1175
1299
  function createWidgetPackageEntrySource(options) {
1176
1300
  return `import { createElement } from "react";
1177
- import { startWidgetPackage } from "@fluid-app/portal-sdk/widgets/worker";
1301
+ import { prepareRemoteDomWidgetWorker, startWidgetPackage } from "@fluid-app/portal-sdk/widgets/worker";
1178
1302
  ${options.cssImportPath ? `import ${JSON.stringify(options.cssImportPath)};` : ""}
1179
1303
 
1304
+ prepareRemoteDomWidgetWorker();
1180
1305
  const widgetConfig = await import(${JSON.stringify(options.configImportPath)});
1181
1306
  const SOURCE_PACKAGE_MARKER = "__fluidSourceWidgetPackage";
1182
1307
  const descriptors = ${JSON.stringify(options.validated.widgets, null, 2)};
@@ -2152,6 +2277,6 @@ function isRecord(value) {
2152
2277
  return typeof value === "object" && value !== null && !Array.isArray(value);
2153
2278
  }
2154
2279
  //#endregion
2155
- export { resolvePublishVersion as a, fluidWidgetDevPlugin as c, readCompanyWidgetPackages as i, handleOptions as l, preflightCompanyPackage as n, loadAndValidateWidgetPackage as o, readCompanyWidgetPackage as r, publishWidgetPackage as s, formatPackageKeyMismatch as t, setCorsHeaders as u };
2280
+ export { resolvePublishVersion as a, fluidWidgetDevPlugin as c, isRemoteDomWidgetPackageDescriptor as d, readCompanyWidgetPackages as i, handleOptions as l, preflightCompanyPackage as n, loadAndValidateWidgetPackage as o, readCompanyWidgetPackage as r, publishWidgetPackage as s, formatPackageKeyMismatch as t, setCorsHeaders as u };
2156
2281
 
2157
- //# sourceMappingURL=src-CpaIqsaL.mjs.map
2282
+ //# sourceMappingURL=src-2znVq55R.mjs.map