@tinyrack/devsync 1.1.0 → 1.3.0

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.
Files changed (220) hide show
  1. package/README.md +230 -62
  2. package/dist/cli/base-command.d.ts +14 -0
  3. package/dist/cli/base-command.d.ts.map +1 -0
  4. package/dist/cli/base-command.js +22 -0
  5. package/dist/cli/base-command.js.map +1 -0
  6. package/dist/cli/commands/dir.d.ts +8 -0
  7. package/dist/cli/commands/dir.d.ts.map +1 -0
  8. package/dist/cli/commands/dir.js +16 -0
  9. package/dist/cli/commands/dir.js.map +1 -0
  10. package/dist/cli/commands/doctor.d.ts +8 -0
  11. package/dist/cli/commands/doctor.d.ts.map +1 -0
  12. package/dist/cli/commands/doctor.js +18 -0
  13. package/dist/cli/commands/doctor.js.map +1 -0
  14. package/dist/cli/commands/index.d.ts +23 -0
  15. package/dist/cli/commands/index.d.ts.map +1 -0
  16. package/dist/cli/commands/index.js +23 -0
  17. package/dist/cli/commands/index.js.map +1 -0
  18. package/dist/cli/commands/init.d.ts +15 -0
  19. package/dist/cli/commands/init.d.ts.map +1 -0
  20. package/dist/cli/commands/init.js +43 -0
  21. package/dist/cli/commands/init.js.map +1 -0
  22. package/dist/cli/commands/machine/list.d.ts +7 -0
  23. package/dist/cli/commands/machine/list.d.ts.map +1 -0
  24. package/dist/cli/commands/machine/list.js +12 -0
  25. package/dist/cli/commands/machine/list.js.map +1 -0
  26. package/dist/cli/commands/machine/use.d.ts +11 -0
  27. package/dist/cli/commands/machine/use.d.ts.map +1 -0
  28. package/dist/cli/commands/machine/use.js +28 -0
  29. package/dist/cli/commands/machine/use.js.map +1 -0
  30. package/dist/cli/commands/pull.d.ts +12 -0
  31. package/dist/cli/commands/pull.d.ts.map +1 -0
  32. package/dist/cli/commands/pull.js +34 -0
  33. package/dist/cli/commands/pull.js.map +1 -0
  34. package/dist/cli/commands/push.d.ts +12 -0
  35. package/dist/cli/commands/push.d.ts.map +1 -0
  36. package/dist/cli/commands/push.js +34 -0
  37. package/dist/cli/commands/push.js.map +1 -0
  38. package/dist/cli/commands/status.d.ts +11 -0
  39. package/dist/cli/commands/status.d.ts.map +1 -0
  40. package/dist/cli/commands/status.js +27 -0
  41. package/dist/cli/commands/status.js.map +1 -0
  42. package/dist/cli/commands/track.d.ts +16 -0
  43. package/dist/cli/commands/track.d.ts.map +1 -0
  44. package/dist/cli/commands/track.js +82 -0
  45. package/dist/cli/commands/track.js.map +1 -0
  46. package/dist/cli/commands/untrack.d.ts +11 -0
  47. package/dist/cli/commands/untrack.d.ts.map +1 -0
  48. package/dist/cli/commands/untrack.js +28 -0
  49. package/dist/cli/commands/untrack.js.map +1 -0
  50. package/dist/config/global-config.d.ts +21 -0
  51. package/dist/config/global-config.d.ts.map +1 -0
  52. package/dist/config/global-config.js +106 -0
  53. package/dist/config/global-config.js.map +1 -0
  54. package/dist/config/platform.d.ts +11 -0
  55. package/dist/config/platform.d.ts.map +1 -0
  56. package/dist/config/platform.js +19 -0
  57. package/dist/config/platform.js.map +1 -0
  58. package/dist/config/sync.d.ts +107 -0
  59. package/dist/config/sync.d.ts.map +1 -0
  60. package/dist/config/sync.js +424 -0
  61. package/dist/config/sync.js.map +1 -0
  62. package/dist/config/xdg.d.ts +14 -0
  63. package/dist/config/xdg.d.ts.map +1 -0
  64. package/dist/config/xdg.js +102 -0
  65. package/dist/config/xdg.js.map +1 -0
  66. package/dist/index.d.ts +3 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/{src/index.ts → dist/index.js} +1 -1
  69. package/dist/index.js.map +1 -0
  70. package/dist/lib/file-mode.d.ts +3 -0
  71. package/dist/lib/file-mode.d.ts.map +1 -0
  72. package/dist/lib/file-mode.js +7 -0
  73. package/dist/lib/file-mode.js.map +1 -0
  74. package/dist/lib/output.d.ts +31 -0
  75. package/dist/lib/output.d.ts.map +1 -0
  76. package/dist/lib/output.js +198 -0
  77. package/dist/lib/output.js.map +1 -0
  78. package/dist/lib/path.d.ts +5 -0
  79. package/dist/lib/path.d.ts.map +1 -0
  80. package/dist/lib/path.js +25 -0
  81. package/dist/lib/path.js.map +1 -0
  82. package/dist/lib/string.d.ts +2 -0
  83. package/dist/lib/string.d.ts.map +1 -0
  84. package/dist/lib/string.js +4 -0
  85. package/dist/lib/string.js.map +1 -0
  86. package/dist/lib/validation.d.ts +3 -0
  87. package/dist/lib/validation.d.ts.map +1 -0
  88. package/dist/lib/validation.js +9 -0
  89. package/dist/lib/validation.js.map +1 -0
  90. package/dist/services/add.d.ts +20 -0
  91. package/dist/services/add.d.ts.map +1 -0
  92. package/dist/services/add.js +161 -0
  93. package/dist/services/add.js.map +1 -0
  94. package/dist/services/config-file.d.ts +45 -0
  95. package/dist/services/config-file.d.ts.map +1 -0
  96. package/dist/services/config-file.js +35 -0
  97. package/dist/services/config-file.js.map +1 -0
  98. package/dist/services/crypto.d.ts +9 -0
  99. package/dist/services/crypto.d.ts.map +1 -0
  100. package/dist/services/crypto.js +75 -0
  101. package/dist/services/crypto.js.map +1 -0
  102. package/dist/services/doctor.d.ts +16 -0
  103. package/dist/services/doctor.d.ts.map +1 -0
  104. package/dist/services/doctor.js +84 -0
  105. package/dist/services/doctor.js.map +1 -0
  106. package/dist/services/error.d.ts +14 -0
  107. package/dist/services/error.d.ts.map +1 -0
  108. package/dist/services/error.js +38 -0
  109. package/dist/services/error.js.map +1 -0
  110. package/dist/services/filesystem.d.ts +15 -0
  111. package/dist/services/filesystem.d.ts.map +1 -0
  112. package/dist/services/filesystem.js +113 -0
  113. package/dist/services/filesystem.js.map +1 -0
  114. package/dist/services/forget.d.ts +14 -0
  115. package/dist/services/forget.d.ts.map +1 -0
  116. package/dist/services/forget.js +124 -0
  117. package/dist/services/forget.js.map +1 -0
  118. package/dist/services/git.d.ts +10 -0
  119. package/dist/services/git.d.ts.map +1 -0
  120. package/dist/services/git.js +57 -0
  121. package/dist/services/git.js.map +1 -0
  122. package/dist/services/init.d.ts +19 -0
  123. package/dist/services/init.d.ts.map +1 -0
  124. package/dist/services/init.js +203 -0
  125. package/dist/services/init.js.map +1 -0
  126. package/dist/services/local-materialization.d.ts +28 -0
  127. package/dist/services/local-materialization.d.ts.map +1 -0
  128. package/dist/services/local-materialization.js +262 -0
  129. package/dist/services/local-materialization.js.map +1 -0
  130. package/dist/services/local-snapshot.d.ts +25 -0
  131. package/dist/services/local-snapshot.d.ts.map +1 -0
  132. package/dist/services/local-snapshot.js +93 -0
  133. package/dist/services/local-snapshot.js.map +1 -0
  134. package/dist/services/machine.d.ts +40 -0
  135. package/dist/services/machine.d.ts.map +1 -0
  136. package/dist/services/machine.js +113 -0
  137. package/dist/services/machine.js.map +1 -0
  138. package/dist/services/paths.d.ts +13 -0
  139. package/dist/services/paths.d.ts.map +1 -0
  140. package/dist/services/paths.js +71 -0
  141. package/dist/services/paths.js.map +1 -0
  142. package/dist/services/pull.d.ts +28 -0
  143. package/dist/services/pull.d.ts.map +1 -0
  144. package/dist/services/pull.js +67 -0
  145. package/dist/services/pull.js.map +1 -0
  146. package/dist/services/push.d.ts +35 -0
  147. package/dist/services/push.d.ts.map +1 -0
  148. package/dist/services/push.js +96 -0
  149. package/dist/services/push.js.map +1 -0
  150. package/dist/services/repo-artifacts.d.ts +52 -0
  151. package/dist/services/repo-artifacts.d.ts.map +1 -0
  152. package/dist/services/repo-artifacts.js +251 -0
  153. package/dist/services/repo-artifacts.js.map +1 -0
  154. package/dist/services/repo-snapshot.d.ts +6 -0
  155. package/dist/services/repo-snapshot.d.ts.map +1 -0
  156. package/dist/services/repo-snapshot.js +163 -0
  157. package/dist/services/repo-snapshot.js.map +1 -0
  158. package/dist/services/runtime.d.ts +40 -0
  159. package/dist/services/runtime.d.ts.map +1 -0
  160. package/dist/services/runtime.js +71 -0
  161. package/dist/services/runtime.js.map +1 -0
  162. package/dist/services/set.d.ts +38 -0
  163. package/dist/services/set.d.ts.map +1 -0
  164. package/dist/services/set.js +184 -0
  165. package/dist/services/set.js.map +1 -0
  166. package/dist/services/status.d.ts +30 -0
  167. package/dist/services/status.d.ts.map +1 -0
  168. package/dist/services/status.js +35 -0
  169. package/dist/services/status.js.map +1 -0
  170. package/package.json +15 -7
  171. package/src/cli/commands/add.ts +0 -40
  172. package/src/cli/commands/cd.ts +0 -80
  173. package/src/cli/commands/doctor.ts +0 -20
  174. package/src/cli/commands/forget.ts +0 -32
  175. package/src/cli/commands/index.ts +0 -23
  176. package/src/cli/commands/init.ts +0 -43
  177. package/src/cli/commands/list.ts +0 -17
  178. package/src/cli/commands/pull.ts +0 -31
  179. package/src/cli/commands/push.ts +0 -31
  180. package/src/cli/commands/set.ts +0 -47
  181. package/src/cli/commands/status.ts +0 -18
  182. package/src/cli/sync-output.test.ts +0 -173
  183. package/src/cli/sync-output.ts +0 -200
  184. package/src/config/sync.test.ts +0 -609
  185. package/src/config/sync.ts +0 -572
  186. package/src/config/xdg.ts +0 -138
  187. package/src/lib/string.test.ts +0 -13
  188. package/src/lib/string.ts +0 -3
  189. package/src/lib/validation.test.ts +0 -32
  190. package/src/lib/validation.ts +0 -11
  191. package/src/services/add.ts +0 -178
  192. package/src/services/config-file.test.ts +0 -161
  193. package/src/services/config-file.ts +0 -101
  194. package/src/services/crypto.test.ts +0 -132
  195. package/src/services/crypto.ts +0 -83
  196. package/src/services/doctor.ts +0 -142
  197. package/src/services/error.ts +0 -6
  198. package/src/services/filesystem.test.ts +0 -171
  199. package/src/services/filesystem.ts +0 -183
  200. package/src/services/forget.ts +0 -261
  201. package/src/services/git.test.ts +0 -83
  202. package/src/services/git.ts +0 -74
  203. package/src/services/init.test.ts +0 -109
  204. package/src/services/init.ts +0 -244
  205. package/src/services/list.ts +0 -63
  206. package/src/services/local-materialization.ts +0 -421
  207. package/src/services/local-snapshot.ts +0 -173
  208. package/src/services/paths.test.ts +0 -74
  209. package/src/services/paths.ts +0 -98
  210. package/src/services/pull.ts +0 -144
  211. package/src/services/push.ts +0 -168
  212. package/src/services/repo-artifacts.ts +0 -262
  213. package/src/services/repo-snapshot.ts +0 -197
  214. package/src/services/runtime.ts +0 -57
  215. package/src/services/set.ts +0 -383
  216. package/src/services/status.ts +0 -57
  217. package/src/services/sync.dry-run.test.ts +0 -179
  218. package/src/services/sync.runtime.test.ts +0 -756
  219. package/src/services/sync.service.test.ts +0 -1169
  220. package/src/test/helpers/sync-fixture.ts +0 -47
