@fml-inc/fml 0.8.6 → 0.8.7

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.
@@ -1,7 +1,9 @@
1
1
  import {
2
- resolvePackageRoot,
3
2
  runRawSyncOnce
4
- } from "./chunk-V2XC7JTB.js";
3
+ } from "./chunk-5JJQWV4G.js";
4
+ import {
5
+ resolvePackageRoot
6
+ } from "./chunk-F57IFY3K.js";
5
7
 
6
8
  // src/observer/sync-cycle.ts
7
9
  import { fork } from "child_process";
@@ -0,0 +1,21 @@
1
+ // src/package-root.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ function resolvePackageRoot(startDir = path.dirname(fileURLToPath(import.meta.url))) {
6
+ let directory = startDir;
7
+ while (true) {
8
+ if (fs.existsSync(path.join(directory, "package.json")) && fs.existsSync(path.join(directory, "bin"))) {
9
+ return directory;
10
+ }
11
+ const parent = path.dirname(directory);
12
+ if (parent === directory) {
13
+ throw new Error(`Could not resolve FML package root from ${startDir}`);
14
+ }
15
+ directory = parent;
16
+ }
17
+ }
18
+
19
+ export {
20
+ resolvePackageRoot
21
+ };
@@ -1,5 +1,6 @@
1
1
  import {
2
- FML_HOOK_CAPTURE_DISABLED_FILE
2
+ FML_HOOK_CAPTURE_DISABLED_FILE,
3
+ FML_OBSERVER_LOG_FILE
3
4
  } from "./chunk-B3H2YWPJ.js";
4
5
 
5
6
  // src/json-file.ts
@@ -100,7 +101,42 @@ function setHookCaptureDisabled(disabled, stateFile = FML_HOOK_CAPTURE_DISABLED_
100
101
  fs2.rmSync(stateFile, { force: true });
101
102
  }
102
103
 
