@groundfloorcloud/cli 0.1.3 → 0.1.4
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 +6 -5
- package/dist/index.js +91 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -152,11 +152,12 @@ gf apps publish --path release.zip
|
|
|
152
152
|
(`/downloads/shell-starter-kit.zip` on the current cell) and stamps
|
|
153
153
|
`APP_ID` / `groundfloor.manifest.json` `appId` to the slug.
|
|
154
154
|
|
|
155
|
-
`gf apps publish` uploads `release.zip`
|
|
156
|
-
Vite `dist/assets/` layout
|
|
157
|
-
|
|
158
|
-
`
|
|
159
|
-
`
|
|
155
|
+
`gf apps publish` uploads **only** `release.zip` from `npm run release`.
|
|
156
|
+
It flattens a Vite `dist/assets/` layout when needed, then **rejects** a zip
|
|
157
|
+
that is only `remoteEntry.js` (missing `__federation_*.js` / CSS) or whose
|
|
158
|
+
federation `name` does not match `federationRemoteName(slug)`. A lone
|
|
159
|
+
`.js` file is also rejected. Pass an app id/slug, or set `appId` in
|
|
160
|
+
`groundfloor.manifest.json` next to the zip.
|
|
160
161
|
|
|
161
162
|
Wrap an existing **service** coderunner as a product:
|
|
162
163
|
|
package/dist/index.js
CHANGED
|
@@ -1810,8 +1810,80 @@ function flattenFederatedReleaseZip(bytes) {
|
|
|
1810
1810
|
}
|
|
1811
1811
|
return zip.toBuffer();
|
|
1812
1812
|
}
|
|
1813
|
-
function federatedReleaseZipHint() {
|
|
1814
|
-
return
|
|
1813
|
+
function federatedReleaseZipHint(slug) {
|
|
1814
|
+
return `Run \`npm run release\` in a kit from \`gf apps init\`, then publish that zip (do not zip Vite dist/ or upload only remoteEntry.js). ${initThenPublishHint(slug)}`;
|
|
1815
|
+
}
|
|
1816
|
+
function federationRemoteName(appId) {
|
|
1817
|
+
const sanitized = appId.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
1818
|
+
return sanitized.length > 0 ? sanitized : "federatedApp";
|
|
1819
|
+
}
|
|
1820
|
+
function initThenPublishHint(slug) {
|
|
1821
|
+
const s = (slug ?? "").trim() || "<portal-slug>";
|
|
1822
|
+
return `gf apps init --slug ${s} && cd ${s} && npm install && npm run release && gf apps publish --path release.zip`;
|
|
1823
|
+
}
|
|
1824
|
+
function extractReferencedSiblings(remoteEntrySource) {
|
|
1825
|
+
const names = /* @__PURE__ */ new Set();
|
|
1826
|
+
for (const match of remoteEntrySource.matchAll(
|
|
1827
|
+
/["']\.\/([^"'/]+\.(?:js|mjs|cjs|css))["']/g
|
|
1828
|
+
)) {
|
|
1829
|
+
if (match[1] !== REMOTE_ENTRY) names.add(match[1]);
|
|
1830
|
+
}
|
|
1831
|
+
for (const match of remoteEntrySource.matchAll(
|
|
1832
|
+
/["']([A-Za-z0-9._-]+\.css)["']/g
|
|
1833
|
+
)) {
|
|
1834
|
+
names.add(match[1]);
|
|
1835
|
+
}
|
|
1836
|
+
return [...names];
|
|
1837
|
+
}
|
|
1838
|
+
function extractFederationScope(remoteEntrySource) {
|
|
1839
|
+
return remoteEntrySource.match(/css__([A-Za-z0-9_-]+)__/)?.[1];
|
|
1840
|
+
}
|
|
1841
|
+
function zipRootNames(bytes) {
|
|
1842
|
+
const zip = new AdmZip2(bytes);
|
|
1843
|
+
return zip.getEntries().filter((entry) => !entry.isDirectory).map((entry) => normalizeZipEntryName(entry.entryName));
|
|
1844
|
+
}
|
|
1845
|
+
function readZipFile(bytes, rel) {
|
|
1846
|
+
const zip = new AdmZip2(bytes);
|
|
1847
|
+
const entry = zip.getEntries().find((item) => {
|
|
1848
|
+
if (item.isDirectory) return false;
|
|
1849
|
+
return normalizeZipEntryName(item.entryName) === rel;
|
|
1850
|
+
});
|
|
1851
|
+
return entry ? entry.getData().toString("utf8") : void 0;
|
|
1852
|
+
}
|
|
1853
|
+
function assertFederatedReleaseZip(bytes, slug) {
|
|
1854
|
+
const hint = initThenPublishHint(slug);
|
|
1855
|
+
const names = zipRootNames(bytes);
|
|
1856
|
+
const present = new Set(names.map((name) => basename(name)));
|
|
1857
|
+
const entryRel = chooseRemoteEntry(names);
|
|
1858
|
+
if (!entryRel) {
|
|
1859
|
+
throw new Error(`Zip has no remoteEntry.js. ${hint}`);
|
|
1860
|
+
}
|
|
1861
|
+
const source = readZipFile(bytes, entryRel);
|
|
1862
|
+
if (!source) {
|
|
1863
|
+
throw new Error(`Zip has no remoteEntry.js. ${hint}`);
|
|
1864
|
+
}
|
|
1865
|
+
const siblings = extractReferencedSiblings(source);
|
|
1866
|
+
const missing = siblings.filter((name) => !present.has(name));
|
|
1867
|
+
const jsOrCssAtRoot = names.filter((name) => {
|
|
1868
|
+
if (name.includes("/")) return false;
|
|
1869
|
+
const lower = name.toLowerCase();
|
|
1870
|
+
return lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".css");
|
|
1871
|
+
});
|
|
1872
|
+
if (missing.length > 0 || siblings.length === 0 && jsOrCssAtRoot.length < 2) {
|
|
1873
|
+
const detail = missing.length > 0 ? `Missing next to remoteEntry.js: ${missing.join(", ")}.` : "Zip is only remoteEntry.js (Vite chunks were never packed).";
|
|
1874
|
+
throw new Error(
|
|
1875
|
+
`${detail} Do not zip dist/ or upload a lone remoteEntry.js. ${hint}`
|
|
1876
|
+
);
|
|
1877
|
+
}
|
|
1878
|
+
const scope = extractFederationScope(source);
|
|
1879
|
+
if (slug && scope) {
|
|
1880
|
+
const expected = federationRemoteName(slug);
|
|
1881
|
+
if (scope !== expected) {
|
|
1882
|
+
throw new Error(
|
|
1883
|
+
`Federation name is "${scope}" but Shell looks up "${expected}" (from slug ${slug}). Use the starter-kit vite.config (federationRemoteName). ${hint}`
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1815
1887
|
}
|
|
1816
1888
|
|
|
1817
1889
|
// src/commands/apps.ts
|
|
@@ -1854,10 +1926,10 @@ function zipHasRemoteEntry(bytes) {
|
|
|
1854
1926
|
return name === REMOTE_ENTRY || name.endsWith(`/${REMOTE_ENTRY}`);
|
|
1855
1927
|
});
|
|
1856
1928
|
}
|
|
1857
|
-
function prepareReleaseZip(bytes) {
|
|
1929
|
+
function prepareReleaseZip(bytes, slug) {
|
|
1858
1930
|
if (!zipHasRemoteEntry(bytes)) {
|
|
1859
1931
|
throw new Error(
|
|
1860
|
-
`Zip has no remoteEntry.js. ${
|
|
1932
|
+
`Zip has no remoteEntry.js. ${initThenPublishHint(slug)}`
|
|
1861
1933
|
);
|
|
1862
1934
|
}
|
|
1863
1935
|
const flattened = flattenFederatedReleaseZip(bytes);
|
|
@@ -1867,10 +1939,11 @@ function prepareReleaseZip(bytes) {
|
|
|
1867
1939
|
if (Object.keys(plan).length > 0) {
|
|
1868
1940
|
process.stderr.write(
|
|
1869
1941
|
`Note: flattened Vite dist/assets layout so chunks sit next to remoteEntry.js.
|
|
1870
|
-
Prefer: ${federatedReleaseZipHint()}
|
|
1942
|
+
Prefer: ${federatedReleaseZipHint(slug)}
|
|
1871
1943
|
`
|
|
1872
1944
|
);
|
|
1873
1945
|
}
|
|
1946
|
+
assertFederatedReleaseZip(flattened, slug);
|
|
1874
1947
|
return flattened;
|
|
1875
1948
|
}
|
|
1876
1949
|
async function isDirectory(filePath) {
|
|
@@ -1969,7 +2042,8 @@ async function appsCreateCommand(opts) {
|
|
|
1969
2042
|
} catch (err) {
|
|
1970
2043
|
if (err instanceof CpError && err.status === 409) {
|
|
1971
2044
|
throw new Error(
|
|
1972
|
-
`App slug ${slug} already exists.
|
|
2045
|
+
`App slug ${slug} already exists. Init the kit if you do not have it, then publish:
|
|
2046
|
+
${initThenPublishHint(slug)}`
|
|
1973
2047
|
);
|
|
1974
2048
|
}
|
|
1975
2049
|
throw err;
|
|
@@ -2001,7 +2075,7 @@ async function appsPublishCommand(appRef, opts) {
|
|
|
2001
2075
|
bytes = await fs5.readFile(bundlePath);
|
|
2002
2076
|
} catch {
|
|
2003
2077
|
throw new Error(
|
|
2004
|
-
`No bundle at ${bundlePath}.
|
|
2078
|
+
`No bundle at ${bundlePath}. ${initThenPublishHint()}`
|
|
2005
2079
|
);
|
|
2006
2080
|
}
|
|
2007
2081
|
if (bytes.byteLength === 0) {
|
|
@@ -2027,11 +2101,14 @@ async function appsPublishCommand(appRef, opts) {
|
|
|
2027
2101
|
}
|
|
2028
2102
|
const ext = path5.extname(bundlePath).toLowerCase();
|
|
2029
2103
|
const isZip = ext === ".zip";
|
|
2030
|
-
if (isZip) {
|
|
2031
|
-
|
|
2104
|
+
if (!isZip) {
|
|
2105
|
+
throw new Error(
|
|
2106
|
+
`Publish release.zip from \`npm run release\`, not a lone ${path5.basename(bundlePath)}. ` + initThenPublishHint(app.slug)
|
|
2107
|
+
);
|
|
2032
2108
|
}
|
|
2033
|
-
|
|
2034
|
-
const
|
|
2109
|
+
bytes = prepareReleaseZip(bytes, app.slug);
|
|
2110
|
+
const bundle = "zip";
|
|
2111
|
+
const contentType = "application/zip";
|
|
2035
2112
|
if (opts.syncManifest !== false && sibling) {
|
|
2036
2113
|
await patchApp(cfg.apiUrl, cfg.workspaceId, app.id, {
|
|
2037
2114
|
manifest: sibling.manifest
|
|
@@ -2301,7 +2378,7 @@ function run(fn) {
|
|
|
2301
2378
|
var program = new Command();
|
|
2302
2379
|
program.name("gf").description(
|
|
2303
2380
|
"Groundfloor CLI \u2014 sign in, deploy coderunners, publish Shell apps, and manage workspace resources."
|
|
2304
|
-
).version("0.1.
|
|
2381
|
+
).version("0.1.4");
|
|
2305
2382
|
program.command("login").description(
|
|
2306
2383
|
"Log in via the browser (production by default; --dev / --stage for other cells)"
|
|
2307
2384
|
).option("--dev", "Sign in to Groundfloor dev").option("--stage", "Sign in to Groundfloor stage").option(
|
|
@@ -2398,10 +2475,10 @@ apps.command("create").description("Register a Shell federated or standalone pro
|
|
|
2398
2475
|
"Path to groundfloor.manifest.json (Shell apps)"
|
|
2399
2476
|
).option("--json", "Output raw JSON").action((opts) => run(() => appsCreateCommand(opts)));
|
|
2400
2477
|
apps.command("publish [appId]").description(
|
|
2401
|
-
"Upload release.zip
|
|
2478
|
+
"Upload release.zip from npm run release (not a lone remoteEntry.js)"
|
|
2402
2479
|
).option("-w, --workspace <id>", "Workspace id override").option("--app <id|slug>", "App id or slug (else manifest appId)").option(
|
|
2403
2480
|
"-p, --path <file>",
|
|
2404
|
-
"release.zip
|
|
2481
|
+
"release.zip from npm run release (or a folder containing it)",
|
|
2405
2482
|
"release.zip"
|
|
2406
2483
|
).option("-m, --label <text>", "Optional release label").option(
|
|
2407
2484
|
"--no-sync-manifest",
|
package/package.json
CHANGED