@mondaydotcomorg/z2h-cli 0.1.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 (157) hide show
  1. package/README.md +16 -0
  2. package/bin/z2h-cli.js +2 -0
  3. package/dist/commands/build.d.ts +5 -0
  4. package/dist/commands/build.d.ts.map +1 -0
  5. package/dist/commands/build.js +37 -0
  6. package/dist/commands/clean.d.ts +5 -0
  7. package/dist/commands/clean.d.ts.map +1 -0
  8. package/dist/commands/clean.js +24 -0
  9. package/dist/commands/deploy.d.ts +6 -0
  10. package/dist/commands/deploy.d.ts.map +1 -0
  11. package/dist/commands/deploy.js +92 -0
  12. package/dist/commands/dev.d.ts +5 -0
  13. package/dist/commands/dev.d.ts.map +1 -0
  14. package/dist/commands/dev.js +58 -0
  15. package/dist/constants.d.ts +25 -0
  16. package/dist/constants.d.ts.map +1 -0
  17. package/dist/constants.js +67 -0
  18. package/dist/esm/commands/build.d.ts +5 -0
  19. package/dist/esm/commands/build.d.ts.map +1 -0
  20. package/dist/esm/commands/build.mjs +35 -0
  21. package/dist/esm/commands/clean.d.ts +5 -0
  22. package/dist/esm/commands/clean.d.ts.map +1 -0
  23. package/dist/esm/commands/clean.mjs +22 -0
  24. package/dist/esm/commands/deploy.d.ts +6 -0
  25. package/dist/esm/commands/deploy.d.ts.map +1 -0
  26. package/dist/esm/commands/deploy.mjs +86 -0
  27. package/dist/esm/commands/dev.d.ts +5 -0
  28. package/dist/esm/commands/dev.d.ts.map +1 -0
  29. package/dist/esm/commands/dev.mjs +56 -0
  30. package/dist/esm/constants.d.ts +25 -0
  31. package/dist/esm/constants.d.ts.map +1 -0
  32. package/dist/esm/constants.mjs +57 -0
  33. package/dist/esm/index.d.ts +2 -0
  34. package/dist/esm/index.d.ts.map +1 -0
  35. package/dist/esm/index.mjs +86 -0
  36. package/dist/esm/shadow/consumer-pkg.d.ts +24 -0
  37. package/dist/esm/shadow/consumer-pkg.d.ts.map +1 -0
  38. package/dist/esm/shadow/consumer-pkg.mjs +81 -0
  39. package/dist/esm/shadow/gitignore.d.ts +7 -0
  40. package/dist/esm/shadow/gitignore.d.ts.map +1 -0
  41. package/dist/esm/shadow/gitignore.mjs +29 -0
  42. package/dist/esm/shadow/merge-package-json.d.ts +16 -0
  43. package/dist/esm/shadow/merge-package-json.d.ts.map +1 -0
  44. package/dist/esm/shadow/merge-package-json.mjs +58 -0
  45. package/dist/esm/shadow/metadata.d.ts +4 -0
  46. package/dist/esm/shadow/metadata.d.ts.map +1 -0
  47. package/dist/esm/shadow/metadata.mjs +18 -0
  48. package/dist/esm/shadow/paths.d.ts +3 -0
  49. package/dist/esm/shadow/paths.d.ts.map +1 -0
  50. package/dist/esm/shadow/paths.mjs +25 -0
  51. package/dist/esm/shadow/port.d.ts +7 -0
  52. package/dist/esm/shadow/port.d.ts.map +1 -0
  53. package/dist/esm/shadow/port.mjs +34 -0
  54. package/dist/esm/shadow/prepare.d.ts +3 -0
  55. package/dist/esm/shadow/prepare.d.ts.map +1 -0
  56. package/dist/esm/shadow/prepare.mjs +60 -0
  57. package/dist/esm/shadow/scaffold.d.ts +7 -0
  58. package/dist/esm/shadow/scaffold.d.ts.map +1 -0
  59. package/dist/esm/shadow/scaffold.mjs +112 -0
  60. package/dist/esm/shadow/sync.d.ts +9 -0
  61. package/dist/esm/shadow/sync.d.ts.map +1 -0
  62. package/dist/esm/shadow/sync.mjs +29 -0
  63. package/dist/esm/shadow/yarnrc.d.ts +10 -0
  64. package/dist/esm/shadow/yarnrc.d.ts.map +1 -0
  65. package/dist/esm/shadow/yarnrc.mjs +27 -0
  66. package/dist/esm/types.d.ts +53 -0
  67. package/dist/esm/types.d.ts.map +1 -0
  68. package/dist/esm/types.mjs +1 -0
  69. package/dist/esm/util/app-name.d.ts +12 -0
  70. package/dist/esm/util/app-name.d.ts.map +1 -0
  71. package/dist/esm/util/app-name.mjs +17 -0
  72. package/dist/esm/util/logger.d.ts +22 -0
  73. package/dist/esm/util/logger.d.ts.map +1 -0
  74. package/dist/esm/util/logger.mjs +59 -0
  75. package/dist/esm/util/manifest.d.ts +31 -0
  76. package/dist/esm/util/manifest.d.ts.map +1 -0
  77. package/dist/esm/util/manifest.mjs +31 -0
  78. package/dist/esm/util/s3/client.d.ts +11 -0
  79. package/dist/esm/util/s3/client.d.ts.map +1 -0
  80. package/dist/esm/util/s3/client.mjs +38 -0
  81. package/dist/esm/util/s3/upload.d.ts +2 -0
  82. package/dist/esm/util/s3/upload.d.ts.map +1 -0
  83. package/dist/esm/util/s3/upload.mjs +32 -0
  84. package/dist/index.d.ts +2 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +86 -0
  87. package/dist/shadow/consumer-pkg.d.ts +24 -0
  88. package/dist/shadow/consumer-pkg.d.ts.map +1 -0
  89. package/dist/shadow/consumer-pkg.js +84 -0
  90. package/dist/shadow/gitignore.d.ts +7 -0
  91. package/dist/shadow/gitignore.d.ts.map +1 -0
  92. package/dist/shadow/gitignore.js +35 -0
  93. package/dist/shadow/merge-package-json.d.ts +16 -0
  94. package/dist/shadow/merge-package-json.d.ts.map +1 -0
  95. package/dist/shadow/merge-package-json.js +66 -0
  96. package/dist/shadow/metadata.d.ts +4 -0
  97. package/dist/shadow/metadata.d.ts.map +1 -0
  98. package/dist/shadow/metadata.js +21 -0
  99. package/dist/shadow/paths.d.ts +3 -0
  100. package/dist/shadow/paths.d.ts.map +1 -0
  101. package/dist/shadow/paths.js +31 -0
  102. package/dist/shadow/port.d.ts +7 -0
  103. package/dist/shadow/port.d.ts.map +1 -0
  104. package/dist/shadow/port.js +41 -0
  105. package/dist/shadow/prepare.d.ts +3 -0
  106. package/dist/shadow/prepare.d.ts.map +1 -0
  107. package/dist/shadow/prepare.js +62 -0
  108. package/dist/shadow/scaffold.d.ts +7 -0
  109. package/dist/shadow/scaffold.d.ts.map +1 -0
  110. package/dist/shadow/scaffold.js +118 -0
  111. package/dist/shadow/sync.d.ts +9 -0
  112. package/dist/shadow/sync.d.ts.map +1 -0
  113. package/dist/shadow/sync.js +31 -0
  114. package/dist/shadow/yarnrc.d.ts +10 -0
  115. package/dist/shadow/yarnrc.d.ts.map +1 -0
  116. package/dist/shadow/yarnrc.js +33 -0
  117. package/dist/types.d.ts +53 -0
  118. package/dist/types.d.ts.map +1 -0
  119. package/dist/types.js +1 -0
  120. package/dist/util/app-name.d.ts +12 -0
  121. package/dist/util/app-name.d.ts.map +1 -0
  122. package/dist/util/app-name.js +20 -0
  123. package/dist/util/logger.d.ts +22 -0
  124. package/dist/util/logger.d.ts.map +1 -0
  125. package/dist/util/logger.js +71 -0
  126. package/dist/util/manifest.d.ts +31 -0
  127. package/dist/util/manifest.d.ts.map +1 -0
  128. package/dist/util/manifest.js +36 -0
  129. package/dist/util/s3/client.d.ts +11 -0
  130. package/dist/util/s3/client.d.ts.map +1 -0
  131. package/dist/util/s3/client.js +48 -0
  132. package/dist/util/s3/upload.d.ts +2 -0
  133. package/dist/util/s3/upload.d.ts.map +1 -0
  134. package/dist/util/s3/upload.js +39 -0
  135. package/package.json +56 -0
  136. package/src/commands/build.ts +42 -0
  137. package/src/commands/clean.ts +26 -0
  138. package/src/commands/deploy.ts +114 -0
  139. package/src/commands/dev.ts +64 -0
  140. package/src/constants.ts +63 -0
  141. package/src/index.ts +90 -0
  142. package/src/shadow/consumer-pkg.ts +100 -0
  143. package/src/shadow/gitignore.ts +30 -0
  144. package/src/shadow/merge-package-json.ts +62 -0
  145. package/src/shadow/metadata.ts +17 -0
  146. package/src/shadow/paths.ts +24 -0
  147. package/src/shadow/port.ts +34 -0
  148. package/src/shadow/prepare.ts +68 -0
  149. package/src/shadow/scaffold.ts +120 -0
  150. package/src/shadow/sync.ts +30 -0
  151. package/src/shadow/yarnrc.ts +29 -0
  152. package/src/types.ts +60 -0
  153. package/src/util/app-name.ts +16 -0
  154. package/src/util/logger.ts +64 -0
  155. package/src/util/manifest.ts +65 -0
  156. package/src/util/s3/client.ts +40 -0
  157. package/src/util/s3/upload.ts +41 -0