104
+ // src/observer/log-file.ts
105
+ import fs3 from "fs";
106
+ import path3 from "path";
107
+ var LOG_ROTATE_BYTES = 10 * 1024 * 1024;
108
+ var LOG_ROTATE_FILES = 3;
109
+ function rotateObserverLog() {
110
+ let size = 0;
111
+ try {
112
+ size = fs3.statSync(FML_OBSERVER_LOG_FILE).size;
113
+ } catch {
114
+ return;
115
+ }
116
+ if (size < LOG_ROTATE_BYTES) return;
117
+ try {
118
+ fs3.rmSync(`${FML_OBSERVER_LOG_FILE}.${LOG_ROTATE_FILES}`, { force: true });
119
+ for (let index = LOG_ROTATE_FILES - 1; index >= 1; index -= 1) {
120
+ const source = `${FML_OBSERVER_LOG_FILE}.${index}`;
121
+ if (fs3.existsSync(source)) {
122
+ fs3.renameSync(source, `${FML_OBSERVER_LOG_FILE}.${index + 1}`);
123
+ }
124
+ }
125
+ fs3.renameSync(FML_OBSERVER_LOG_FILE, `${FML_OBSERVER_LOG_FILE}.1`);
126
+ } catch {
127
+ }
128
+ }
129
+ function openObserverLog() {
130
+ fs3.mkdirSync(path3.dirname(FML_OBSERVER_LOG_FILE), {
131
+ recursive: true,
132
+ mode: 448
133
+ });
134
+ rotateObserverLog();
135
+ return fs3.openSync(FML_OBSERVER_LOG_FILE, "a", 384);
136
+ }
137
+
103
138
  export {
139
+ openObserverLog,
104
140
  readJsonFile,
105
141
  writeTextFileAtomic,
106
142
  writeJsonFile,
@@ -0,0 +1,277 @@
1
+ import {
2
+ Sentry
3
+ } from "./chunk-B3H2YWPJ.js";
4
+
5
+ // src/bin-utils.ts
6
+ import fs from "fs";
7
+ import path from "path";
8
+ import { execFileSync, execSync } from "child_process";
9
+ var IS_WIN = process.platform === "win32";
10
+ var DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
11
+ function resolveBin(name) {
12
+ const pathDirs = (process.env.PATH ?? "").split(path.delimiter).filter(Boolean);
13
+ const hasExt = path.extname(name) !== "";
14
+ const exts = !IS_WIN || hasExt ? [""] : (process.env.PATHEXT ?? DEFAULT_PATHEXT).split(";").filter(Boolean);
15
+ for (const dir of pathDirs) {
16
+ for (const ext of exts) {
17
+ const candidate = path.join(dir, name + ext);
18
+ try {
19
+ if (fs.statSync(candidate).isFile()) return candidate;
20
+ } catch {
21
+ }
22
+ }
23
+ }
24
+ return null;
25
+ }
26
+ function isWinShim(bin) {
27
+ return IS_WIN && /\.(cmd|bat)$/i.test(bin);
28
+ }
29
+ function quoteWinArg(arg) {
30
+ if (arg.length === 0) return '""';
31
+ if (/^[A-Za-z0-9@\-_+=:/.,]+$/.test(arg)) return arg;
32
+ const crt = arg.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, "$1$1");
33
+ const quoted = `"${crt}"`;
34
+ return quoted.replace(/([()%!^<>&|"])/g, "^$1");
35
+ }
36
+ function execBinSync(bin, args, opts = {}) {
37
+ const {
38
+ timeout = 1e4,
39
+ encoding = "utf-8",
40
+ stdio = "pipe",
41
+ env = process.env
42
+ } = opts;
43
+ if (isWinShim(bin)) {
44
+ const command = [bin, ...args].map(quoteWinArg).join(" ");
45
+ return execSync(command, { timeout, encoding, stdio, env });
46
+ }
47
+ return execFileSync(bin, args, { timeout, encoding, stdio, env });
48
+ }
49
+
50
+ // src/commands/update.ts
51
+ var SEMVER_PATTERN = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-((?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
52
+ var PACKAGE_NAME = "@fml-inc/fml";
53
+ var UPDATE_TIMEOUT_MS = 12e4;
54
+ var SETUP_TIMEOUT_MS = 3e5;
55
+ function getCurrentVersion() {
56
+ return true ? "0.8.7+af45d2af" : "unknown";
57
+ }
58
+ function parseVersion(version) {
59
+ const match = SEMVER_PATTERN.exec(version);
60
+ if (!match) return null;
61
+ return {
62
+ major: match[1],
63
+ minor: match[2],
64
+ patch: match[3],
65
+ prerelease: match[4]?.split(".") ?? []
66
+ };
67
+ }
68
+ function compareNumericIdentifiers(left, right) {
69
+ if (left.length !== right.length) return left.length - right.length;
70
+ if (left === right) return 0;
71
+ return left < right ? -1 : 1;
72
+ }
73
+ function compareIdentifiers(left, right) {
74
+ const leftIsNumeric = /^[0-9]+$/u.test(left);
75
+ const rightIsNumeric = /^[0-9]+$/u.test(right);
76
+ if (leftIsNumeric && rightIsNumeric) {
77
+ return compareNumericIdentifiers(left, right);
78
+ }
79
+ if (leftIsNumeric) return -1;
80
+ if (rightIsNumeric) return 1;
81
+ if (left === right) return 0;
82
+ return left < right ? -1 : 1;
83
+ }
84
+ function compareVersions(left, right) {
85
+ const leftVersion = parseVersion(left);
86
+ const rightVersion = parseVersion(right);
87
+ if (!leftVersion || !rightVersion) {
88
+ throw new Error("FML update requires valid semantic versions.");
89
+ }
90
+ for (const part of ["major", "minor", "patch"]) {
91
+ const difference = compareNumericIdentifiers(
92
+ leftVersion[part],
93
+ rightVersion[part]
94
+ );
95
+ if (difference) return difference;
96
+ }
97
+ if (!leftVersion.prerelease.length && !rightVersion.prerelease.length) {
98
+ return 0;
99
+ }
100
+ if (!leftVersion.prerelease.length) return 1;
101
+ if (!rightVersion.prerelease.length) return -1;
102
+ const length = Math.max(
103
+ leftVersion.prerelease.length,
104
+ rightVersion.prerelease.length
105
+ );
106
+ for (let index = 0; index < length; index += 1) {
107
+ const leftIdentifier = leftVersion.prerelease[index];
108
+ const rightIdentifier = rightVersion.prerelease[index];
109
+ if (leftIdentifier === void 0) return -1;
110
+ if (rightIdentifier === void 0) return 1;
111
+ const difference = compareIdentifiers(leftIdentifier, rightIdentifier);
112
+ if (difference) return difference;
113
+ }
114
+ return 0;
115
+ }
116
+ function isUpdatableVersion(version) {
117
+ if (version === "dev" || version === "unknown") return false;
118
+ const parsed = parseVersion(version);
119
+ if (!parsed) return false;
120
+ if (parsed.prerelease.includes("dev")) return false;
121
+ return true;
122
+ }
123
+ function resolveUpdateChannel(version) {
124
+ const match = SEMVER_PATTERN.exec(version);
125
+ if (!match) return "latest";
126
+ const prerelease = match[4];
127
+ if (!prerelease) return "latest";
128
+ return prerelease.split(".").every((identifier) => {
129
+ return !(identifier.length > 1 && identifier.startsWith("0") && /^[0-9]+$/u.test(identifier));
130
+ }) ? "next" : "latest";
131
+ }
132
+ function resolveUpdateTarget(version) {
133
+ return `${PACKAGE_NAME}@${resolveUpdateChannel(version)}`;
134
+ }
135
+ function createUpdateEnvironment(env) {
136
+ const updateEnv = { ...env };
137
+ for (const key of Object.keys(updateEnv)) {
138
+ if (key.toLowerCase() === "npm_config_registry") {
139
+ delete updateEnv[key];
140
+ }
141
+ }
142
+ return updateEnv;
143
+ }
144
+ function parseAvailableVersion(output) {
145
+ let response;
146
+ try {
147
+ response = JSON.parse(output);
148
+ } catch {
149
+ throw new Error("Could not read the available FML version from npm.");
150
+ }
151
+ const version = Array.isArray(response) && response.length === 1 ? response[0] : response;
152
+ if (typeof version !== "string" || !parseVersion(version)) {
153
+ throw new Error("Could not read the available FML version from npm.");
154
+ }
155
+ return version;
156
+ }
157
+ async function checkForUpdate(dependencies) {
158
+ if (!isUpdatableVersion(dependencies.currentVersion)) {
159
+ throw new Error("FML update requires a released semantic version.");
160
+ }
161
+ const channel = resolveUpdateChannel(dependencies.currentVersion);
162
+ const target = resolveUpdateTarget(dependencies.currentVersion);
163
+ const updateEnv = createUpdateEnvironment(dependencies.env);
164
+ let output;
165
+ if (dependencies.runNpmView) {
166
+ output = await dependencies.runNpmView(target, updateEnv);
167
+ } else {
168
+ const npm = dependencies.resolveBin?.("npm");
169
+ if (!npm) throw new Error("npm was not found on PATH");
170
+ if (!dependencies.execBinSync) {
171
+ throw new Error("FML update check requires an npm view runner.");
172
+ }
173
+ output = dependencies.execBinSync(
174
+ npm,
175
+ ["view", target, "version", "--json"],
176
+ {
177
+ timeout: UPDATE_TIMEOUT_MS,
178
+ env: updateEnv
179
+ }
180
+ );
181
+ }
182
+ const latest = parseAvailableVersion(output);
183
+ return {
184
+ current: dependencies.currentVersion,
185
+ latest,
186
+ channel,
187
+ newer: compareVersions(latest, dependencies.currentVersion) > 0
188
+ };
189
+ }
190
+ function formatUpdateError(err) {
191
+ if (!(err instanceof Error)) return String(err);
192
+ const stderr = err.stderr;
193
+ if (typeof stderr === "string") return stderr;
194
+ if (Buffer.isBuffer(stderr)) return stderr.toString();
195
+ return err.message;
196
+ }
197
+ async function handleUpdate(overrides = {}) {
198
+ const dependencies = {
199
+ currentVersion: getCurrentVersion(),
200
+ env: process.env,
201
+ resolveBin,
202
+ execBinSync,
203
+ captureException: Sentry.captureException,
204
+ log: console.log,
205
+ error: console.error,
206
+ exit: process.exit,
207
+ nonInteractive: false,
208
+ ...overrides
209
+ };
210
+ dependencies.log(`Current version: ${dependencies.currentVersion}`);
211
+ if (!isUpdatableVersion(dependencies.currentVersion)) {
212
+ dependencies.log("This is a development build; `fml update` is disabled.");
213
+ return;
214
+ }
215
+ try {
216
+ const npm = dependencies.resolveBin("npm");
217
+ if (!npm) throw new Error("npm was not found on PATH");
218
+ const check = await checkForUpdate({
219
+ ...dependencies,
220
+ resolveBin: () => npm
221
+ });
222
+ if (!check.newer) {
223
+ dependencies.log(
224
+ `FML is already up to date (${dependencies.currentVersion}).`
225
+ );
226
+ return;
227
+ }
228
+ const updateEnv = createUpdateEnvironment(dependencies.env);
229
+ const availableVersion = check.latest;
230
+ const channel = check.channel;
231
+ dependencies.log(
232
+ `Updating FML from ${dependencies.currentVersion} to ${availableVersion} on the ${channel} channel...
233
+ `
234
+ );
235
+ dependencies.execBinSync(
236
+ npm,
237
+ ["install", "--global", `${PACKAGE_NAME}@${availableVersion}`],
238
+ { timeout: UPDATE_TIMEOUT_MS, env: updateEnv }
239
+ );
240
+ dependencies.log(
241
+ `
242
+ FML updated from ${dependencies.currentVersion} to ${availableVersion}. Refreshing setup...`
243
+ );
244
+ try {
245
+ const fml = dependencies.resolveBin("fml");
246
+ if (!fml) throw new Error("Updated FML was not found on PATH");
247
+ dependencies.execBinSync(
248
+ fml,
249
+ dependencies.nonInteractive ? ["setup", "--non-interactive"] : ["setup"],
250
+ { timeout: SETUP_TIMEOUT_MS, stdio: "inherit" }
251
+ );
252
+ dependencies.log("FML setup complete.");
253
+ } catch (err) {
254
+ dependencies.captureException(err);
255
+ dependencies.error(
256
+ "FML updated, but setup failed:",
257
+ formatUpdateError(err)
258
+ );
259
+ dependencies.error("Run `fml setup` to finish refreshing FML.");
260
+ dependencies.exit(1);
261
+ }
262
+ } catch (err) {
263
+ dependencies.captureException(err);
264
+ dependencies.error("Update failed:", formatUpdateError(err));
265
+ dependencies.exit(1);
266
+ }
267
+ }
268
+
269
+ export {
270
+ resolveBin,
271
+ isWinShim,
272
+ quoteWinArg,
273
+ execBinSync,
274
+ isUpdatableVersion,
275
+ checkForUpdate,
276
+ handleUpdate
277
+ };