@forgecart/cli 2.202610102045.0 → 2.202610110825.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.
- package/dist/src/authorization/cli-unauthorized-refusal.d.ts +10 -4
- package/dist/src/authorization/cli-unauthorized-refusal.js +11 -3
- package/dist/src/authorization/cli-unauthorized-refusal.js.map +1 -1
- package/dist/src/cli.js +4 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/init.js +5 -47
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/sync.d.ts +14 -0
- package/dist/src/commands/sync.js +49 -0
- package/dist/src/commands/sync.js.map +1 -0
- package/dist/src/exit-ladder.d.ts +8 -1
- package/dist/src/exit-ladder.js +7 -0
- package/dist/src/exit-ladder.js.map +1 -1
- package/dist/src/sync/sync-api.error.d.ts +15 -0
- package/dist/src/sync/sync-api.error.js +26 -0
- package/dist/src/sync/sync-api.error.js.map +1 -0
- package/dist/src/sync/sync-divergence.d.ts +43 -0
- package/dist/src/sync/sync-divergence.js +104 -0
- package/dist/src/sync/sync-divergence.js.map +1 -0
- package/dist/src/sync/sync-dotenv.file.d.ts +36 -0
- package/dist/src/sync/sync-dotenv.file.js +95 -0
- package/dist/src/sync/sync-dotenv.file.js.map +1 -0
- package/dist/src/sync/sync-env.runner.d.ts +29 -0
- package/dist/src/sync/sync-env.runner.js +95 -0
- package/dist/src/sync/sync-env.runner.js.map +1 -0
- package/dist/src/sync/sync-record.file.d.ts +28 -0
- package/dist/src/sync/sync-record.file.js +86 -0
- package/dist/src/sync/sync-record.file.js.map +1 -0
- package/dist/src/sync/sync-repository.d.ts +110 -0
- package/dist/src/sync/sync-repository.js +396 -0
- package/dist/src/sync/sync-repository.js.map +1 -0
- package/dist/src/sync/sync-round.d.ts +95 -0
- package/dist/src/sync/sync-round.js +355 -0
- package/dist/src/sync/sync-round.js.map +1 -0
- package/dist/src/sync/sync-watch.d.ts +48 -0
- package/dist/src/sync/sync-watch.js +144 -0
- package/dist/src/sync/sync-watch.js.map +1 -0
- package/dist/src/sync/sync.runner.d.ts +37 -0
- package/dist/src/sync/sync.runner.js +121 -0
- package/dist/src/sync/sync.runner.js.map +1 -0
- package/dist/src/sync/type/sync.types.d.ts +159 -0
- package/dist/src/sync/type/sync.types.js +30 -0
- package/dist/src/sync/type/sync.types.js.map +1 -0
- package/dist/src/sync/workspace-sync.client.d.ts +85 -0
- package/dist/src/sync/workspace-sync.client.js +317 -0
- package/dist/src/sync/workspace-sync.client.js.map +1 -0
- package/dist/src/util/dotenv.util.d.ts +28 -0
- package/dist/src/util/dotenv.util.js +81 -0
- package/dist/src/util/dotenv.util.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { CliUnauthorizedReason, CliUnauthorizedRefusal, } from '../authorization/cli-unauthorized-refusal.js';
|
|
3
|
+
import { isJsonObject } from '../json.js';
|
|
4
|
+
import { SyncApiError } from './sync-api.error.js';
|
|
5
|
+
/** The SDK's `WorkspaceSyncRetrieve` operation, verbatim. */
|
|
6
|
+
const STATE_DOCUMENT = `query WorkspaceSyncRetrieve {
|
|
7
|
+
workspaceSyncState {
|
|
8
|
+
id
|
|
9
|
+
servingSha
|
|
10
|
+
}
|
|
11
|
+
}`;
|
|
12
|
+
/** The SDK's `WorkspaceSyncPull` operation, verbatim. */
|
|
13
|
+
const BUNDLE_DOCUMENT = `query WorkspaceSyncPull($input: WorkspaceSyncBundleInput!) {
|
|
14
|
+
workspaceSyncBundle(input: $input) {
|
|
15
|
+
id
|
|
16
|
+
sinceSha
|
|
17
|
+
byteLength
|
|
18
|
+
sha256
|
|
19
|
+
data
|
|
20
|
+
envLines
|
|
21
|
+
}
|
|
22
|
+
}`;
|
|
23
|
+
/** The SDK's `WorkspaceSyncPush` operation, verbatim. */
|
|
24
|
+
const PUSH_DOCUMENT = `mutation WorkspaceSyncPush($input: PushWorkspaceSyncInput!) {
|
|
25
|
+
pushWorkspaceSync(input: $input) {
|
|
26
|
+
id
|
|
27
|
+
accepted
|
|
28
|
+
servingSha
|
|
29
|
+
gateMessages
|
|
30
|
+
runtimeError
|
|
31
|
+
seoFindings
|
|
32
|
+
envKeys
|
|
33
|
+
}
|
|
34
|
+
}`;
|
|
35
|
+
/** The codes the admin API refuses an admin operation's credentials with. */
|
|
36
|
+
const CREDENTIAL_REFUSALS = new Set(['UNAUTHORIZED', 'FORBIDDEN']);
|
|
37
|
+
/** The multipart part a push's bundle travels in, and the variable it answers (GraphQL multipart request spec). */
|
|
38
|
+
const BUNDLE_PART = '0';
|
|
39
|
+
const BUNDLE_VARIABLE = 'variables.input.bundle';
|
|
40
|
+
const BUNDLE_FILE_NAME = 'serving.bundle';
|
|
41
|
+
/** Milliseconds in a second — `--timeout` is given in seconds. */
|
|
42
|
+
const MS_PER_SECOND = 1000;
|
|
43
|
+
/**
|
|
44
|
+
* The admin API's three workspace-sync operations (#2781), over GraphQL on
|
|
45
|
+
* HTTP with the channel token and the admin secret as the `forgecart-token` and
|
|
46
|
+
* `forgecart-secret` headers — the one surface `forgecart sync` reaches the
|
|
47
|
+
* shop through. Every request goes to FORGECART_ADMIN_API_URL as given, and
|
|
48
|
+
* every exchange is bounded by `--timeout` and ends when the run is
|
|
49
|
+
* interrupted. Answers are read field by field: one the CLI cannot read is an
|
|
50
|
+
* error naming the endpoint, never a guess. Credentials the API refuses end the
|
|
51
|
+
* run on exit 3; any other refusal is a {@link SyncApiError} carrying its code.
|
|
52
|
+
*/
|
|
53
|
+
export class WorkspaceSyncClient {
|
|
54
|
+
credentials;
|
|
55
|
+
timeoutSeconds;
|
|
56
|
+
interrupt;
|
|
57
|
+
/**
|
|
58
|
+
* @param credentials the four credentials the run settled.
|
|
59
|
+
* @param timeoutSeconds the bound on one exchange — `--timeout`.
|
|
60
|
+
* @param interrupt ends every exchange in flight when the run is interrupted.
|
|
61
|
+
*/
|
|
62
|
+
constructor(credentials, timeoutSeconds, interrupt) {
|
|
63
|
+
this.credentials = credentials;
|
|
64
|
+
this.timeoutSeconds = timeoutSeconds;
|
|
65
|
+
this.interrupt = interrupt;
|
|
66
|
+
}
|
|
67
|
+
/** The board's serving commit, read from the pod. */
|
|
68
|
+
async state() {
|
|
69
|
+
const field = 'workspaceSyncState';
|
|
70
|
+
const result = await this.exchange(this.jsonRequest(STATE_DOCUMENT, {}), field);
|
|
71
|
+
return { servingSha: this.readString(result, 'servingSha', field) };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The serving branch since `sinceSha` — all of it for `''` — as ONE git
|
|
75
|
+
* bundle, checked against its declared byte length and sha256 before anything
|
|
76
|
+
* reads it: a short or mismatched bundle is an error, and nothing is written.
|
|
77
|
+
*/
|
|
78
|
+
async bundle(sinceSha) {
|
|
79
|
+
const field = 'workspaceSyncBundle';
|
|
80
|
+
const result = await this.exchange(this.jsonRequest(BUNDLE_DOCUMENT, { input: { sinceSha } }), field);
|
|
81
|
+
const bytes = Buffer.from(this.readString(result, 'data', field), 'base64');
|
|
82
|
+
this.assertDeclared(bytes, this.readCount(result, 'byteLength', field), this.readString(result, 'sha256', field));
|
|
83
|
+
return {
|
|
84
|
+
sinceSha: this.readText(result, 'sinceSha', field),
|
|
85
|
+
bytes,
|
|
86
|
+
envLines: this.readStringList(result, 'envLines', field),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Push `bundle` — the laptop's tree as one commit cut on the serving tip — as
|
|
91
|
+
* an authenticated upload, with the merchant `.env` lines `envLines`; a push
|
|
92
|
+
* without a bundle carries the lines alone.
|
|
93
|
+
*/
|
|
94
|
+
async push(bundle, envLines) {
|
|
95
|
+
const field = 'pushWorkspaceSync';
|
|
96
|
+
const result = await this.exchange(this.pushRequest(bundle, envLines), field);
|
|
97
|
+
return {
|
|
98
|
+
accepted: this.readBoolean(result, 'accepted', field),
|
|
99
|
+
servingSha: this.readNullableString(result, 'servingSha', field),
|
|
100
|
+
gateMessages: this.readStringList(result, 'gateMessages', field),
|
|
101
|
+
runtimeError: this.readNullableString(result, 'runtimeError', field),
|
|
102
|
+
seoFindings: this.readStringList(result, 'seoFindings', field),
|
|
103
|
+
envKeys: this.readCount(result, 'envKeys', field),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/** A push with a bundle is an upload; one with `.env` lines alone is plain JSON. */
|
|
107
|
+
pushRequest(bundle, envLines) {
|
|
108
|
+
if (bundle === null)
|
|
109
|
+
return this.jsonRequest(PUSH_DOCUMENT, { input: { envLines } });
|
|
110
|
+
return this.uploadRequest(bundle, envLines);
|
|
111
|
+
}
|
|
112
|
+
/** A GraphQL request as JSON. */
|
|
113
|
+
jsonRequest(document, variables) {
|
|
114
|
+
return {
|
|
115
|
+
body: JSON.stringify({ query: document, variables }),
|
|
116
|
+
headers: { ...this.authHeaders(), 'content-type': 'application/json' },
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The push as a GraphQL multipart request: the operation, the map naming the
|
|
121
|
+
* bundle's variable, and the bundle itself, declared by its byte length and
|
|
122
|
+
* sha256. The admin API reads a multipart request only with the preflight
|
|
123
|
+
* header; the boundary is the form's own.
|
|
124
|
+
*/
|
|
125
|
+
uploadRequest(bundle, envLines) {
|
|
126
|
+
const input = {
|
|
127
|
+
bundle: null,
|
|
128
|
+
byteLength: bundle.length,
|
|
129
|
+
sha256: createHash('sha256').update(bundle).digest('hex'),
|
|
130
|
+
envLines,
|
|
131
|
+
};
|
|
132
|
+
const form = new FormData();
|
|
133
|
+
form.append('operations', JSON.stringify({ query: PUSH_DOCUMENT, variables: { input } }));
|
|
134
|
+
form.append('map', JSON.stringify({ [BUNDLE_PART]: [BUNDLE_VARIABLE] }));
|
|
135
|
+
form.append(BUNDLE_PART, new Blob([new Uint8Array(bundle)]), BUNDLE_FILE_NAME);
|
|
136
|
+
return { body: form, headers: { ...this.authHeaders(), 'apollo-require-preflight': 'true' } };
|
|
137
|
+
}
|
|
138
|
+
authHeaders() {
|
|
139
|
+
return {
|
|
140
|
+
accept: 'application/json',
|
|
141
|
+
'forgecart-token': this.credentials.channelToken,
|
|
142
|
+
'forgecart-secret': this.credentials.adminSecret,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* POST one operation and answer its root field's object. The exchange — the
|
|
147
|
+
* response and its whole body — is bounded by `--timeout` and ends with the
|
|
148
|
+
* run's interrupt.
|
|
149
|
+
*/
|
|
150
|
+
async exchange(request, field) {
|
|
151
|
+
const bound = new AbortController();
|
|
152
|
+
const timer = setTimeout(() => {
|
|
153
|
+
bound.abort();
|
|
154
|
+
}, this.timeoutSeconds * MS_PER_SECOND);
|
|
155
|
+
const relay = () => {
|
|
156
|
+
bound.abort();
|
|
157
|
+
};
|
|
158
|
+
this.interrupt.addEventListener('abort', relay, { once: true });
|
|
159
|
+
try {
|
|
160
|
+
const { status, text } = await this.post(request, bound.signal, field);
|
|
161
|
+
return this.rootField(this.parseBody(text, status), field);
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
clearTimeout(timer);
|
|
165
|
+
this.interrupt.removeEventListener('abort', relay);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async post(request, signal, field) {
|
|
169
|
+
try {
|
|
170
|
+
const response = await fetch(this.credentials.adminApiUrl, {
|
|
171
|
+
method: 'POST',
|
|
172
|
+
headers: request.headers,
|
|
173
|
+
body: request.body,
|
|
174
|
+
signal,
|
|
175
|
+
});
|
|
176
|
+
return { status: response.status, text: await response.text() };
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
throw this.exchangeFailure(error, signal, field);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** Why an exchange ended without an answer: the run's interrupt, `--timeout`, or the network. */
|
|
183
|
+
exchangeFailure(error, signal, field) {
|
|
184
|
+
const endpoint = this.credentials.adminApiUrl;
|
|
185
|
+
if (this.interrupt.aborted) {
|
|
186
|
+
return new Error(`Interrupted while the admin API at ${endpoint} answered ${field}.`, {
|
|
187
|
+
cause: error,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (signal.aborted) {
|
|
191
|
+
return new Error(`The admin API at ${endpoint} did not answer ${field} within --timeout ${String(this.timeoutSeconds)} s.`, { cause: error });
|
|
192
|
+
}
|
|
193
|
+
return new Error(`Could not reach the admin API at ${endpoint}: ${this.failureDetail(error)}.`, {
|
|
194
|
+
cause: error,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
/** The response's GraphQL body — an answer that is not one names its HTTP status. */
|
|
198
|
+
parseBody(text, status) {
|
|
199
|
+
let parsed;
|
|
200
|
+
try {
|
|
201
|
+
parsed = JSON.parse(text);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
throw new Error(this.notGraphQlMessage(status), { cause: error });
|
|
205
|
+
}
|
|
206
|
+
if (!isJsonObject(parsed))
|
|
207
|
+
throw new Error(this.notGraphQlMessage(status));
|
|
208
|
+
return parsed;
|
|
209
|
+
}
|
|
210
|
+
/** The root field's object, once the body carries no error. */
|
|
211
|
+
rootField(body, field) {
|
|
212
|
+
this.assertNoErrors(body.errors, field);
|
|
213
|
+
const data = body.data;
|
|
214
|
+
const result = isJsonObject(data) ? data[field] : undefined;
|
|
215
|
+
if (!isJsonObject(result))
|
|
216
|
+
throw this.unreadable(field);
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* A GraphQL error ends the call: credentials refused as UNAUTHORIZED or
|
|
221
|
+
* FORBIDDEN on exit 3, any other code as the typed {@link SyncApiError}.
|
|
222
|
+
*/
|
|
223
|
+
assertNoErrors(errors, field) {
|
|
224
|
+
if (!Array.isArray(errors) || errors.length === 0)
|
|
225
|
+
return;
|
|
226
|
+
const first = errors[0];
|
|
227
|
+
const extensions = this.extensionsOf(first);
|
|
228
|
+
const code = typeof extensions.code === 'string' ? extensions.code : undefined;
|
|
229
|
+
if (code !== undefined && CREDENTIAL_REFUSALS.has(code)) {
|
|
230
|
+
throw new CliUnauthorizedRefusal(CliUnauthorizedReason.REFUSED);
|
|
231
|
+
}
|
|
232
|
+
throw new SyncApiError(this.credentials.adminApiUrl, field, code ?? this.messageOf(first), this.errorVariables(extensions.variables));
|
|
233
|
+
}
|
|
234
|
+
extensionsOf(error) {
|
|
235
|
+
return isJsonObject(error) && isJsonObject(error.extensions) ? error.extensions : {};
|
|
236
|
+
}
|
|
237
|
+
messageOf(error) {
|
|
238
|
+
return isJsonObject(error) && typeof error.message === 'string' ? error.message : '';
|
|
239
|
+
}
|
|
240
|
+
/** The variables a typed error carries, as text — none when it carries none. */
|
|
241
|
+
errorVariables(variables) {
|
|
242
|
+
if (!isJsonObject(variables))
|
|
243
|
+
return {};
|
|
244
|
+
return Object.fromEntries(Object.entries(variables).map(([key, value]) => [
|
|
245
|
+
key,
|
|
246
|
+
typeof value === 'string' ? value : JSON.stringify(value),
|
|
247
|
+
]));
|
|
248
|
+
}
|
|
249
|
+
/** A pulled bundle's bytes against what it declares: its length, then its sha256. */
|
|
250
|
+
assertDeclared(bytes, byteLength, sha256) {
|
|
251
|
+
if (bytes.length !== byteLength) {
|
|
252
|
+
throw new Error(`The pulled bundle is ${String(bytes.length)} bytes, not the ${String(byteLength)} it declares; nothing was written.`);
|
|
253
|
+
}
|
|
254
|
+
if (createHash('sha256').update(bytes).digest('hex') !== sha256) {
|
|
255
|
+
throw new Error('The pulled bundle does not hash to the sha256 it declares; nothing was written.');
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/** A string field that must carry text. */
|
|
259
|
+
readString(record, key, field) {
|
|
260
|
+
const value = record[key];
|
|
261
|
+
if (typeof value !== 'string' || value === '')
|
|
262
|
+
throw this.unreadable(field);
|
|
263
|
+
return value;
|
|
264
|
+
}
|
|
265
|
+
/** A string field that may be empty. */
|
|
266
|
+
readText(record, key, field) {
|
|
267
|
+
const value = record[key];
|
|
268
|
+
if (typeof value !== 'string')
|
|
269
|
+
throw this.unreadable(field);
|
|
270
|
+
return value;
|
|
271
|
+
}
|
|
272
|
+
readNullableString(record, key, field) {
|
|
273
|
+
const value = record[key];
|
|
274
|
+
if (value === null)
|
|
275
|
+
return null;
|
|
276
|
+
if (typeof value !== 'string')
|
|
277
|
+
throw this.unreadable(field);
|
|
278
|
+
return value;
|
|
279
|
+
}
|
|
280
|
+
readStringList(record, key, field) {
|
|
281
|
+
const value = record[key];
|
|
282
|
+
if (!Array.isArray(value))
|
|
283
|
+
throw this.unreadable(field);
|
|
284
|
+
const items = value;
|
|
285
|
+
if (!items.every((item) => typeof item === 'string')) {
|
|
286
|
+
throw this.unreadable(field);
|
|
287
|
+
}
|
|
288
|
+
return items;
|
|
289
|
+
}
|
|
290
|
+
readCount(record, key, field) {
|
|
291
|
+
const value = record[key];
|
|
292
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {
|
|
293
|
+
throw this.unreadable(field);
|
|
294
|
+
}
|
|
295
|
+
return value;
|
|
296
|
+
}
|
|
297
|
+
readBoolean(record, key, field) {
|
|
298
|
+
const value = record[key];
|
|
299
|
+
if (typeof value !== 'boolean')
|
|
300
|
+
throw this.unreadable(field);
|
|
301
|
+
return value;
|
|
302
|
+
}
|
|
303
|
+
unreadable(field) {
|
|
304
|
+
return new Error(`The admin API at ${this.credentials.adminApiUrl} answered a ${field} result the CLI cannot read.`);
|
|
305
|
+
}
|
|
306
|
+
notGraphQlMessage(status) {
|
|
307
|
+
return `The admin API at ${this.credentials.adminApiUrl} answered HTTP ${String(status)} without a GraphQL response.`;
|
|
308
|
+
}
|
|
309
|
+
/** What failed to connect: fetch's own message names nothing, its cause names the socket error. */
|
|
310
|
+
failureDetail(error) {
|
|
311
|
+
const cause = error instanceof Error ? error.cause : undefined;
|
|
312
|
+
if (cause instanceof Error)
|
|
313
|
+
return cause.message;
|
|
314
|
+
return error instanceof Error ? error.message : String(error);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=workspace-sync.client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-sync.client.js","sourceRoot":"","sources":["../../../src/sync/workspace-sync.client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `.env` rules a storefront's `.env` is written and read by (#1123, #2781):
|
|
3
|
+
* init's merge — the keys a run manages refreshed in place, every other line
|
|
4
|
+
* verbatim — and the split `forgecart sync` carries `.env` by: the platform's
|
|
5
|
+
* own `FORGECART_*` keys, which init writes and which never cross between a
|
|
6
|
+
* laptop and its workspace, against the merchant's keys, which do.
|
|
7
|
+
*/
|
|
8
|
+
/** The key `line` assigns — undefined for a comment, a blank or any other line. */
|
|
9
|
+
export declare function dotenvKey(line: string): string | undefined;
|
|
10
|
+
/** Whether `key` is the platform's — init's to write, never a merchant's to carry. */
|
|
11
|
+
export declare function isPlatformKey(key: string): boolean;
|
|
12
|
+
/** The value an assignment line assigns `key`: everything after its `=`, verbatim. */
|
|
13
|
+
export declare function dotenvValue(line: string, key: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* The merchant's assignments among `lines`: the FIRST line of every key that is
|
|
16
|
+
* no platform key — the line init's merge refreshes — keyed by it, in order.
|
|
17
|
+
*/
|
|
18
|
+
export declare function merchantAssignments(lines: readonly string[]): Map<string, string>;
|
|
19
|
+
/**
|
|
20
|
+
* Merge the `managed` keys into an `existing` `.env`, preserving every other
|
|
21
|
+
* line — merchant keys, comments, ordering — verbatim (#1123: a wholesale
|
|
22
|
+
* rewrite wiped the demo store's DEMO_* config on three provisioning drives).
|
|
23
|
+
* A managed key is refreshed in place at its existing position; its duplicates
|
|
24
|
+
* collapse onto the first occurrence; a managed key with no existing line is
|
|
25
|
+
* appended in the map's order. Trailing blank lines normalize to the single
|
|
26
|
+
* final newline. An empty `existing` renders the managed keys alone.
|
|
27
|
+
*/
|
|
28
|
+
export declare function mergeDotenv(existing: string, managed: ReadonlyMap<string, string>): string;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `.env` rules a storefront's `.env` is written and read by (#1123, #2781):
|
|
3
|
+
* init's merge — the keys a run manages refreshed in place, every other line
|
|
4
|
+
* verbatim — and the split `forgecart sync` carries `.env` by: the platform's
|
|
5
|
+
* own `FORGECART_*` keys, which init writes and which never cross between a
|
|
6
|
+
* laptop and its workspace, against the merchant's keys, which do.
|
|
7
|
+
*/
|
|
8
|
+
/** An assignment line: its key, then `=`. A line of any other shape is carried verbatim and never travels. */
|
|
9
|
+
const ASSIGNMENT = /^([A-Za-z_]\w*)=/;
|
|
10
|
+
/** The prefix every platform-owned key carries. */
|
|
11
|
+
const PLATFORM_KEY_PREFIX = 'FORGECART_';
|
|
12
|
+
/** The key `line` assigns — undefined for a comment, a blank or any other line. */
|
|
13
|
+
export function dotenvKey(line) {
|
|
14
|
+
return ASSIGNMENT.exec(line)?.[1];
|
|
15
|
+
}
|
|
16
|
+
/** Whether `key` is the platform's — init's to write, never a merchant's to carry. */
|
|
17
|
+
export function isPlatformKey(key) {
|
|
18
|
+
return key.startsWith(PLATFORM_KEY_PREFIX);
|
|
19
|
+
}
|
|
20
|
+
/** The value an assignment line assigns `key`: everything after its `=`, verbatim. */
|
|
21
|
+
export function dotenvValue(line, key) {
|
|
22
|
+
return line.slice(key.length + 1);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The merchant's assignments among `lines`: the FIRST line of every key that is
|
|
26
|
+
* no platform key — the line init's merge refreshes — keyed by it, in order.
|
|
27
|
+
*/
|
|
28
|
+
export function merchantAssignments(lines) {
|
|
29
|
+
const assignments = new Map();
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const key = dotenvKey(line);
|
|
32
|
+
if (key === undefined || isPlatformKey(key) || assignments.has(key))
|
|
33
|
+
continue;
|
|
34
|
+
assignments.set(key, line);
|
|
35
|
+
}
|
|
36
|
+
return assignments;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Merge the `managed` keys into an `existing` `.env`, preserving every other
|
|
40
|
+
* line — merchant keys, comments, ordering — verbatim (#1123: a wholesale
|
|
41
|
+
* rewrite wiped the demo store's DEMO_* config on three provisioning drives).
|
|
42
|
+
* A managed key is refreshed in place at its existing position; its duplicates
|
|
43
|
+
* collapse onto the first occurrence; a managed key with no existing line is
|
|
44
|
+
* appended in the map's order. Trailing blank lines normalize to the single
|
|
45
|
+
* final newline. An empty `existing` renders the managed keys alone.
|
|
46
|
+
*/
|
|
47
|
+
export function mergeDotenv(existing, managed) {
|
|
48
|
+
const refreshed = new Set();
|
|
49
|
+
const merged = existing.split('\n').flatMap((line) => refreshLine(line, managed, refreshed));
|
|
50
|
+
trimTrailingBlanks(merged);
|
|
51
|
+
for (const [key, value] of managed) {
|
|
52
|
+
if (!refreshed.has(key))
|
|
53
|
+
merged.push(`${key}=${value}`);
|
|
54
|
+
}
|
|
55
|
+
merged.push('');
|
|
56
|
+
return merged.join('\n');
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* One existing line through the merge: a line no managed key assigns passes
|
|
60
|
+
* through verbatim, a managed key refreshes in place on first sight, and its
|
|
61
|
+
* later duplicates drop.
|
|
62
|
+
*/
|
|
63
|
+
function refreshLine(line, managed, refreshed) {
|
|
64
|
+
const key = dotenvKey(line);
|
|
65
|
+
if (key === undefined)
|
|
66
|
+
return [line];
|
|
67
|
+
const value = managed.get(key);
|
|
68
|
+
if (value === undefined)
|
|
69
|
+
return [line];
|
|
70
|
+
if (refreshed.has(key))
|
|
71
|
+
return [];
|
|
72
|
+
refreshed.add(key);
|
|
73
|
+
return [`${key}=${value}`];
|
|
74
|
+
}
|
|
75
|
+
/** Drop the blank lines a file ends on: the merge writes exactly one final newline. */
|
|
76
|
+
function trimTrailingBlanks(lines) {
|
|
77
|
+
while (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
78
|
+
lines.pop();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=dotenv.util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dotenv.util.js","sourceRoot":"","sources":["../../../src/util/dotenv.util.ts"],"names":[],"mappings":"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"}
|