@@ -1,609 +0,0 @@
1
- import { rm, writeFile } from "node:fs/promises";
2
- import { homedir } from "node:os";
3
- import { join } from "node:path";
4
-
5
- import { afterEach, describe, expect, it } from "vitest";
6
-
7
- import {
8
- isIgnoredSyncPath,
9
- isSecretSyncPath,
10
- parseSyncConfig,
11
- readSyncConfig,
12
- resolveSyncMode,
13
- syncSecretArtifactSuffix,
14
- } from "#app/config/sync.ts";
15
- import {
16
- resolveConfiguredAbsolutePath,
17
- resolveHomeConfiguredAbsolutePath,
18
- resolveHomeDirectory,
19
- resolveXdgConfigHome,
20
- } from "#app/config/xdg.ts";
21
- import { DevsyncError } from "#app/services/error.ts";
22
- import { createTemporaryDirectory } from "../test/helpers/sync-fixture.ts";
23
-
24
- const testHomeDirectory = "/tmp/devsync-home";
25
- const testXdgConfigHome = "/tmp/devsync-xdg";
26
- const temporaryDirectories: string[] = [];
27
-
28
- afterEach(async () => {
29
- while (temporaryDirectories.length > 0) {
30
- const directory = temporaryDirectories.pop();
31
-
32
- if (directory !== undefined) {
33
- await rm(directory, { force: true, recursive: true });
34
- }
35
- }
36
- });
37
-
38
- describe("resolveHomeDirectory", () => {
39
- it("falls back to the operating system home directory", () => {
40
- expect(resolveHomeDirectory({})).toBe(homedir());
41
- });
42
-
43
- it("prefers HOME when set", () => {
44
- expect(
45
- resolveHomeDirectory({
46
- HOME: testHomeDirectory,
47
- }),
48
- ).toBe(testHomeDirectory);
49
- });
50
- });
51
-
52
- describe("resolveXdgConfigHome", () => {
53
- it("falls back to the default XDG config home", () => {
54
- expect(resolveXdgConfigHome({})).toBe(join(homedir(), ".config"));
55
- });
56
-
57
- it("derives the default XDG config home from HOME", () => {
58
- expect(
59
- resolveXdgConfigHome({
60
- HOME: testHomeDirectory,
61
- }),
62
- ).toBe(join(testHomeDirectory, ".config"));
63
- });
64
-
65
- it("prefers XDG_CONFIG_HOME when set", () => {
66
- expect(
67
- resolveXdgConfigHome({
68
- XDG_CONFIG_HOME: testXdgConfigHome,
69
- }),
70
- ).toBe(testXdgConfigHome);
71
- });
72
- });
73
-
74
- describe("configured path resolution", () => {
75
- it("expands home-relative path prefixes", () => {
76
- expect(
77
- resolveHomeConfiguredAbsolutePath("~/demo", {
78
- HOME: testHomeDirectory,
79
- }),
80
- ).toBe(join(testHomeDirectory, "demo"));
81
- });
82
-
83
- it("expands supported path prefixes for devsync-owned paths", () => {
84
- expect(
85
- resolveConfiguredAbsolutePath("~/demo", {
86
- HOME: testHomeDirectory,
87
- }),
88
- ).toBe(join(testHomeDirectory, "demo"));
89
- expect(
90
- resolveConfiguredAbsolutePath("$XDG_CONFIG_HOME/devsync/keys.txt", {
91
- XDG_CONFIG_HOME: testXdgConfigHome,
92
- }),
93
- ).toBe(join(testXdgConfigHome, "devsync", "keys.txt"));
94
- });
95
- });
96
-
97
- describe("parseSyncConfig", () => {
98
- it("resolves home-scoped entry paths and normalizes overrides", () => {
99
- const config = parseSyncConfig(
100
- {
101
- version: 1,
102
- age: {
103
- identityFile: "$XDG_CONFIG_HOME/devsync/age/keys.txt",
104
- recipients: ["age1example"],
105
- },
106
- entries: [
107
- {
108
- kind: "directory",
109
- localPath: "~/.config/mytool",
110
- mode: "secret",
111
- name: ".config/mytool",
112
- overrides: {
113
- "cache\\tmp/": "ignore",
114
- "cache\\tmp\\keep.json": "normal",
115
- },
116
- repoPath: ".config\\mytool",
117
- },
118
- ],
119
- },
120
- {
121
- HOME: testHomeDirectory,
122
- XDG_CONFIG_HOME: testXdgConfigHome,
123
- },
124
- );
125
-
126
- expect(config.age.identityFile).toBe(
127
- join(testXdgConfigHome, "devsync", "age", "keys.txt"),
128
- );
129
- expect(config.entries).toEqual([
130
- {
131
- configuredLocalPath: "~/.config/mytool",
132
- kind: "directory",
133
- localPath: join(testHomeDirectory, ".config", "mytool"),
134
- mode: "secret",
135
- name: ".config/mytool",
136
- overrides: [
137
- {
138
- match: "subtree",
139
- mode: "ignore",
140
- path: "cache/tmp",
141
- },
142
- {
143
- match: "exact",
144
- mode: "normal",
145
- path: "cache/tmp/keep.json",
146
- },
147
- ],
148
- repoPath: ".config/mytool",
149
- },
150
- ]);
151
- });
152
-
153
- it("accepts absolute sync entry paths that stay inside HOME", () => {
154
- const config = parseSyncConfig(
155
- {
156
- version: 1,
157
- age: {
158
- identityFile: "/tmp/identity.txt",
159
- recipients: ["age1example"],
160
- },
161
- entries: [
162
- {
163
- kind: "directory",
164
- localPath: "/tmp/devsync-home/bundle",
165
- mode: "normal",
166
- name: "bundle",
167
- repoPath: "bundle",
168
- },
169
- ],
170
- },
171
- {
172
- HOME: testHomeDirectory,
173
- },
174
- );
175
-
176
- expect(config.entries[0]?.localPath).toBe("/tmp/devsync-home/bundle");
177
- expect(config.entries[0]?.mode).toBe("normal");
178
- });
179
-
180
- it("rejects sync entry local paths outside HOME", () => {
181
- expect(() => {
182
- parseSyncConfig(
183
- {
184
- version: 1,
185
- age: {
186
- identityFile: "/tmp/identity.txt",
187
- recipients: ["age1example"],
188
- },
189
- entries: [
190
- {
191
- kind: "directory",
192
- localPath: "/tmp/outside-home/bundle",
193
- mode: "normal",
194
- name: "bundle",
195
- repoPath: "bundle",
196
- },
197
- ],
198
- },
199
- {
200
- HOME: testHomeDirectory,
201
- },
202
- );
203
- }).toThrowError(DevsyncError);
204
- });
205
-
206
- it("rejects XDG tokens for sync entry local paths", () => {
207
- expect(() => {
208
- parseSyncConfig(
209
- {
210
- version: 1,
211
- age: {
212
- identityFile: "$XDG_CONFIG_HOME/devsync/age/keys.txt",
213
- recipients: ["age1example"],
214
- },
215
- entries: [
216
- {
217
- kind: "directory",
218
- localPath: "$XDG_CONFIG_HOME/bundle",
219
- mode: "normal",
220
- name: "bundle",
221
- repoPath: "bundle",
222
- },
223
- ],
224
- },
225
- {
226
- HOME: testHomeDirectory,
227
- XDG_CONFIG_HOME: testXdgConfigHome,
228
- },
229
- );
230
- }).toThrowError(DevsyncError);
231
- });
232
-
233
- it("rejects unsupported glob fields", () => {
234
- expect(() => {
235
- parseSyncConfig(
236
- {
237
- version: 1,
238
- age: {
239
- identityFile: "/tmp/identity.txt",
240
- recipients: ["age1example"],
241
- },
242
- entries: [
243
- {
244
- kind: "directory",
245
- localPath: "~/bundle",
246
- mode: "normal",
247
- name: "bundle",
248
- repoPath: "bundle",
249
- secretGlobs: ["**"],
250
- },
251
- ],
252
- },
253
- {
254
- HOME: testHomeDirectory,
255
- },
256
- );
257
- }).toThrowError(DevsyncError);
258
- });
259
-
260
- it("rejects repository paths and overrides that use the reserved secret suffix", () => {
261
- expect(() => {
262
- parseSyncConfig(
263
- {
264
- version: 1,
265
- age: {
266
- identityFile: "/tmp/identity.txt",
267
- recipients: ["age1example"],
268
- },
269
- entries: [
270
- {
271
- kind: "file",
272
- localPath: "~/bundle/token.txt",
273
- mode: "normal",
274
- name: "bundle/token.txt",
275
- repoPath: `bundle/token.txt${syncSecretArtifactSuffix}`,
276
- },
277
- ],
278
- },
279
- {
280
- HOME: testHomeDirectory,
281
- },
282
- );
283
- }).toThrowError(DevsyncError);
284
-
285
- expect(() => {
286
- parseSyncConfig(
287
- {
288
- version: 1,
289
- age: {
290
- identityFile: "/tmp/identity.txt",
291
- recipients: ["age1example"],
292
- },
293
- entries: [
294
- {
295
- kind: "directory",
296
- localPath: "~/bundle",
297
- mode: "normal",
298
- name: "bundle",
299
- overrides: {
300
- [`token.txt${syncSecretArtifactSuffix}`]: "secret",
301
- },
302
- repoPath: "bundle",
303
- },
304
- ],
305
- },
306
- {
307
- HOME: testHomeDirectory,
308
- },
309
- );
310
- }).toThrowError(DevsyncError);
311
- });
312
-
313
- it("rejects overrides on file entries", () => {
314
- expect(() => {
315
- parseSyncConfig(
316
- {
317
- version: 1,
318
- age: {
319
- identityFile: "/tmp/identity.txt",
320
- recipients: ["age1example"],
321
- },
322
- entries: [
323
- {
324
- kind: "file",
325
- localPath: "~/bundle.json",
326
- mode: "normal",
327
- name: "bundle.json",
328
- overrides: {
329
- "nested.json": "secret",
330
- },
331
- repoPath: "bundle.json",
332
- },
333
- ],
334
- },
335
- {
336
- HOME: testHomeDirectory,
337
- },
338
- );
339
- }).toThrowError(DevsyncError);
340
- });
341
-
342
- it("rejects duplicate overrides after normalization", () => {
343
- expect(() => {
344
- parseSyncConfig(
345
- {
346
- version: 1,
347
- age: {
348
- identityFile: "/tmp/identity.txt",
349
- recipients: ["age1example"],
350
- },
351
- entries: [
352
- {
353
- kind: "directory",
354
- localPath: "~/bundle",
355
- mode: "normal",
356
- name: "bundle",
357
- overrides: {
358
- "cache/": "ignore",
359
- "cache//": "secret",
360
- },
361
- repoPath: "bundle",
362
- },
363
- ],
364
- },
365
- {
366
- HOME: testHomeDirectory,
367
- },
368
- );
369
- }).toThrowError(DevsyncError);
370
- });
371
-
372
- it("rejects duplicate entry names and overlapping entry paths", () => {
373
- expect(() => {
374
- parseSyncConfig(
375
- {
376
- version: 1,
377
- age: {
378
- identityFile: "/tmp/identity.txt",
379
- recipients: ["age1example"],
380
- },
381
- entries: [
382
- {
383
- kind: "file",
384
- localPath: "~/bundle/one.json",
385
- mode: "normal",
386
- name: "bundle",
387
- repoPath: "bundle/one.json",
388
- },
389
- {
390
- kind: "file",
391
- localPath: "~/bundle/two.json",
392
- mode: "normal",
393
- name: "bundle",
394
- repoPath: "bundle/two.json",
395
- },
396
- ],
397
- },
398
- {
399
- HOME: testHomeDirectory,
400
- },
401
- );
402
- }).toThrowError(DevsyncError);
403
-
404
- expect(() => {
405
- parseSyncConfig(
406
- {
407
- version: 1,
408
- age: {
409
- identityFile: "/tmp/identity.txt",
410
- recipients: ["age1example"],
411
- },
412
- entries: [
413
- {
414
- kind: "directory",
415
- localPath: "~/bundle",
416
- mode: "normal",
417
- name: "bundle",
418
- repoPath: "bundle",
419
- },
420
- {
421
- kind: "file",
422
- localPath: "~/bundle/file.txt",
423
- mode: "normal",
424
- name: "bundle/file.txt",
425
- repoPath: "bundle/file.txt",
426
- },
427
- ],
428
- },
429
- {
430
- HOME: testHomeDirectory,
431
- },
432
- );
433
- }).toThrowError(DevsyncError);
434
- });
435
-
436
- it("rejects the home directory itself and escaping rule paths", () => {
437
- expect(() => {
438
- parseSyncConfig(
439
- {
440
- version: 1,
441
- age: {
442
- identityFile: "/tmp/identity.txt",
443
- recipients: ["age1example"],
444
- },
445
- entries: [
446
- {
447
- kind: "directory",
448
- localPath: "~",
449
- mode: "normal",
450
- name: "bundle",
451
- repoPath: "bundle",
452
- },
453
- ],
454
- },
455
- {
456
- HOME: testHomeDirectory,
457
- },
458
- );
459
- }).toThrowError(DevsyncError);
460
-
461
- expect(() => {
462
- parseSyncConfig(
463
- {
464
- version: 1,
465
- age: {
466
- identityFile: "/tmp/identity.txt",
467
- recipients: ["age1example"],
468
- },
469
- entries: [
470
- {
471
- kind: "directory",
472
- localPath: "~/bundle",
473
- mode: "normal",
474
- name: "bundle",
475
- overrides: {
476
- "../token.txt": "secret",
477
- },
478
- repoPath: "bundle",
479
- },
480
- ],
481
- },
482
- {
483
- HOME: testHomeDirectory,
484
- },
485
- );
486
- }).toThrowError(DevsyncError);
487
- });
488
-
489
- it("resolves modes with exact rules overriding subtree rules and defaults", () => {
490
- const config = parseSyncConfig(
491
- {
492
- version: 1,
493
- age: {
494
- identityFile: "/tmp/identity.txt",
495
- recipients: ["age1example"],
496
- },
497
- entries: [
498
- {
499
- kind: "directory",
500
- localPath: "~/bundle",
501
- mode: "secret",
502
- name: "bundle",
503
- overrides: {
504
- "private/": "ignore",
505
- "private/public.json": "normal",
506
- },
507
- repoPath: "bundle",
508
- },
509
- ],
510
- },
511
- {
512
- HOME: testHomeDirectory,
513
- },
514
- );
515
-
516
- expect(resolveSyncMode(config, "bundle/plain.txt")).toBe("secret");
517
- expect(resolveSyncMode(config, "bundle/private/token.txt")).toBe("ignore");
518
- expect(resolveSyncMode(config, "bundle/private/public.json")).toBe(
519
- "normal",
520
- );
521
- });
522
-
523
- it("prefers deeper subtree rules and exact matches over same-path subtrees", () => {
524
- const config = parseSyncConfig(
525
- {
526
- version: 1,
527
- age: {
528
- identityFile: "/tmp/identity.txt",
529
- recipients: ["age1example"],
530
- },
531
- entries: [
532
- {
533
- kind: "directory",
534
- localPath: "~/bundle",
535
- mode: "normal",
536
- name: "bundle",
537
- overrides: {
538
- "private/": "secret",
539
- "private/public/": "ignore",
540
- "private/public/file.txt": "normal",
541
- "private/public/file.txt/": "secret",
542
- },
543
- repoPath: "bundle",
544
- },
545
- ],
546
- },
547
- {
548
- HOME: testHomeDirectory,
549
- },
550
- );
551
-
552
- expect(resolveSyncMode(config, "bundle/private/secret.txt")).toBe("secret");
553
- expect(resolveSyncMode(config, "bundle/private/public/child.txt")).toBe(
554
- "ignore",
555
- );
556
- expect(resolveSyncMode(config, "bundle/private/public/file.txt")).toBe(
557
- "normal",
558
- );
559
- });
560
-
561
- it("returns undefined for unmanaged paths and exposes helper predicates", () => {
562
- const config = parseSyncConfig(
563
- {
564
- version: 1,
565
- age: {
566
- identityFile: "/tmp/identity.txt",
567
- recipients: ["age1example"],
568
- },
569
- entries: [
570
- {
571
- kind: "directory",
572
- localPath: "~/bundle",
573
- mode: "secret",
574
- name: "bundle",
575
- overrides: {
576
- "ignored.txt": "ignore",
577
- },
578
- repoPath: "bundle",
579
- },
580
- ],
581
- },
582
- {
583
- HOME: testHomeDirectory,
584
- },
585
- );
586
-
587
- expect(resolveSyncMode(config, "elsewhere/file.txt")).toBeUndefined();
588
- expect(isSecretSyncPath(config, "bundle/token.txt")).toBe(true);
589
- expect(isIgnoredSyncPath(config, "bundle/ignored.txt")).toBe(true);
590
- expect(isSecretSyncPath(config, "elsewhere/file.txt")).toBe(false);
591
- expect(isIgnoredSyncPath(config, "elsewhere/file.txt")).toBe(false);
592
- });
593
-
594
- it("wraps malformed JSON when reading a sync config file", async () => {
595
- const syncDirectory = await createTemporaryDirectory(
596
- "devsync-sync-config-",
597
- );
598
-
599
- temporaryDirectories.push(syncDirectory);
600
-
601
- await writeFile(join(syncDirectory, "config.json"), "{\n", "utf8");
602
-
603
- await expect(
604
- readSyncConfig(syncDirectory, {
605
- HOME: testHomeDirectory,
606
- }),
607
- ).rejects.toThrowError(DevsyncError);
608
- });
609
- });