@swell/cli 0.1.1 → 1.0.1-alpha

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/README.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # @swell/cli
2
2
 
3
- We're in beta: commands and arguments can still change.
3
+ #We're in beta: commands and arguments can still change.#
4
+
5
+ Install with:
6
+
7
+ ```sh
8
+ npm install -g @swell/cli
9
+ ```
10
+
11
+ If you are using tools for version managing like [asdf](https://asdf-vm.com/), make sure you [reshim](https://asdf-vm.com/manage/core.html#reshim) after install:
12
+
13
+ ```sh
14
+ asdf reshim
15
+ ```
4
16
 
5
17
  # Commands
6
18
 
@@ -12,15 +24,10 @@ Commands tend to support the following helper options:
12
24
  - --verbose (-v): Boolean, outputs all commands
13
25
  - --dry-run: Boolean, dry runs requests that delete data, but doesn't run them
14
26
  - --yes (-y): Boolean, answers all confirmation questions with **yes**.
15
- - --secret-key: String, Enables ephemeral secret-key based authentication for commands
16
-
17
- ## Store namespaces
18
-
19
- All commands accept a `--store` or `-s` option, which allows switching the context of the CLI commands to the selected store. When not in use, all commands refer to the set default store.
20
27
 
21
28
  ## Login
22
29
 
23
- The `login` command authenticates and stores a user's session locally. Saved sessions can/will be used by other commands, unless a secret key is specified.
30
+ The `admin login` command authenticates and stores a user's session locally. Saved sessions can/will be used by other commands, unless a secret key is specified.
24
31
 
25
32
  ```sh
26
33
  swell login [--username (-u) String] [--password (-p) String] [--set-default Boolean]
@@ -30,54 +37,41 @@ swell login [--username (-u) String] [--password (-p) String] [--set-default Boo
30
37
 
31
38
  ```sh
32
39
  swell login
33
- swell login --store test
34
- swell login --store test-default --set-default
35
- # local login
36
- ADMIN_API_BASE_URL='http://${STORE_ID}.swell.test:4001/admin/api' swell login
40
+ swell login --set-default
37
41
  ```
38
42
 
39
- ## Storefront
40
-
41
- ### Settings
43
+ ## Storefronts
42
44
 
43
- Push or pull storefront settings. Scopes include `settings`, `editor` and `menus`.
45
+ List all storefronts associated to logged in user:
44
46
 
45
47
  ```sh
46
- # pull => retrieves settings from the server and tries to merge and/or write to a specified JSON file or stdout
47
- swell settings pull SCOPE [--file (-f) String] [--output (-o) Boolean] [--strategy String]
48
-
49
- # push => send settings from a JSON file or stdin to the server
50
- swell settings push SCOPE [--file (-f) String] [--input (-i) Boolean] [--strategy String]
48
+ swell storefronts list
51
49
  ```
52
50
 
53
- ### Examples
54
-
51
+ Retrieves storefront configuration data from the server and writes it to a file if specified or stdout by default. It requires a type to run. Configuration types allowed are "editor", "menus" and "settings":
55
52
  ```sh
56
- swell storefront settings pull settings -f settings/store.json
57
- swell storefront settings pull settings -f settings/store.json --strategy ours # keeps our version of the file in case of conflicts
58
-
59
- swell storefront settings push editor -f settings/editor.json
60
- swell storefront settings push editor -f settings/editor.json --strategy "set" # overwrites the settings object instead of merging
61
-
62
- cat settings/menus.json | swell storefront settings push menus -i --strategy "defaults" # merges remote settings on top of the settings being pushed, as a default mechanism
53
+ swell storefronts pull --id String --type (-t) String [--file (-f) String]
63
54
  ```
64
55
 
65
- ### SSH Keys
56
+ Send storefront configuration data to the server. Configuration types allowed are "editor", "menus" and "settings":
57
+ ```sh
58
+ swell storefronts push --id String --type (-t) String [--file (-f) String] [--strategy (-i) String]
59
+ ```
66
60
 
67
- Manage a user's SSH keys to a storefront. Needs session authentication (`swell login`).
61
+ ### Examples
68
62
 
69
63
  ```sh
70
- swell storefront ssh-keys list
71
- swell storefront ssh-keys add [--name String]
72
- swell storefront ssh-keys delete KEY_ID [--dry-run Boolean]
64
+ swell storefronts pull --id P5OzfzLD -t editor
65
+ swell storefronts pull --id P5OzfzLD -t menus -f menus.json
66
+ swell storefronts push --id P5OzfzLD -t editor -s set -f ../origin-storefront/config/editor.json
73
67
  ```
74
68
 
75
- ### Examples
69
+ ### Without logging in
76
70
 
77
71
  ```sh
78
- swell storefront ssh-keys list
79
- swell storefront ssh-keys add --name deploy-key
80
- swell storefront ssh-keys delete abcdef1234567890
72
+ swell storefronts pull --id P5OzfzLD -t editor --store my-test-store --secret-key my-dev-secret-key
73
+ swell storefronts pull --id P5OzfzLD -t menus -f menus.json --store my-test-store --secret-key my-dev-secret-key
74
+ swell storefronts push --id P5OzfzLD -t editor -s set -f ../origin-storefront/config/editor.json --store my-test-store --secret-key my-dev-secret-key
81
75
  ```
82
76
 
83
77
  ## Model
@@ -85,12 +79,12 @@ swell storefront ssh-keys delete abcdef1234567890
85
79
  Push or pull model definitions, including namespaced models (e.g. content models), abstract content models and content fields.
86
80
 
87
81
  ```sh
88
- # pull => retrieves a model from the server and tries to merge and/or write to a specified JSON file or stdout
82
+ # pull => retrieves a model from the server and tries to merge and/or write to a specified JSON file or stdout (by default)
89
83
  # --content flag toggles between base models and abstract content models or content fields
90
- swell model pull MODEL_ID [--file (-f) String] [--output (-o) Boolean] [--strategy String] [--content Boolean]
84
+ swell model pull -m MODEL_ID [--file (-f) String] [--strategy (-s) String] [--content Boolean]
91
85
 
92
86
  # push => send a model definition from a JSON file or stdin to the server
93
- swell model push [--file (-f) String] [--input (-i) Boolean] [--content Boolean]
87
+ swell model push [--file (-f) String] [--input (-i) stdin] [--content Boolean]
94
88
  ```
95
89
 
96
90
  It is important that the model definition includes some fields depending on the type/source of the model.
@@ -129,32 +123,19 @@ It is important that the model definition includes some fields depending on the
129
123
  ### Examples
130
124
 
131
125
  ```sh
132
- swell model pull products -f models/products.json
133
- swell model pull products -f models/products.json --strategy ours # keeps our version of the file in case of conflicts
134
- swell model pull theme.origin.page --content --f models/page.json # pull a content model
126
+ swell model pull -m products # pull products model and display in stdout
127
+ swell model pull -m theme.origin.page --content -f models/page.json # pull a content model and save it to a file
135
128
 
136
129
  swell model push -f models/orders.json
137
- swell model push --content --f models/page.json # push a content model
138
- ```
139
-
140
- ## Settings
141
-
142
- Push or pull store settings (e.g. payments, shipping, etc...)
143
-
144
- ```sh
145
- # pull => retrieves settings from the server and tries to merge and/or write to a specified JSON file or stdout
146
- swell settings pull SCOPE [--file (-f) String] [--output (-o) Boolean] [--strategy String]
147
-
148
- # push => send settings from a JSON file or stdin to the server
149
- swell settings push SCOPE [--file (-f) String] [--input (-i) Boolean] [--strategy String]
130
+ swell model push -c -f models/page.json # push a content model
150
131
  ```
151
132
 
152
- ### Examples
133
+ ### Without logging in
153
134
 
154
135
  ```sh
155
- swell settings pull payments -f settings/payments.json
156
- swell settings pull payments -f settings/payments.json --strategy ours # keeps our version of the file in case of conflicts
136
+ swell model pull -m products --store my-test-store --secret-key my-dev-secret-key
137
+ swell model pull -m theme.origin.page --content -f models/page.json --store my-test-store --secret-key my-dev-secret-key
157
138
 
158
- swell settings push payments -f settings/payments.json
159
- swell settings push payments -f settings/payments.json --strategy "set" # overwrites the settings object instead of merging
160
- ```
139
+ swell model push -f models/orders.json --store my-test-store --secret-key my-dev-secret-key
140
+ swell model push -c -f models/page.json --store my-test-store --secret-key my-dev-secret-key
141
+ ```
@@ -1,29 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import 'fs';
4
- import 'path';
5
- import { F as FormData, a as File } from './swell-2acb6c0f.js';
6
- import 'assert';
7
- import 'child_process';
8
- import 'async_hooks';
9
- import 'util';
10
- import 'process';
11
- import 'os';
12
- import 'tty';
13
- import 'stream';
14
- import 'string_decoder';
15
- import 'buffer';
16
- import 'events';
17
- import 'url';
18
- import 'http';
19
- import 'https';
20
- import 'zlib';
21
- import 'stream/web';
22
- import 'net';
23
- import 'worker_threads';
24
- import 'readline';
25
- import 'constants';
26
- import 'crypto';
3
+ import { F as FormData, a as File } from './swell-cfd63c19.js';
4
+ import 'node:assert';
5
+ import 'node:child_process';
6
+ import 'node:async_hooks';
7
+ import 'node:util';
8
+ import 'node:process';
9
+ import 'node:os';
10
+ import 'node:tty';
11
+ import 'node:fs';
12
+ import 'node:path';
13
+ import 'node:stream';
14
+ import 'node:string_decoder';
15
+ import 'node:buffer';
16
+ import 'node:events';
17
+ import 'node:url';
18
+ import 'node:http';
19
+ import 'node:https';
20
+ import 'node:zlib';
21
+ import 'node:net';
22
+ import 'node:readline';
23
+ import 'node:constants';
24
+ import 'node:crypto';
27
25
 
28
26
  let s = 0;
29
27
  const S = {
@@ -0,0 +1,456 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { F as FormData, a as File } from './swell-96dae89a.js';
4
+ import 'node:assert';
5
+ import 'node:child_process';
6
+ import 'node:async_hooks';
7
+ import 'node:util';
8
+ import 'node:process';
9
+ import 'node:os';
10
+ import 'node:tty';
11
+ import 'node:fs';
12
+ import 'node:path';
13
+ import 'node:stream';
14
+ import 'node:string_decoder';
15
+ import 'node:buffer';
16
+ import 'node:events';
17
+ import 'node:url';
18
+ import 'node:http';
19
+ import 'node:https';
20
+ import 'node:zlib';
21
+ import 'node:net';
22
+ import 'node:readline';
23
+ import 'node:constants';
24
+ import 'node:crypto';
25
+
26
+ let s = 0;
27
+ const S = {
28
+ START_BOUNDARY: s++,
29
+ HEADER_FIELD_START: s++,
30
+ HEADER_FIELD: s++,
31
+ HEADER_VALUE_START: s++,
32
+ HEADER_VALUE: s++,
33
+ HEADER_VALUE_ALMOST_DONE: s++,
34
+ HEADERS_ALMOST_DONE: s++,
35
+ PART_DATA_START: s++,
36
+ PART_DATA: s++,
37
+ END: s++
38
+ };
39
+
40
+ let f = 1;
41
+ const F = {
42
+ PART_BOUNDARY: f,
43
+ LAST_BOUNDARY: f *= 2
44
+ };
45
+
46
+ const LF = 10;
47
+ const CR = 13;
48
+ const SPACE = 32;
49
+ const HYPHEN = 45;
50
+ const COLON = 58;
51
+ const A = 97;
52
+ const Z = 122;
53
+
54
+ const lower = c => c | 0x20;
55
+
56
+ const noop = () => {};
57
+
58
+ class MultipartParser {
59
+ /**
60
+ * @param {string} boundary
61
+ */
62
+ constructor(boundary) {
63
+ this.index = 0;
64
+ this.flags = 0;
65
+
66
+ this.onHeaderEnd = noop;
67
+ this.onHeaderField = noop;
68
+ this.onHeadersEnd = noop;
69
+ this.onHeaderValue = noop;
70
+ this.onPartBegin = noop;
71
+ this.onPartData = noop;
72
+ this.onPartEnd = noop;
73
+
74
+ this.boundaryChars = {};
75
+
76
+ boundary = '\r\n--' + boundary;
77
+ const ui8a = new Uint8Array(boundary.length);
78
+ for (let i = 0; i < boundary.length; i++) {
79
+ ui8a[i] = boundary.charCodeAt(i);
80
+ this.boundaryChars[ui8a[i]] = true;
81
+ }
82
+
83
+ this.boundary = ui8a;
84
+ this.lookbehind = new Uint8Array(this.boundary.length + 8);
85
+ this.state = S.START_BOUNDARY;
86
+ }
87
+
88
+ /**
89
+ * @param {Uint8Array} data
90
+ */
91
+ write(data) {
92
+ let i = 0;
93
+ const length_ = data.length;
94
+ let previousIndex = this.index;
95
+ let {lookbehind, boundary, boundaryChars, index, state, flags} = this;
96
+ const boundaryLength = this.boundary.length;
97
+ const boundaryEnd = boundaryLength - 1;
98
+ const bufferLength = data.length;
99
+ let c;
100
+ let cl;
101
+
102
+ const mark = name => {
103
+ this[name + 'Mark'] = i;
104
+ };
105
+
106
+ const clear = name => {
107
+ delete this[name + 'Mark'];
108
+ };
109
+
110
+ const callback = (callbackSymbol, start, end, ui8a) => {
111
+ if (start === undefined || start !== end) {
112
+ this[callbackSymbol](ui8a && ui8a.subarray(start, end));
113
+ }
114
+ };
115
+
116
+ const dataCallback = (name, clear) => {
117
+ const markSymbol = name + 'Mark';
118
+ if (!(markSymbol in this)) {
119
+ return;
120
+ }
121
+
122
+ if (clear) {
123
+ callback(name, this[markSymbol], i, data);
124
+ delete this[markSymbol];
125
+ } else {
126
+ callback(name, this[markSymbol], data.length, data);
127
+ this[markSymbol] = 0;
128
+ }
129
+ };
130
+
131
+ for (i = 0; i < length_; i++) {
132
+ c = data[i];
133
+
134
+ switch (state) {
135
+ case S.START_BOUNDARY:
136
+ if (index === boundary.length - 2) {
137
+ if (c === HYPHEN) {
138
+ flags |= F.LAST_BOUNDARY;
139
+ } else if (c !== CR) {
140
+ return;
141
+ }
142
+
143
+ index++;
144
+ break;
145
+ } else if (index - 1 === boundary.length - 2) {
146
+ if (flags & F.LAST_BOUNDARY && c === HYPHEN) {
147
+ state = S.END;
148
+ flags = 0;
149
+ } else if (!(flags & F.LAST_BOUNDARY) && c === LF) {
150
+ index = 0;
151
+ callback('onPartBegin');
152
+ state = S.HEADER_FIELD_START;
153
+ } else {
154
+ return;
155
+ }
156
+
157
+ break;
158
+ }
159
+
160
+ if (c !== boundary[index + 2]) {
161
+ index = -2;
162
+ }
163
+
164
+ if (c === boundary[index + 2]) {
165
+ index++;
166
+ }
167
+
168
+ break;
169
+ case S.HEADER_FIELD_START:
170
+ state = S.HEADER_FIELD;
171
+ mark('onHeaderField');
172
+ index = 0;
173
+ // falls through
174
+ case S.HEADER_FIELD:
175
+ if (c === CR) {
176
+ clear('onHeaderField');
177
+ state = S.HEADERS_ALMOST_DONE;
178
+ break;
179
+ }
180
+
181
+ index++;
182
+ if (c === HYPHEN) {
183
+ break;
184
+ }
185
+
186
+ if (c === COLON) {
187
+ if (index === 1) {
188
+ // empty header field
189
+ return;
190
+ }
191
+
192
+ dataCallback('onHeaderField', true);
193
+ state = S.HEADER_VALUE_START;
194
+ break;
195
+ }
196
+
197
+ cl = lower(c);
198
+ if (cl < A || cl > Z) {
199
+ return;
200
+ }
201
+
202
+ break;
203
+ case S.HEADER_VALUE_START:
204
+ if (c === SPACE) {
205
+ break;
206
+ }
207
+
208
+ mark('onHeaderValue');
209
+ state = S.HEADER_VALUE;
210
+ // falls through
211
+ case S.HEADER_VALUE:
212
+ if (c === CR) {
213
+ dataCallback('onHeaderValue', true);
214
+ callback('onHeaderEnd');
215
+ state = S.HEADER_VALUE_ALMOST_DONE;
216
+ }
217
+
218
+ break;
219
+ case S.HEADER_VALUE_ALMOST_DONE:
220
+ if (c !== LF) {
221
+ return;
222
+ }
223
+
224
+ state = S.HEADER_FIELD_START;
225
+ break;
226
+ case S.HEADERS_ALMOST_DONE:
227
+ if (c !== LF) {
228
+ return;
229
+ }
230
+
231
+ callback('onHeadersEnd');
232
+ state = S.PART_DATA_START;
233
+ break;
234
+ case S.PART_DATA_START:
235
+ state = S.PART_DATA;
236
+ mark('onPartData');
237
+ // falls through
238
+ case S.PART_DATA:
239
+ previousIndex = index;
240
+
241
+ if (index === 0) {
242
+ // boyer-moore derrived algorithm to safely skip non-boundary data
243
+ i += boundaryEnd;
244
+ while (i < bufferLength && !(data[i] in boundaryChars)) {
245
+ i += boundaryLength;
246
+ }
247
+
248
+ i -= boundaryEnd;
249
+ c = data[i];
250
+ }
251
+
252
+ if (index < boundary.length) {
253
+ if (boundary[index] === c) {
254
+ if (index === 0) {
255
+ dataCallback('onPartData', true);
256
+ }
257
+
258
+ index++;
259
+ } else {
260
+ index = 0;
261
+ }
262
+ } else if (index === boundary.length) {
263
+ index++;
264
+ if (c === CR) {
265
+ // CR = part boundary
266
+ flags |= F.PART_BOUNDARY;
267
+ } else if (c === HYPHEN) {
268
+ // HYPHEN = end boundary
269
+ flags |= F.LAST_BOUNDARY;
270
+ } else {
271
+ index = 0;
272
+ }
273
+ } else if (index - 1 === boundary.length) {
274
+ if (flags & F.PART_BOUNDARY) {
275
+ index = 0;
276
+ if (c === LF) {
277
+ // unset the PART_BOUNDARY flag
278
+ flags &= ~F.PART_BOUNDARY;
279
+ callback('onPartEnd');
280
+ callback('onPartBegin');
281
+ state = S.HEADER_FIELD_START;
282
+ break;
283
+ }
284
+ } else if (flags & F.LAST_BOUNDARY) {
285
+ if (c === HYPHEN) {
286
+ callback('onPartEnd');
287
+ state = S.END;
288
+ flags = 0;
289
+ } else {
290
+ index = 0;
291
+ }
292
+ } else {
293
+ index = 0;
294
+ }
295
+ }
296
+
297
+ if (index > 0) {
298
+ // when matching a possible boundary, keep a lookbehind reference
299
+ // in case it turns out to be a false lead
300
+ lookbehind[index - 1] = c;
301
+ } else if (previousIndex > 0) {
302
+ // if our boundary turned out to be rubbish, the captured lookbehind
303
+ // belongs to partData
304
+ const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength);
305
+ callback('onPartData', 0, previousIndex, _lookbehind);
306
+ previousIndex = 0;
307
+ mark('onPartData');
308
+
309
+ // reconsider the current character even so it interrupted the sequence
310
+ // it could be the beginning of a new sequence
311
+ i--;
312
+ }
313
+
314
+ break;
315
+ case S.END:
316
+ break;
317
+ default:
318
+ throw new Error(`Unexpected state entered: ${state}`);
319
+ }
320
+ }
321
+
322
+ dataCallback('onHeaderField');
323
+ dataCallback('onHeaderValue');
324
+ dataCallback('onPartData');
325
+
326
+ // Update properties for the next call
327
+ this.index = index;
328
+ this.state = state;
329
+ this.flags = flags;
330
+ }
331
+
332
+ end() {
333
+ if ((this.state === S.HEADER_FIELD_START && this.index === 0) ||
334
+ (this.state === S.PART_DATA && this.index === this.boundary.length)) {
335
+ this.onPartEnd();
336
+ } else if (this.state !== S.END) {
337
+ throw new Error('MultipartParser.end(): stream ended unexpectedly');
338
+ }
339
+ }
340
+ }
341
+
342
+ function _fileName(headerValue) {
343
+ // matches either a quoted-string or a token (RFC 2616 section 19.5.1)
344
+ const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);
345
+ if (!m) {
346
+ return;
347
+ }
348
+
349
+ const match = m[2] || m[3] || '';
350
+ let filename = match.slice(match.lastIndexOf('\\') + 1);
351
+ filename = filename.replace(/%22/g, '"');
352
+ filename = filename.replace(/&#(\d{4});/g, (m, code) => {
353
+ return String.fromCharCode(code);
354
+ });
355
+ return filename;
356
+ }
357
+
358
+ async function toFormData(Body, ct) {
359
+ if (!/multipart/i.test(ct)) {
360
+ throw new TypeError('Failed to fetch');
361
+ }
362
+
363
+ const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
364
+
365
+ if (!m) {
366
+ throw new TypeError('no or bad content-type header, no multipart boundary');
367
+ }
368
+
369
+ const parser = new MultipartParser(m[1] || m[2]);
370
+
371
+ let headerField;
372
+ let headerValue;
373
+ let entryValue;
374
+ let entryName;
375
+ let contentType;
376
+ let filename;
377
+ const entryChunks = [];
378
+ const formData = new FormData();
379
+
380
+ const onPartData = ui8a => {
381
+ entryValue += decoder.decode(ui8a, {stream: true});
382
+ };
383
+
384
+ const appendToFile = ui8a => {
385
+ entryChunks.push(ui8a);
386
+ };
387
+
388
+ const appendFileToFormData = () => {
389
+ const file = new File(entryChunks, filename, {type: contentType});
390
+ formData.append(entryName, file);
391
+ };
392
+
393
+ const appendEntryToFormData = () => {
394
+ formData.append(entryName, entryValue);
395
+ };
396
+
397
+ const decoder = new TextDecoder('utf-8');
398
+ decoder.decode();
399
+
400
+ parser.onPartBegin = function () {
401
+ parser.onPartData = onPartData;
402
+ parser.onPartEnd = appendEntryToFormData;
403
+
404
+ headerField = '';
405
+ headerValue = '';
406
+ entryValue = '';
407
+ entryName = '';
408
+ contentType = '';
409
+ filename = null;
410
+ entryChunks.length = 0;
411
+ };
412
+
413
+ parser.onHeaderField = function (ui8a) {
414
+ headerField += decoder.decode(ui8a, {stream: true});
415
+ };
416
+
417
+ parser.onHeaderValue = function (ui8a) {
418
+ headerValue += decoder.decode(ui8a, {stream: true});
419
+ };
420
+
421
+ parser.onHeaderEnd = function () {
422
+ headerValue += decoder.decode();
423
+ headerField = headerField.toLowerCase();
424
+
425
+ if (headerField === 'content-disposition') {
426
+ // matches either a quoted-string or a token (RFC 2616 section 19.5.1)
427
+ const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);
428
+
429
+ if (m) {
430
+ entryName = m[2] || m[3] || '';
431
+ }
432
+
433
+ filename = _fileName(headerValue);
434
+
435
+ if (filename) {
436
+ parser.onPartData = appendToFile;
437
+ parser.onPartEnd = appendFileToFormData;
438
+ }
439
+ } else if (headerField === 'content-type') {
440
+ contentType = headerValue;
441
+ }
442
+
443
+ headerValue = '';
444
+ headerField = '';
445
+ };
446
+
447
+ for await (const chunk of Body) {
448
+ parser.write(chunk);
449
+ }
450
+
451
+ parser.end();
452
+
453
+ return formData;
454
+ }
455
+
456
+ export { toFormData };