@@ -0,0 +1,30 @@
1
+ import { pathExists, readFile, writeFile } from 'fs-extra';
2
+ import { execa } from 'execa';
3
+ import { info } from '../util/logger';
4
+ import { buildShadowPkg, hashConsumerDeps, writeShadowPkg } from './merge-package-json';
5
+ import type { ConsumerPaths, ConsumerPkg } from '../types';
6
+
7
+ /**
8
+ * Regenerate the shadow package.json from the current CLI template and
9
+ * run `yarn install` at the consumer dir iff the consumer's declared deps
10
+ * have changed since the last install. Idempotent — safe to call on every
11
+ * build, dev start, or consumer package.json edit.
12
+ */
13
+ export async function syncShadow(paths: ConsumerPaths, consumerPkg: ConsumerPkg): Promise<void> {
14
+ const shadow = buildShadowPkg(consumerPkg);
15
+ await writeShadowPkg(paths.shadowPkg, shadow);
16
+
17
+ const newHash = hashConsumerDeps(consumerPkg);
18
+ const oldHash = (await pathExists(paths.shadowDepsHash)) ? await readFile(paths.shadowDepsHash, 'utf8') : null;
19
+
20
+ if (oldHash !== newHash) {
21
+ info(oldHash ? 'consumer deps changed — yarn install' : 'first run — yarn install');
22
+ // Yarn 4 is cache-first and non-interactive by default
23
+ // in non-TTY contexts, so no flags are needed
24
+ await execa('yarn', ['install'], {
25
+ cwd: paths.consumerDir,
26
+ stdio: 'inherit',
27
+ });
28
+ await writeFile(paths.shadowDepsHash, newHash);
29
+ }
30
+ }
@@ -0,0 +1,29 @@
1
+ import path from 'node:path';
2
+ import { pathExists, readFile, writeFile } from 'fs-extra';
3
+ import { YARNRC_LINES } from '../constants';
4
+
5
+ /**
6
+ * Ensure the consumer has a `.yarnrc.yml` that pins `nodeLinker: node-modules`.
7
+ *
8
+ * Yarn 4 defaults to PnP, which trident-toolkit's webpack pipeline doesn't
9
+ * support cleanly (webpack expects a flat node_modules). We force the
10
+ * classic linker so installs produce a real `node_modules/`. Idempotent —
11
+ * leaves any other settings the user added alone.
12
+ */
13
+
14
+ export async function ensureYarnRc(consumerDir: string): Promise<void> {
15
+ const yarnrcPath = path.join(consumerDir, '.yarnrc.yml');
16
+
17
+ if (!(await pathExists(yarnrcPath))) {
18
+ await writeFile(yarnrcPath, YARNRC_LINES.join('\n\n') + '\n');
19
+ return;
20
+ }
21
+
22
+ const body = await readFile(yarnrcPath, 'utf8');
23
+ if (/^\s*nodeLinker\s*:/m.test(body)) {
24
+ return;
25
+ }
26
+
27
+ const prefix = body.length && !body.endsWith('\n') ? '\n' : '';
28
+ await writeFile(yarnrcPath, body + prefix + 'nodeLinker: node-modules\n');
29
+ }
package/src/types.ts ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Shared domain types for the Z2H CLI. Command-scoped option types live
3
+ * with their command; narrow single-use types stay local to their file.
4
+ */
5
+
6
+ // ─────────── Consumer pkg management ───────────
7
+ export interface ConsumerPkg {
8
+ name: string;
9
+ version?: string;
10
+ private?: boolean;
11
+ workspaces?: string[] | { packages?: string[] };
12
+ dependencies?: Record<string, string>;
13
+ devDependencies?: Record<string, string>;
14
+ peerDependencies?: Record<string, string>;
15
+ scripts?: Record<string, string>;
16
+ [k: string]: unknown;
17
+ }
18
+
19
+ // ─────────── Shadow pkg (generated by CLI, lives at .zth/package.json) ───
20
+ export interface ShadowPkg {
21
+ name: string;
22
+ version: string;
23
+ private: true;
24
+ scripts: Record<string, string>;
25
+ peerDependencies: Record<string, string>;
26
+ devDependencies: Record<string, string>;
27
+ }
28
+
29
+ // ─────────── Persisted shadow metadata (.zth/metadata.json) ───────
30
+ export interface ShadowMetadata {
31
+ createdAt: string;
32
+ appName: string;
33
+ port: number;
34
+ }
35
+
36
+ // ─────────── Filesystem paths, resolved once per invocation ─────
37
+ export interface ConsumerPaths {
38
+ consumerDir: string;
39
+ consumerPkg: string;
40
+ consumerSrc: string;
41
+ consumerSrcEntry: string;
42
+ shadowDir: string;
43
+ shadowPkg: string;
44
+ shadowSrc: string;
45
+ shadowWrapper: string;
46
+ shadowTridentrc: string;
47
+ shadowNvmrc: string;
48
+ shadowDts: string;
49
+ shadowTsconfig: string;
50
+ shadowBuild: string;
51
+ shadowDepsHash: string;
52
+ shadowMetadata: string;
53
+ }
54
+
55
+ // ─────────── prepareShadow result ───────────
56
+ export interface PreparedShadow {
57
+ appName: string;
58
+ port: number;
59
+ paths: ConsumerPaths;
60
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Strip any npm scope (`@scope/`) from a package name.
3
+ * e.g. `@trident/customer-viewer` → `customer-viewer`, `foo` → `foo`.
4
+ */
5
+ export function cleanAppName(name: string): string {
6
+ return name.replace(/^@[^/]+\//, '');
7
+ }
8
+
9
+ /**
10
+ * Canonical MF bundle name: the de-scoped app name with an `mf-` prefix.
11
+ * This matches the trident convention (shadow pkg is `@trident/mf-<app>`,
12
+ * emitted bundle is `MF-mf-<app>`).
13
+ */
14
+ export function mfAppName(name: string): string {
15
+ return `mf-${cleanAppName(name)}`;
16
+ }
@@ -0,0 +1,64 @@
1
+ import pc from 'picocolors';
2
+
3
+ export type OutputMode = 'human' | 'json';
4
+
5
+ let mode: OutputMode = 'human';
6
+
7
+ export function setOutputMode(next: OutputMode): void {
8
+ mode = next;
9
+ }
10
+
11
+ export function getOutputMode(): OutputMode {
12
+ return mode;
13
+ }
14
+
15
+ /**
16
+ * Progress, info, and warnings go to stderr so stdout stays clean
17
+ * for machine-readable output. Always suppressed in JSON mode except
18
+ * when the consumer explicitly wants chatty progress (e.g. setup).
19
+ */
20
+ export function info(msg: string): void {
21
+ if (mode === 'human') {
22
+ process.stderr.write(pc.cyan(`[z2h-cli] ${msg}\n`));
23
+ }
24
+ }
25
+
26
+ export function warn(msg: string): void {
27
+ if (mode === 'human') {
28
+ process.stderr.write(pc.yellow(`[z2h-cli] ${msg}\n`));
29
+ }
30
+ }
31
+
32
+ export function errorLog(msg: string): void {
33
+ if (mode === 'human') {
34
+ process.stderr.write(pc.red(`[z2h-cli] ${msg}\n`));
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Data output. In human mode writes `human` to stdout; in JSON mode writes
40
+ * JSON-stringified `data` to stdout. Exactly one line of stdout per call.
41
+ */
42
+ export function output(human: string, data: unknown): void {
43
+ if (mode === 'json') {
44
+ process.stdout.write(`${JSON.stringify(data)}\n`);
45
+ } else {
46
+ process.stdout.write(`${human}\n`);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Emit a structured failure payload (JSON mode) or a red error line (human mode),
52
+ * then exit with the given code.
53
+ */
54
+ export function fail(message: string, payload: Record<string, unknown>, exitCode = 1): never {
55
+ if (mode === 'json') {
56
+ process.stdout.write(`${JSON.stringify({ ok: false, error: message, ...payload })}\n`);
57
+ } else {
58
+ process.stderr.write(pc.red(`[z2h-cli] ${message}\n`));
59
+ if (typeof payload.suggestedCommand === 'string') {
60
+ process.stderr.write(pc.dim(` → ${payload.suggestedCommand}\n`));
61
+ }
62
+ }
63
+ process.exit(exitCode);
64
+ }
@@ -0,0 +1,65 @@
1
+ import { PutObjectCommand } from '@aws-sdk/client-s3';
2
+ import { BUCKET_WRITE_NAME, MANIFEST_KEY, MANIFEST_READ_URL, getS3Client } from './s3/client';
3
+
4
+ export interface SharedDep {
5
+ name?: string;
6
+ globalName: string;
7
+ url?: string;
8
+ minVersion?: string;
9
+ dependencies: Record<string, number>;
10
+ }
11
+
12
+ /**
13
+ * Shape of the `asset-manifest.json` trident-toolkit emits alongside the
14
+ * bundle. We consume this file directly as the base for a Z2H registry
15
+ * entry — deploy.ts only overlays `version` and `url`.
16
+ */
17
+ export interface AssetManifestFile {
18
+ sameAsHost: boolean;
19
+ skipTranslationFetch: boolean;
20
+ shared: SharedDep[];
21
+ entrypoints: string[];
22
+ nonExternalsEntrypoints?: string[];
23
+ scriptType?: 'module' | 'classic';
24
+ mfs?: string[];
25
+ }
26
+
27
+ export interface ManifestEntry extends AssetManifestFile {
28
+ version: number;
29
+ url: string;
30
+ }
31
+
32
+ export type Manifest = Record<string, ManifestEntry>;
33
+
34
+ export async function readManifest(): Promise<Manifest> {
35
+ // Read via the public CDN endpoint, no AWS auth required.
36
+ const res = await fetch(MANIFEST_READ_URL);
37
+
38
+ if (!res.ok) {
39
+ throw new Error(`failed to read manifest from ${MANIFEST_READ_URL}: HTTP ${res.status}`);
40
+ }
41
+ return (await res.json()) as Manifest;
42
+ }
43
+
44
+ export function nextVersion(manifest: Manifest, app: string) {
45
+ const current = manifest[app]?.version;
46
+ const n = current ? Number(current) : 0;
47
+ return (Number.isNaN(n) ? 0 : n) + 1;
48
+ }
49
+
50
+ export function upsertEntry(manifest: Manifest, app: string, entry: ManifestEntry): Manifest {
51
+ return { ...manifest, [app]: entry };
52
+ }
53
+
54
+ export async function writeManifest(manifest: Manifest): Promise<void> {
55
+ const s3 = getS3Client();
56
+ await s3.send(
57
+ new PutObjectCommand({
58
+ Bucket: BUCKET_WRITE_NAME,
59
+ Key: MANIFEST_KEY,
60
+ Body: JSON.stringify(manifest, null, 2),
61
+ ContentType: 'application/json',
62
+ CacheControl: 'no-cache',
63
+ })
64
+ );
65
+ }
@@ -0,0 +1,40 @@
1
+ import { HeadBucketCommand, S3Client, S3ServiceException } from '@aws-sdk/client-s3';
2
+
3
+ export const BUCKET_WRITE_NAME = 'prod-use1-bigbrain-zth-mf-assets';
4
+ export const REGION = 'us-east-1';
5
+ export const BUCKET_WRITE_URL = `https://${BUCKET_WRITE_NAME}.s3.${REGION}.amazonaws.com`;
6
+
7
+ export const BUCKET_READ_DNS_NAME = 'bigbrain-zth-mf-assets';
8
+ export const BUCKET_READ_DNS_URL = `https://${BUCKET_READ_DNS_NAME}.bigbrain.me`;
9
+
10
+ // No `.json` extension — TridentSkeleton's `mfUrl` API appends `/mf-assets`
11
+ // to the bucket base when fetching, so the manifest must live at this exact
12
+ // key. See microfrontends/mf-bigbrain-zth/.../ZeroToHeroHost.tsx.
13
+ export const MANIFEST_KEY = 'mf-assets';
14
+ export const MANIFEST_READ_URL = `${BUCKET_READ_DNS_URL}/${MANIFEST_KEY}`;
15
+
16
+ let client: S3Client | null = null;
17
+
18
+ export function getS3Client(): S3Client {
19
+ if (!client) {
20
+ client = new S3Client({ region: REGION });
21
+ }
22
+ return client;
23
+ }
24
+
25
+ export async function assertBucketExists(): Promise<void> {
26
+ try {
27
+ await getS3Client().send(new HeadBucketCommand({ Bucket: BUCKET_WRITE_NAME }));
28
+ } catch (err) {
29
+ if (err instanceof S3ServiceException) {
30
+ const status = err.$metadata.httpStatusCode;
31
+ if (status === 404 || err.name === 'NotFound' || err.name === 'NoSuchBucket') {
32
+ throw new Error(`S3 bucket "${BUCKET_WRITE_NAME}" does not exist (region: ${REGION}).`);
33
+ }
34
+ if (status === 403) {
35
+ throw new Error(`No access to S3 bucket "${BUCKET_WRITE_NAME}" (region: ${REGION}). Check AWS credentials.`);
36
+ }
37
+ }
38
+ throw err;
39
+ }
40
+ }
@@ -0,0 +1,41 @@
1
+ import path from 'node:path';
2
+ import { readFile } from 'fs-extra';
3
+ import { readdir } from 'node:fs/promises';
4
+ import { lookup as lookupMime } from 'mime-types';
5
+ import pMap from 'p-map';
6
+ import { PutObjectCommand } from '@aws-sdk/client-s3';
7
+ import { BUCKET_WRITE_NAME, getS3Client } from './client';
8
+
9
+ // Each deploy uploads to a version-prefixed path (<app>/<version>/...), so
10
+ // the URL itself is the cache key. Assets never need to be revalidated.
11
+ const FOREVER = 'public, immutable';
12
+
13
+ const UPLOAD_CONCURRENCY = 5;
14
+
15
+ export async function uploadDir(localDir: string, keyPrefix: string): Promise<string[]> {
16
+ const entries = await readdir(localDir, { recursive: true, withFileTypes: true });
17
+ const files = entries.filter(e => e.isFile()).map(e => path.relative(localDir, path.join(e.parentPath, e.name)));
18
+
19
+ const s3 = getS3Client();
20
+
21
+ await pMap(
22
+ files,
23
+ async rel => {
24
+ const body = await readFile(path.join(localDir, rel));
25
+ const contentType = lookupMime(rel) || 'application/octet-stream';
26
+ const key = `${keyPrefix}/${rel.split(path.sep).join('/')}`;
27
+ await s3.send(
28
+ new PutObjectCommand({
29
+ Bucket: BUCKET_WRITE_NAME,
30
+ Key: key,
31
+ Body: body,
32
+ ContentType: contentType,
33
+ CacheControl: FOREVER,
34
+ })
35
+ );
36
+ },
37
+ { concurrency: UPLOAD_CONCURRENCY }
38
+ );
39
+
40
+ return files.map(f => f.split(path.sep).join('/'));
41
+ }