@tinyrack/devsync 1.1.0 → 1.2.2

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 (216) 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/sync.d.ts +96 -0
  55. package/dist/config/sync.d.ts.map +1 -0
  56. package/dist/config/sync.js +412 -0
  57. package/dist/config/sync.js.map +1 -0
  58. package/dist/config/xdg.d.ts +11 -0
  59. package/dist/config/xdg.d.ts.map +1 -0
  60. package/dist/config/xdg.js +79 -0
  61. package/dist/config/xdg.js.map +1 -0
  62. package/dist/index.d.ts +3 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/{src/index.ts → dist/index.js} +1 -1
  65. package/dist/index.js.map +1 -0
  66. package/dist/lib/file-mode.d.ts +3 -0
  67. package/dist/lib/file-mode.d.ts.map +1 -0
  68. package/dist/lib/file-mode.js +7 -0
  69. package/dist/lib/file-mode.js.map +1 -0
  70. package/dist/lib/output.d.ts +31 -0
  71. package/dist/lib/output.d.ts.map +1 -0
  72. package/dist/lib/output.js +198 -0
  73. package/dist/lib/output.js.map +1 -0
  74. package/dist/lib/path.d.ts +5 -0
  75. package/dist/lib/path.d.ts.map +1 -0
  76. package/dist/lib/path.js +25 -0
  77. package/dist/lib/path.js.map +1 -0
  78. package/dist/lib/string.d.ts +2 -0
  79. package/dist/lib/string.d.ts.map +1 -0
  80. package/dist/lib/string.js +4 -0
  81. package/dist/lib/string.js.map +1 -0
  82. package/dist/lib/validation.d.ts +3 -0
  83. package/dist/lib/validation.d.ts.map +1 -0
  84. package/dist/lib/validation.js +9 -0
  85. package/dist/lib/validation.js.map +1 -0
  86. package/dist/services/add.d.ts +20 -0
  87. package/dist/services/add.d.ts.map +1 -0
  88. package/dist/services/add.js +161 -0
  89. package/dist/services/add.js.map +1 -0
  90. package/dist/services/config-file.d.ts +30 -0
  91. package/dist/services/config-file.d.ts.map +1 -0
  92. package/dist/services/config-file.js +34 -0
  93. package/dist/services/config-file.js.map +1 -0
  94. package/dist/services/crypto.d.ts +9 -0
  95. package/dist/services/crypto.d.ts.map +1 -0
  96. package/dist/services/crypto.js +75 -0
  97. package/dist/services/crypto.js.map +1 -0
  98. package/dist/services/doctor.d.ts +16 -0
  99. package/dist/services/doctor.d.ts.map +1 -0
  100. package/dist/services/doctor.js +84 -0
  101. package/dist/services/doctor.js.map +1 -0
  102. package/dist/services/error.d.ts +14 -0
  103. package/dist/services/error.d.ts.map +1 -0
  104. package/dist/services/error.js +38 -0
  105. package/dist/services/error.js.map +1 -0
  106. package/dist/services/filesystem.d.ts +15 -0
  107. package/dist/services/filesystem.d.ts.map +1 -0
  108. package/dist/services/filesystem.js +113 -0
  109. package/dist/services/filesystem.js.map +1 -0
  110. package/dist/services/forget.d.ts +14 -0
  111. package/dist/services/forget.d.ts.map +1 -0
  112. package/dist/services/forget.js +124 -0
  113. package/dist/services/forget.js.map +1 -0
  114. package/dist/services/git.d.ts +10 -0
  115. package/dist/services/git.d.ts.map +1 -0
  116. package/dist/services/git.js +57 -0
  117. package/dist/services/git.js.map +1 -0
  118. package/dist/services/init.d.ts +19 -0
  119. package/dist/services/init.d.ts.map +1 -0
  120. package/dist/services/init.js +203 -0
  121. package/dist/services/init.js.map +1 -0
  122. package/dist/services/local-materialization.d.ts +28 -0
  123. package/dist/services/local-materialization.d.ts.map +1 -0
  124. package/dist/services/local-materialization.js +262 -0
  125. package/dist/services/local-materialization.js.map +1 -0
  126. package/dist/services/local-snapshot.d.ts +25 -0
  127. package/dist/services/local-snapshot.d.ts.map +1 -0
  128. package/dist/services/local-snapshot.js +93 -0
  129. package/dist/services/local-snapshot.js.map +1 -0
  130. package/dist/services/machine.d.ts +40 -0
  131. package/dist/services/machine.d.ts.map +1 -0
  132. package/dist/services/machine.js +113 -0
  133. package/dist/services/machine.js.map +1 -0
  134. package/dist/services/paths.d.ts +12 -0
  135. package/dist/services/paths.d.ts.map +1 -0
  136. package/dist/services/paths.js +71 -0
  137. package/dist/services/paths.js.map +1 -0
  138. package/dist/services/pull.d.ts +28 -0
  139. package/dist/services/pull.d.ts.map +1 -0
  140. package/dist/services/pull.js +67 -0
  141. package/dist/services/pull.js.map +1 -0
  142. package/dist/services/push.d.ts +35 -0
  143. package/dist/services/push.d.ts.map +1 -0
  144. package/dist/services/push.js +96 -0
  145. package/dist/services/push.js.map +1 -0
  146. package/dist/services/repo-artifacts.d.ts +52 -0
  147. package/dist/services/repo-artifacts.d.ts.map +1 -0
  148. package/dist/services/repo-artifacts.js +251 -0
  149. package/dist/services/repo-artifacts.js.map +1 -0
  150. package/dist/services/repo-snapshot.d.ts +6 -0
  151. package/dist/services/repo-snapshot.d.ts.map +1 -0
  152. package/dist/services/repo-snapshot.js +163 -0
  153. package/dist/services/repo-snapshot.js.map +1 -0
  154. package/dist/services/runtime.d.ts +40 -0
  155. package/dist/services/runtime.d.ts.map +1 -0
  156. package/dist/services/runtime.js +71 -0
  157. package/dist/services/runtime.js.map +1 -0
  158. package/dist/services/set.d.ts +38 -0
  159. package/dist/services/set.d.ts.map +1 -0
  160. package/dist/services/set.js +184 -0
  161. package/dist/services/set.js.map +1 -0
  162. package/dist/services/status.d.ts +30 -0
  163. package/dist/services/status.d.ts.map +1 -0
  164. package/dist/services/status.js +35 -0
  165. package/dist/services/status.js.map +1 -0
  166. package/package.json +15 -7
  167. package/src/cli/commands/add.ts +0 -40
  168. package/src/cli/commands/cd.ts +0 -80
  169. package/src/cli/commands/doctor.ts +0 -20
  170. package/src/cli/commands/forget.ts +0 -32
  171. package/src/cli/commands/index.ts +0 -23
  172. package/src/cli/commands/init.ts +0 -43
  173. package/src/cli/commands/list.ts +0 -17
  174. package/src/cli/commands/pull.ts +0 -31
  175. package/src/cli/commands/push.ts +0 -31
  176. package/src/cli/commands/set.ts +0 -47
  177. package/src/cli/commands/status.ts +0 -18
  178. package/src/cli/sync-output.test.ts +0 -173
  179. package/src/cli/sync-output.ts +0 -200
  180. package/src/config/sync.test.ts +0 -609
  181. package/src/config/sync.ts +0 -572
  182. package/src/config/xdg.ts +0 -138
  183. package/src/lib/string.test.ts +0 -13
  184. package/src/lib/string.ts +0 -3
  185. package/src/lib/validation.test.ts +0 -32
  186. package/src/lib/validation.ts +0 -11
  187. package/src/services/add.ts +0 -178
  188. package/src/services/config-file.test.ts +0 -161
  189. package/src/services/config-file.ts +0 -101
  190. package/src/services/crypto.test.ts +0 -132
  191. package/src/services/crypto.ts +0 -83
  192. package/src/services/doctor.ts +0 -142
  193. package/src/services/error.ts +0 -6
  194. package/src/services/filesystem.test.ts +0 -171
  195. package/src/services/filesystem.ts +0 -183
  196. package/src/services/forget.ts +0 -261
  197. package/src/services/git.test.ts +0 -83
  198. package/src/services/git.ts +0 -74
  199. package/src/services/init.test.ts +0 -109
  200. package/src/services/init.ts +0 -244
  201. package/src/services/list.ts +0 -63
  202. package/src/services/local-materialization.ts +0 -421
  203. package/src/services/local-snapshot.ts +0 -173
  204. package/src/services/paths.test.ts +0 -74
  205. package/src/services/paths.ts +0 -98
  206. package/src/services/pull.ts +0 -144
  207. package/src/services/push.ts +0 -168
  208. package/src/services/repo-artifacts.ts +0 -262
  209. package/src/services/repo-snapshot.ts +0 -197
  210. package/src/services/runtime.ts +0 -57
  211. package/src/services/set.ts +0 -383
  212. package/src/services/status.ts +0 -57
  213. package/src/services/sync.dry-run.test.ts +0 -179
  214. package/src/services/sync.runtime.test.ts +0 -756
  215. package/src/services/sync.service.test.ts +0 -1169
  216. package/src/test/helpers/sync-fixture.ts +0 -47
