@floomhq/floom 1.0.40 → 1.0.42
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/sync-manifest.js +78 -14
- package/package.json +1 -1
package/dist/sync-manifest.js
CHANGED
|
@@ -1,60 +1,119 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
2
|
import { lstat, mkdir, open, rename, rm, stat } from "node:fs/promises";
|
|
3
|
-
import {
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
4
5
|
import { CONFIG_DIR } from "./config.js";
|
|
5
6
|
const MANIFEST_VERSION = 1;
|
|
6
|
-
const MANIFEST_PATH = join(CONFIG_DIR, "sync-manifest.json");
|
|
7
|
-
const LOCK_PATH = join(CONFIG_DIR, "sync.lock");
|
|
8
7
|
const LOCK_TIMEOUT_MS = 60_000;
|
|
9
8
|
const LOCK_STALE_MS = 5 * 60_000;
|
|
10
9
|
const SLUG_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
11
10
|
const FD_PATH_ROOT = "/proc/self/fd";
|
|
12
11
|
const SUPPORT_DIRS = new Set([
|
|
12
|
+
".github",
|
|
13
13
|
"agents",
|
|
14
14
|
"assets",
|
|
15
|
+
"bin",
|
|
15
16
|
"canvas-fonts",
|
|
16
17
|
"checks",
|
|
18
|
+
"claude",
|
|
17
19
|
"codex",
|
|
20
|
+
"contrib",
|
|
18
21
|
"core",
|
|
19
22
|
"cursor",
|
|
23
|
+
"design",
|
|
24
|
+
"docs",
|
|
20
25
|
"evidence",
|
|
21
26
|
"examples",
|
|
27
|
+
"extension",
|
|
22
28
|
"helpers",
|
|
29
|
+
"hosts",
|
|
23
30
|
"kimi",
|
|
31
|
+
"lib",
|
|
32
|
+
"migrations",
|
|
33
|
+
"model-overlays",
|
|
34
|
+
"openclaw",
|
|
24
35
|
"opencode",
|
|
25
36
|
"reference",
|
|
26
37
|
"references",
|
|
38
|
+
"remotion-starter",
|
|
27
39
|
"scripts",
|
|
28
40
|
"sdk",
|
|
29
41
|
"schemas",
|
|
42
|
+
"src",
|
|
43
|
+
"specialists",
|
|
44
|
+
"supabase",
|
|
30
45
|
"templates",
|
|
46
|
+
"test",
|
|
31
47
|
"tests",
|
|
32
48
|
"themes",
|
|
49
|
+
"vendor",
|
|
33
50
|
]);
|
|
34
51
|
const ROOT_SUPPORT_FILES = new Set([
|
|
35
52
|
".env.example",
|
|
36
53
|
"ACKNOWLEDGEMENTS.md",
|
|
54
|
+
"AGENTS.md",
|
|
55
|
+
"ARCHITECTURE.md",
|
|
56
|
+
"BROWSER.md",
|
|
37
57
|
"CHANGELOG.md",
|
|
58
|
+
"CLAUDE.md",
|
|
59
|
+
"CONTRIBUTING.md",
|
|
60
|
+
"DESIGN.md",
|
|
61
|
+
"ETHOS.md",
|
|
38
62
|
"LICENSE",
|
|
39
63
|
"LICENSE.md",
|
|
40
64
|
"LICENSE.txt",
|
|
65
|
+
"PLAN-snapshot-dropdown-interactive.md",
|
|
41
66
|
"README.md",
|
|
67
|
+
"REMOTION_PROTOCOL.md",
|
|
42
68
|
"SKILL.md.tmpl",
|
|
69
|
+
"TODOS.md",
|
|
70
|
+
"TODOS-format.md",
|
|
71
|
+
"USING_GBRAIN_WITH_GSTACK.md",
|
|
72
|
+
"VERSION",
|
|
73
|
+
".gitlab-ci.yml",
|
|
74
|
+
"actionlint.yaml",
|
|
75
|
+
"bun.lock",
|
|
76
|
+
"checklist.md",
|
|
77
|
+
"conductor.json",
|
|
78
|
+
"design-checklist.md",
|
|
79
|
+
"dx-hall-of-fame.md",
|
|
43
80
|
"editing.md",
|
|
44
81
|
"forms.md",
|
|
82
|
+
"greptile-triage.md",
|
|
45
83
|
"instructions.md",
|
|
46
84
|
"nanobanana.py",
|
|
85
|
+
"package.json",
|
|
47
86
|
"plan.md",
|
|
48
87
|
"pptxgenjs.md",
|
|
49
88
|
"reference.md",
|
|
50
89
|
"requirements.txt",
|
|
90
|
+
"setup",
|
|
51
91
|
"skill.md",
|
|
92
|
+
"slop-scan.config.json",
|
|
52
93
|
"style_guidelines.md",
|
|
53
94
|
"theme-showcase.pdf",
|
|
54
95
|
]);
|
|
55
96
|
function emptyManifest() {
|
|
56
97
|
return { version: MANIFEST_VERSION, files: {} };
|
|
57
98
|
}
|
|
99
|
+
function expandHome(path) {
|
|
100
|
+
if (path === "~")
|
|
101
|
+
return homedir();
|
|
102
|
+
if (path.startsWith("~/"))
|
|
103
|
+
return join(homedir(), path.slice(2));
|
|
104
|
+
return path;
|
|
105
|
+
}
|
|
106
|
+
export function syncManifestPath() {
|
|
107
|
+
if (process.env.FLOOM_SYNC_MANIFEST_PATH)
|
|
108
|
+
return expandHome(process.env.FLOOM_SYNC_MANIFEST_PATH);
|
|
109
|
+
return join(CONFIG_DIR, "sync-manifest.json");
|
|
110
|
+
}
|
|
111
|
+
function syncManifestDir() {
|
|
112
|
+
return dirname(syncManifestPath());
|
|
113
|
+
}
|
|
114
|
+
function syncLockPath() {
|
|
115
|
+
return join(syncManifestDir(), "sync.lock");
|
|
116
|
+
}
|
|
58
117
|
function isEntryForKey(key, value) {
|
|
59
118
|
if (!value || typeof value !== "object")
|
|
60
119
|
return false;
|
|
@@ -88,7 +147,7 @@ function isPackageFilePath(packagePath) {
|
|
|
88
147
|
export async function readSyncManifest() {
|
|
89
148
|
try {
|
|
90
149
|
await ensureSyncManifestDir();
|
|
91
|
-
const handle = await open(
|
|
150
|
+
const handle = await open(syncManifestPath(), constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
92
151
|
let body;
|
|
93
152
|
try {
|
|
94
153
|
body = await handle.readFile("utf8");
|
|
@@ -117,20 +176,23 @@ export async function readSyncManifest() {
|
|
|
117
176
|
}
|
|
118
177
|
export async function writeSyncManifest(manifest) {
|
|
119
178
|
await ensureSyncManifestDir();
|
|
120
|
-
const
|
|
121
|
-
const
|
|
179
|
+
const path = syncManifestPath();
|
|
180
|
+
const dirPath = dirname(path);
|
|
181
|
+
const targetName = basename(path);
|
|
182
|
+
const dir = await open(dirPath, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
183
|
+
const tmpBase = `${targetName}.${process.pid}.${Date.now()}`;
|
|
122
184
|
const body = JSON.stringify(manifest, null, 2);
|
|
123
185
|
try {
|
|
124
186
|
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
125
187
|
const tmpName = attempt === 0 ? `${tmpBase}.tmp` : `${tmpBase}.${attempt}.tmp`;
|
|
126
|
-
const tmpPath = childPath(dir,
|
|
188
|
+
const tmpPath = childPath(dir, dirPath, tmpName);
|
|
127
189
|
let handle = null;
|
|
128
190
|
try {
|
|
129
191
|
handle = await open(tmpPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
130
192
|
await handle.writeFile(body, "utf8");
|
|
131
193
|
await handle.close();
|
|
132
194
|
handle = null;
|
|
133
|
-
await rename(tmpPath, childPath(dir,
|
|
195
|
+
await rename(tmpPath, childPath(dir, dirPath, targetName));
|
|
134
196
|
return;
|
|
135
197
|
}
|
|
136
198
|
catch (err) {
|
|
@@ -154,8 +216,9 @@ function childPath(parent, fallbackParent, name) {
|
|
|
154
216
|
return join(resolve(fallbackParent), name);
|
|
155
217
|
}
|
|
156
218
|
export async function ensureSyncManifestDir() {
|
|
157
|
-
|
|
158
|
-
|
|
219
|
+
const dir = syncManifestDir();
|
|
220
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
221
|
+
const stat = await lstat(dir);
|
|
159
222
|
if (stat.isSymbolicLink()) {
|
|
160
223
|
const err = new Error("sync manifest directory is a symbolic link");
|
|
161
224
|
err.code = "ELOOP";
|
|
@@ -169,19 +232,20 @@ export async function ensureSyncManifestDir() {
|
|
|
169
232
|
}
|
|
170
233
|
export async function withSyncLock(fn) {
|
|
171
234
|
await ensureSyncManifestDir();
|
|
235
|
+
const lockPath = syncLockPath();
|
|
172
236
|
const startedAt = Date.now();
|
|
173
237
|
for (;;) {
|
|
174
238
|
try {
|
|
175
|
-
await mkdir(
|
|
239
|
+
await mkdir(lockPath, { mode: 0o700 });
|
|
176
240
|
break;
|
|
177
241
|
}
|
|
178
242
|
catch (err) {
|
|
179
243
|
if (err.code !== "EEXIST")
|
|
180
244
|
throw err;
|
|
181
245
|
try {
|
|
182
|
-
const lockStat = await stat(
|
|
246
|
+
const lockStat = await stat(lockPath);
|
|
183
247
|
if (Date.now() - lockStat.mtimeMs > LOCK_STALE_MS) {
|
|
184
|
-
await rm(
|
|
248
|
+
await rm(lockPath, { recursive: true, force: true });
|
|
185
249
|
continue;
|
|
186
250
|
}
|
|
187
251
|
}
|
|
@@ -200,7 +264,7 @@ export async function withSyncLock(fn) {
|
|
|
200
264
|
return await fn();
|
|
201
265
|
}
|
|
202
266
|
finally {
|
|
203
|
-
await rm(
|
|
267
|
+
await rm(lockPath, { recursive: true, force: true }).catch(() => { });
|
|
204
268
|
}
|
|
205
269
|
}
|
|
206
270
|
export function manifestKey(root, target) {
|