@fluid-app/fluid-cli-widget 0.1.47 → 0.1.49
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/index.mjs +87 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { execa } from "execa";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { findProjectConfig, readConfig } from "@fluid-app/fluid-cli";
|
|
7
|
+
import { findProjectConfig, gitSyncActorFromMe, gitSyncCommitSubject, parseNameStatusZ, readConfig, resolveGitSyncActor, subjectFragment, summarizeChanges } from "@fluid-app/fluid-cli";
|
|
8
8
|
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { cp, mkdir, mkdtemp, readFile, readdir, rm, rmdir, stat, writeFile } from "node:fs/promises";
|
|
10
10
|
import chalk from "chalk";
|
|
@@ -574,9 +574,9 @@ function isRecord$3(value) {
|
|
|
574
574
|
}
|
|
575
575
|
//#endregion
|
|
576
576
|
//#region src/utils/template.ts
|
|
577
|
-
const EMBEDDED_PORTAL_SDK_VERSION = "0.1.
|
|
578
|
-
const EMBEDDED_CLI_CORE_VERSION = "0.1.
|
|
579
|
-
const EMBEDDED_CLI_WIDGET_VERSION = "0.1.
|
|
577
|
+
const EMBEDDED_PORTAL_SDK_VERSION = "0.1.506";
|
|
578
|
+
const EMBEDDED_CLI_CORE_VERSION = "0.1.23";
|
|
579
|
+
const EMBEDDED_CLI_WIDGET_VERSION = "0.1.49";
|
|
580
580
|
function getDefaultTemplatePath() {
|
|
581
581
|
return path.join(findPackageRoot(), "templates", "default");
|
|
582
582
|
}
|
|
@@ -2138,6 +2138,31 @@ const DEFAULT_COMMIT_IDENTITY = {
|
|
|
2138
2138
|
*
|
|
2139
2139
|
* Steps 2 and 3 are best-effort and never fail the push.
|
|
2140
2140
|
*/
|
|
2141
|
+
/**
|
|
2142
|
+
* The staged file set, as {@link ChangeEntry}s — the input a
|
|
2143
|
+
* {@link CommitMessage} resolver summarizes.
|
|
2144
|
+
*
|
|
2145
|
+
* `-z` (NUL-delimited) rather than the default: paths with spaces or
|
|
2146
|
+
* quotes are legal in git and common in themes and asset folders, and
|
|
2147
|
+
* the newline form quotes and escapes them, which would corrupt both the
|
|
2148
|
+
* path and the count.
|
|
2149
|
+
*
|
|
2150
|
+
* Best-effort — a summary is a nicety, never a reason to fail a publish.
|
|
2151
|
+
* An empty result makes the resolver fall back to its own constant.
|
|
2152
|
+
*/
|
|
2153
|
+
async function stagedChanges(cwd) {
|
|
2154
|
+
try {
|
|
2155
|
+
const { stdout } = await execa("git", [
|
|
2156
|
+
"diff",
|
|
2157
|
+
"--cached",
|
|
2158
|
+
"--name-status",
|
|
2159
|
+
"-z"
|
|
2160
|
+
], { cwd });
|
|
2161
|
+
return parseNameStatusZ(stdout);
|
|
2162
|
+
} catch {
|
|
2163
|
+
return [];
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2141
2166
|
async function gitAutoCommit(cwd, message, identity = DEFAULT_COMMIT_IDENTITY) {
|
|
2142
2167
|
const repoRoot = await resolveRepoRoot(cwd);
|
|
2143
2168
|
excludeLocalStateFromGit(repoRoot);
|
|
@@ -2166,13 +2191,14 @@ async function gitAutoCommit(cwd, message, identity = DEFAULT_COMMIT_IDENTITY) {
|
|
|
2166
2191
|
".mist-desktop"
|
|
2167
2192
|
], { cwd: repoRoot });
|
|
2168
2193
|
} catch {}
|
|
2194
|
+
const subject = typeof message === "string" ? message : await message(await stagedChanges(cwd));
|
|
2169
2195
|
const identityArgs = await resolveIdentityArgs(cwd, identity);
|
|
2170
2196
|
try {
|
|
2171
2197
|
await execa("git", [
|
|
2172
2198
|
...identityArgs,
|
|
2173
2199
|
"commit",
|
|
2174
2200
|
"-m",
|
|
2175
|
-
|
|
2201
|
+
subject
|
|
2176
2202
|
], {
|
|
2177
2203
|
cwd,
|
|
2178
2204
|
stdio: [
|
|
@@ -2291,6 +2317,24 @@ function writeWidgetMarker(dir, marker) {
|
|
|
2291
2317
|
} : { package_key: marker.package_key };
|
|
2292
2318
|
writeFileSync(resolveWidgetMarkerPath(dir), JSON.stringify(payload, null, 2) + "\n", "utf8");
|
|
2293
2319
|
}
|
|
2320
|
+
function readWidgetMarker(dir) {
|
|
2321
|
+
const filePath = resolveWidgetMarkerPath(dir);
|
|
2322
|
+
if (!existsSync(filePath)) return null;
|
|
2323
|
+
try {
|
|
2324
|
+
const parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
2325
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
2326
|
+
const obj = parsed;
|
|
2327
|
+
const packageKey = obj["package_key"];
|
|
2328
|
+
if (typeof packageKey !== "string" || packageKey.length === 0) return null;
|
|
2329
|
+
const droplet = obj["droplet"];
|
|
2330
|
+
return {
|
|
2331
|
+
package_key: packageKey,
|
|
2332
|
+
...typeof droplet === "string" && droplet.length > 0 ? { droplet } : {}
|
|
2333
|
+
};
|
|
2334
|
+
} catch {
|
|
2335
|
+
return null;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2294
2338
|
//#endregion
|
|
2295
2339
|
//#region src/commands/clone.ts
|
|
2296
2340
|
const cloneCommand = new Command("clone").description("Clone a widget package's git repository (mints a fresh token per call)").argument("<package>", "Widget package key to clone").option("--droplet <uuid>", "Droplet UUID that owns the package (defaults to the company-owned package)").option("--dir <dir>", "Target directory (defaults to ./<package>/)").option("--skip-install", "Skip dependency installation").action((packageKey, options) => runCliAction(async () => {
|
|
@@ -3085,25 +3129,24 @@ function printWidgetPublishSummary(options) {
|
|
|
3085
3129
|
}
|
|
3086
3130
|
//#endregion
|
|
3087
3131
|
//#region src/utils/me.ts
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3132
|
+
/**
|
|
3133
|
+
* `GET /api/me` — the authenticated user's identity, used to stamp
|
|
3134
|
+
* auto-commit messages in the canonical GitSync format. The name fallback
|
|
3135
|
+
* chain and the shape it feeds both live in `@fluid-app/fluid-cli`'s
|
|
3136
|
+
* git-sync/commit-subject.ts, shared with the mist / portal / theme CLIs.
|
|
3137
|
+
*
|
|
3138
|
+
* Best-effort by contract: callers treat a rejection (or a null actor) as
|
|
3139
|
+
* a system-attributed commit rather than failing the push.
|
|
3140
|
+
*/
|
|
3092
3141
|
async function fetchCurrentUser(client) {
|
|
3093
|
-
|
|
3142
|
+
return gitSyncActorFromMe(await client.get({
|
|
3094
3143
|
responseStyle: "data",
|
|
3095
3144
|
security: [{
|
|
3096
3145
|
scheme: "bearer",
|
|
3097
3146
|
type: "http"
|
|
3098
3147
|
}],
|
|
3099
3148
|
url: "/api/me"
|
|
3100
|
-
});
|
|
3101
|
-
const composed = [str(raw.first_name), str(raw.last_name)].filter((part) => part !== null).join(" ").trim();
|
|
3102
|
-
const idValue = str(raw.public_id) ?? (typeof raw.id === "number" && Number.isFinite(raw.id) ? String(raw.id) : null);
|
|
3103
|
-
return {
|
|
3104
|
-
name: str(raw.full_name) ?? (composed.length > 0 ? composed : null) ?? str(raw.email),
|
|
3105
|
-
publicId: idValue
|
|
3106
|
-
};
|
|
3149
|
+
}));
|
|
3107
3150
|
}
|
|
3108
3151
|
//#endregion
|
|
3109
3152
|
//#region src/lib/git-sync.ts
|
|
@@ -3120,6 +3163,7 @@ async function fetchCurrentUser(client) {
|
|
|
3120
3163
|
async function syncWidgetGitRepo(options) {
|
|
3121
3164
|
const { cwd, client } = options;
|
|
3122
3165
|
const branch = options.branch ?? "main";
|
|
3166
|
+
const actor = memoizeActor(client);
|
|
3123
3167
|
if (!await isGitRepository(cwd)) await gitInit(cwd, branch);
|
|
3124
3168
|
await ensureLocalStateExcluded(cwd);
|
|
3125
3169
|
const cred = await mintWidgetGitCredential({
|
|
@@ -3148,10 +3192,11 @@ async function syncWidgetGitRepo(options) {
|
|
|
3148
3192
|
}
|
|
3149
3193
|
} else pulled = await gitAdoptFetchHead(cwd);
|
|
3150
3194
|
}
|
|
3151
|
-
await purgeLocalStateFromIndex(cwd, "Remove private Mist Desktop state from the widget repository", await
|
|
3195
|
+
await purgeLocalStateFromIndex(cwd, gitSyncCommitSubject("Remove private Mist Desktop state from the widget repository", null), commitIdentity(await actor()));
|
|
3152
3196
|
let committed = false;
|
|
3153
3197
|
if (options.commit !== false && await hasUncommittedChanges(cwd)) {
|
|
3154
|
-
|
|
3198
|
+
const resolved = await actor();
|
|
3199
|
+
await gitAutoCommit(cwd, (changes) => buildCommitMessage(cwd, resolved, changes, options.message), commitIdentity(resolved));
|
|
3155
3200
|
committed = true;
|
|
3156
3201
|
}
|
|
3157
3202
|
await gitPush(cwd, cred.remoteUrl, branch);
|
|
@@ -3161,33 +3206,33 @@ async function syncWidgetGitRepo(options) {
|
|
|
3161
3206
|
committed
|
|
3162
3207
|
};
|
|
3163
3208
|
}
|
|
3164
|
-
const DEFAULT_MESSAGE_PREFIX = "Publish from Fluid widget CLI";
|
|
3165
|
-
function timestamp() {
|
|
3166
|
-
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
3167
|
-
}
|
|
3168
3209
|
/**
|
|
3169
|
-
* Build the auto-commit message
|
|
3170
|
-
*
|
|
3171
|
-
*
|
|
3210
|
+
* Build the auto-commit message in the canonical GitSync shape —
|
|
3211
|
+
* `<name>: <action> · <timestamp> (<public_id>)` — so widget history reads
|
|
3212
|
+
* the same as mist / theme / portal history and always says who published.
|
|
3213
|
+
* A `-m` message replaces the action but keeps the stamp. An unknown actor
|
|
3214
|
+
* yields a `Fluid:` system commit rather than an unattributed one.
|
|
3172
3215
|
*/
|
|
3173
|
-
|
|
3174
|
-
const
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
return `${me.name ? `${me.name}: ` : ""}${baseMessage}${me.publicId ? ` (${me.publicId})` : ""}`;
|
|
3178
|
-
} catch {
|
|
3179
|
-
return baseMessage;
|
|
3180
|
-
}
|
|
3216
|
+
function buildCommitMessage(cwd, actor, changes, base) {
|
|
3217
|
+
const packageKey = readWidgetMarker(cwd)?.package_key;
|
|
3218
|
+
const fallback = packageKey ? `Publish widget ${subjectFragment(packageKey)}` : "Publish widget";
|
|
3219
|
+
return gitSyncCommitSubject(base?.trim() || summarizeChanges(changes) || fallback, actor);
|
|
3181
3220
|
}
|
|
3182
3221
|
/** Prefer the Fluid user for the git author when the repo has no identity. */
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3222
|
+
function commitIdentity(actor) {
|
|
3223
|
+
return actor ? {
|
|
3224
|
+
name: actor.name,
|
|
3225
|
+
email: "cli@fluid.app"
|
|
3226
|
+
} : void 0;
|
|
3227
|
+
}
|
|
3228
|
+
/**
|
|
3229
|
+
* Resolve the signed-in user once per sync, cached across callers.
|
|
3230
|
+
* Best-effort by contract: a failed or slow `/api/me` resolves to null (a
|
|
3231
|
+
* system-attributed commit) instead of failing the push.
|
|
3232
|
+
*/
|
|
3233
|
+
function memoizeActor(client) {
|
|
3234
|
+
let pending = null;
|
|
3235
|
+
return () => pending ??= resolveGitSyncActor(() => withTimeout(fetchCurrentUser(client), 5e3));
|
|
3191
3236
|
}
|
|
3192
3237
|
function withTimeout(promise, ms) {
|
|
3193
3238
|
return Promise.race([promise, new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`timed out after ${ms}ms`)), ms))]);
|