@@ -1,262 +0,0 @@
1
- import { lstat, mkdir } from "node:fs/promises";
2
- import { join } from "node:path";
3
-
4
- import {
5
- hasReservedSyncArtifactSuffixSegment,
6
- type ResolvedSyncConfig,
7
- resolveSyncArtifactsDirectoryPath,
8
- syncSecretArtifactSuffix,
9
- } from "#app/config/sync.ts";
10
-
11
- import { encryptSecretFile } from "./crypto.ts";
12
- import { DevsyncError } from "./error.ts";
13
- import {
14
- getPathStats,
15
- listDirectoryEntries,
16
- pathExists,
17
- writeFileNode,
18
- writeSymlinkNode,
19
- } from "./filesystem.ts";
20
- import type { SnapshotNode } from "./local-snapshot.ts";
21
- import { buildDirectoryKey } from "./paths.ts";
22
-
23
- export type RepoArtifact =
24
- | Readonly<{
25
- category: "plain";
26
- kind: "directory";
27
- repoPath: string;
28
- }>
29
- | Readonly<{
30
- category: "plain";
31
- kind: "file";
32
- repoPath: string;
33
- contents: Uint8Array;
34
- executable: boolean;
35
- }>
36
- | Readonly<{
37
- category: "plain";
38
- kind: "symlink";
39
- repoPath: string;
40
- linkTarget: string;
41
- }>
42
- | Readonly<{
43
- category: "secret";
44
- kind: "file";
45
- repoPath: string;
46
- contents: string;
47
- executable: boolean;
48
- }>;
49
-
50
- export const buildArtifactKey = (artifact: RepoArtifact) => {
51
- const relativePath = resolveArtifactRelativePath(artifact);
52
-
53
- return artifact.kind === "directory"
54
- ? buildDirectoryKey(relativePath)
55
- : relativePath;
56
- };
57
-
58
- export const isSecretArtifactPath = (relativePath: string) => {
59
- return relativePath.endsWith(syncSecretArtifactSuffix);
60
- };
61
-
62
- export const stripSecretArtifactSuffix = (relativePath: string) => {
63
- if (!isSecretArtifactPath(relativePath)) {
64
- return undefined;
65
- }
66
-
67
- return relativePath.slice(0, -syncSecretArtifactSuffix.length);
68
- };
69
-
70
- export const assertStorageSafeRepoPath = (repoPath: string) => {
71
- if (!hasReservedSyncArtifactSuffixSegment(repoPath)) {
72
- return;
73
- }
74
-
75
- throw new DevsyncError(
76
- `Tracked sync paths must not use the reserved suffix ${syncSecretArtifactSuffix}: ${repoPath}`,
77
- );
78
- };
79
-
80
- export const resolveArtifactRelativePath = (
81
- artifact: Pick<RepoArtifact, "category" | "repoPath">,
82
- ) => {
83
- return artifact.category === "secret"
84
- ? `${artifact.repoPath}${syncSecretArtifactSuffix}`
85
- : artifact.repoPath;
86
- };
87
-
88
- export const buildRepoArtifacts = async (
89
- snapshot: ReadonlyMap<string, SnapshotNode>,
90
- config: ResolvedSyncConfig,
91
- ) => {
92
- const artifacts: RepoArtifact[] = [];
93
- const seenArtifactKeys = new Set<string>();
94
-
95
- for (const repoPath of [...snapshot.keys()].sort((left, right) => {
96
- return left.localeCompare(right);
97
- })) {
98
- assertStorageSafeRepoPath(repoPath);
99
- const node = snapshot.get(repoPath);
100
-
101
- if (node === undefined) {
102
- continue;
103
- }
104
-
105
- if (node.type === "directory") {
106
- const artifact = {
107
- category: "plain",
108
- kind: "directory",
109
- repoPath,
110
- } satisfies RepoArtifact;
111
- const key = buildArtifactKey(artifact);
112
-
113
- if (seenArtifactKeys.has(key)) {
114
- throw new DevsyncError(
115
- `Duplicate repository artifact generated for ${key}`,
116
- );
117
- }
118
-
119
- seenArtifactKeys.add(key);
120
- artifacts.push(artifact);
121
- continue;
122
- }
123
-
124
- if (node.type === "symlink") {
125
- const artifact = {
126
- category: "plain",
127
- kind: "symlink",
128
- linkTarget: node.linkTarget,
129
- repoPath,
130
- } satisfies RepoArtifact;
131
- const key = buildArtifactKey(artifact);
132
-
133
- if (seenArtifactKeys.has(key)) {
134
- throw new DevsyncError(
135
- `Duplicate repository artifact generated for ${key}`,
136
- );
137
- }
138
-
139
- seenArtifactKeys.add(key);
140
- artifacts.push(artifact);
141
- continue;
142
- }
143
-
144
- if (!node.secret) {
145
- const artifact = {
146
- category: "plain",
147
- contents: node.contents,
148
- executable: node.executable,
149
- kind: "file",
150
- repoPath,
151
- } satisfies RepoArtifact;
152
- const key = buildArtifactKey(artifact);
153
-
154
- if (seenArtifactKeys.has(key)) {
155
- throw new DevsyncError(
156
- `Duplicate repository artifact generated for ${key}`,
157
- );
158
- }
159
-
160
- seenArtifactKeys.add(key);
161
- artifacts.push(artifact);
162
- continue;
163
- }
164
-
165
- const artifact = {
166
- category: "secret",
167
- contents: await encryptSecretFile(node.contents, config.age.recipients),
168
- executable: node.executable,
169
- kind: "file",
170
- repoPath,
171
- } satisfies RepoArtifact;
172
- const key = buildArtifactKey(artifact);
173
-
174
- if (seenArtifactKeys.has(key)) {
175
- throw new DevsyncError(
176
- `Duplicate repository artifact generated for ${key}`,
177
- );
178
- }
179
-
180
- seenArtifactKeys.add(key);
181
- artifacts.push(artifact);
182
- }
183
-
184
- return artifacts;
185
- };
186
-
187
- const collectArtifactLeafKeys = async (
188
- rootDirectory: string,
189
- keys: Set<string>,
190
- prefix?: string,
191
- ) => {
192
- if (!(await pathExists(rootDirectory))) {
193
- return;
194
- }
195
-
196
- const entries = await listDirectoryEntries(rootDirectory);
197
-
198
- for (const entry of entries) {
199
- const absolutePath = join(rootDirectory, entry.name);
200
- const relativePath =
201
- prefix === undefined ? entry.name : `${prefix}/${entry.name}`;
202
- const stats = await lstat(absolutePath);
203
-
204
- if (stats?.isDirectory()) {
205
- await collectArtifactLeafKeys(absolutePath, keys, relativePath);
206
- continue;
207
- }
208
-
209
- keys.add(relativePath);
210
- }
211
- };
212
-
213
- export const collectExistingArtifactKeys = async (
214
- syncDirectory: string,
215
- config: ResolvedSyncConfig,
216
- ) => {
217
- const keys = new Set<string>();
218
- const artifactsDirectory = resolveSyncArtifactsDirectoryPath(syncDirectory);
219
-
220
- await collectArtifactLeafKeys(artifactsDirectory, keys);
221
-
222
- for (const entry of config.entries) {
223
- if (entry.kind !== "directory") {
224
- continue;
225
- }
226
-
227
- const path = join(artifactsDirectory, ...entry.repoPath.split("/"));
228
- const stats = await getPathStats(path);
229
-
230
- if (stats?.isDirectory()) {
231
- keys.add(buildDirectoryKey(entry.repoPath));
232
- }
233
- }
234
-
235
- return keys;
236
- };
237
-
238
- export const writeArtifactsToDirectory = async (
239
- rootDirectory: string,
240
- artifacts: readonly RepoArtifact[],
241
- ) => {
242
- await mkdir(rootDirectory, { recursive: true });
243
-
244
- for (const artifact of artifacts) {
245
- const artifactPath = join(
246
- rootDirectory,
247
- ...resolveArtifactRelativePath(artifact).split("/"),
248
- );
249
-
250
- if (artifact.kind === "directory") {
251
- await mkdir(artifactPath, { recursive: true });
252
- continue;
253
- }
254
-
255
- if (artifact.kind === "symlink") {
256
- await writeSymlinkNode(artifactPath, artifact.linkTarget);
257
- continue;
258
- }
259
-
260
- await writeFileNode(artifactPath, artifact);
261
- }
262
- };
@@ -1,197 +0,0 @@
1
- import { lstat, readFile, readlink } from "node:fs/promises";
2
- import { join } from "node:path";
3
-
4
- import {
5
- findOwningSyncEntry,
6
- type ResolvedSyncConfig,
7
- resolveManagedSyncMode,
8
- resolveSyncArtifactsDirectoryPath,
9
- } from "#app/config/sync.ts";
10
-
11
- import { decryptSecretFile } from "./crypto.ts";
12
- import { DevsyncError } from "./error.ts";
13
- import {
14
- getPathStats,
15
- isExecutableMode,
16
- listDirectoryEntries,
17
- pathExists,
18
- } from "./filesystem.ts";
19
- import { addSnapshotNode, type SnapshotNode } from "./local-snapshot.ts";
20
- import {
21
- assertStorageSafeRepoPath,
22
- isSecretArtifactPath,
23
- stripSecretArtifactSuffix,
24
- } from "./repo-artifacts.ts";
25
-
26
- const readPlainSnapshotNode = async (
27
- absolutePath: string,
28
- repoPath: string,
29
- config: ResolvedSyncConfig,
30
- snapshot: Map<string, SnapshotNode>,
31
- ) => {
32
- assertStorageSafeRepoPath(repoPath);
33
- const mode = resolveManagedSyncMode(config, repoPath);
34
-
35
- if (mode === "ignore") {
36
- return;
37
- }
38
-
39
- if (findOwningSyncEntry(config, repoPath) === undefined) {
40
- throw new DevsyncError(
41
- `Unmanaged plain sync path found in repository: ${repoPath}`,
42
- );
43
- }
44
-
45
- if (mode === "secret") {
46
- throw new DevsyncError(
47
- `Secret sync path is stored in plain text in the repository: ${repoPath}`,
48
- );
49
- }
50
-
51
- const stats = await lstat(absolutePath);
52
-
53
- if (stats.isSymbolicLink()) {
54
- addSnapshotNode(snapshot, repoPath, {
55
- linkTarget: await readlink(absolutePath),
56
- type: "symlink",
57
- });
58
-
59
- return;
60
- }
61
-
62
- if (!stats.isFile()) {
63
- throw new DevsyncError(
64
- `Unsupported plain repository entry: ${absolutePath}`,
65
- );
66
- }
67
-
68
- addSnapshotNode(snapshot, repoPath, {
69
- contents: await readFile(absolutePath),
70
- executable: isExecutableMode(stats.mode),
71
- secret: false,
72
- type: "file",
73
- });
74
- };
75
-
76
- const readRepositoryTree = async (
77
- rootDirectory: string,
78
- config: ResolvedSyncConfig,
79
- snapshot: Map<string, SnapshotNode>,
80
- prefix?: string,
81
- ) => {
82
- if (!(await pathExists(rootDirectory))) {
83
- return;
84
- }
85
-
86
- const entries = await listDirectoryEntries(rootDirectory);
87
-
88
- for (const entry of entries) {
89
- const absolutePath = join(rootDirectory, entry.name);
90
- const relativePath =
91
- prefix === undefined ? entry.name : `${prefix}/${entry.name}`;
92
- const stats = await lstat(absolutePath);
93
-
94
- if (stats.isDirectory()) {
95
- assertStorageSafeRepoPath(relativePath);
96
- await readRepositoryTree(absolutePath, config, snapshot, relativePath);
97
- continue;
98
- }
99
-
100
- if (stats.isSymbolicLink()) {
101
- if (isSecretArtifactPath(relativePath)) {
102
- throw new DevsyncError(
103
- `Secret repository entries must be regular files, not symlinks: ${relativePath}`,
104
- );
105
- }
106
-
107
- await readPlainSnapshotNode(absolutePath, relativePath, config, snapshot);
108
- continue;
109
- }
110
-
111
- if (isSecretArtifactPath(relativePath)) {
112
- const repoPath = stripSecretArtifactSuffix(relativePath);
113
-
114
- if (repoPath === undefined || repoPath.length === 0) {
115
- throw new DevsyncError(
116
- `Secret repository files must include a path before ${relativePath}`,
117
- );
118
- }
119
-
120
- assertStorageSafeRepoPath(repoPath);
121
- const mode = resolveManagedSyncMode(config, repoPath);
122
-
123
- if (findOwningSyncEntry(config, repoPath) === undefined) {
124
- throw new DevsyncError(
125
- `Unmanaged secret sync path found in repository: ${repoPath}`,
126
- );
127
- }
128
-
129
- if (mode === "ignore") {
130
- continue;
131
- }
132
-
133
- if (mode !== "secret") {
134
- throw new DevsyncError(
135
- `Plain sync path is stored in secret form in the repository: ${repoPath}`,
136
- );
137
- }
138
-
139
- addSnapshotNode(snapshot, repoPath, {
140
- contents: await decryptSecretFile(
141
- await readFile(absolutePath, "utf8"),
142
- config.age.identityFile,
143
- ),
144
- executable: isExecutableMode(stats.mode),
145
- secret: true,
146
- type: "file",
147
- });
148
- continue;
149
- }
150
-
151
- if (!stats.isFile()) {
152
- throw new DevsyncError(
153
- `Unsupported plain repository entry: ${absolutePath}`,
154
- );
155
- }
156
-
157
- await readPlainSnapshotNode(absolutePath, relativePath, config, snapshot);
158
- }
159
- };
160
-
161
- export const buildRepositorySnapshot = async (
162
- syncDirectory: string,
163
- config: ResolvedSyncConfig,
164
- ) => {
165
- const snapshot = new Map<string, SnapshotNode>();
166
- const artifactsDirectory = resolveSyncArtifactsDirectoryPath(syncDirectory);
167
-
168
- await readRepositoryTree(artifactsDirectory, config, snapshot);
169
-
170
- for (const entry of config.entries) {
171
- if (entry.kind !== "directory") {
172
- continue;
173
- }
174
-
175
- const artifactPath = join(artifactsDirectory, ...entry.repoPath.split("/"));
176
- const stats = await getPathStats(artifactPath);
177
-
178
- if (stats !== undefined && !stats.isDirectory()) {
179
- throw new DevsyncError(
180
- `Directory sync entry is not stored as a directory in the repository: ${entry.repoPath}`,
181
- );
182
- }
183
-
184
- const mode = resolveManagedSyncMode(config, entry.repoPath);
185
- const hasTrackedChildren = [...snapshot.keys()].some((repoPath) => {
186
- return repoPath.startsWith(`${entry.repoPath}/`);
187
- });
188
-
189
- if (stats?.isDirectory() && (mode !== "ignore" || hasTrackedChildren)) {
190
- addSnapshotNode(snapshot, entry.repoPath, {
191
- type: "directory",
192
- });
193
- }
194
- }
195
-
196
- return snapshot;
197
- };
@@ -1,57 +0,0 @@
1
- import {
2
- resolveSyncArtifactsDirectoryPath,
3
- resolveSyncConfigFilePath,
4
- } from "#app/config/sync.ts";
5
- import {
6
- resolveDevsyncSyncDirectory,
7
- resolveHomeDirectory,
8
- } from "#app/config/xdg.ts";
9
-
10
- import { ensureGitRepository } from "./git.ts";
11
-
12
- export type SyncPaths = Readonly<{
13
- artifactsDirectory: string;
14
- configPath: string;
15
- homeDirectory: string;
16
- syncDirectory: string;
17
- }>;
18
-
19
- export type SyncContext = Readonly<{
20
- cwd: string;
21
- environment: NodeJS.ProcessEnv;
22
- paths: SyncPaths;
23
- }>;
24
-
25
- export const createSyncPaths = (
26
- environment: NodeJS.ProcessEnv = process.env,
27
- ): SyncPaths => {
28
- const syncDirectory = resolveDevsyncSyncDirectory(environment);
29
-
30
- return {
31
- artifactsDirectory: resolveSyncArtifactsDirectoryPath(syncDirectory),
32
- configPath: resolveSyncConfigFilePath(syncDirectory),
33
- homeDirectory: resolveHomeDirectory(environment),
34
- syncDirectory,
35
- };
36
- };
37
-
38
- export const createSyncContext = (
39
- options: Readonly<{
40
- cwd?: string;
41
- environment?: NodeJS.ProcessEnv;
42
- }> = {},
43
- ): SyncContext => {
44
- const environment = options.environment ?? process.env;
45
-
46
- return {
47
- cwd: options.cwd ?? process.cwd(),
48
- environment,
49
- paths: createSyncPaths(environment),
50
- };
51
- };
52
-
53
- export const ensureSyncRepository = async (
54
- context: Pick<SyncContext, "paths">,
55
- ) => {
56
- await ensureGitRepository(context.paths.syncDirectory);
57
- };