@logto/cli 1.0.0-beta.15 → 1.0.0-beta.18

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 (43) hide show
  1. package/bin/logto.js +2 -0
  2. package/lib/commands/connector/add.js +8 -10
  3. package/lib/commands/connector/index.js +9 -14
  4. package/lib/commands/connector/list.js +10 -16
  5. package/lib/commands/connector/remove.js +13 -18
  6. package/lib/commands/connector/utils.d.ts +1 -1
  7. package/lib/commands/connector/utils.js +100 -86
  8. package/lib/commands/database/alteration/index.d.ts +1 -2
  9. package/lib/commands/database/alteration/index.js +36 -98
  10. package/lib/commands/database/alteration/index.test.js +36 -57
  11. package/lib/commands/database/alteration/meta-url.d.ts +1 -0
  12. package/lib/commands/database/alteration/meta-url.js +4 -0
  13. package/lib/commands/database/alteration/type.d.ts +1 -1
  14. package/lib/commands/database/alteration/type.js +1 -2
  15. package/lib/commands/database/alteration/utils.d.ts +4 -0
  16. package/lib/commands/database/alteration/utils.js +39 -0
  17. package/lib/commands/database/alteration/version.d.ts +1 -1
  18. package/lib/commands/database/alteration/version.js +18 -25
  19. package/lib/commands/database/config.js +44 -49
  20. package/lib/commands/database/index.js +7 -12
  21. package/lib/commands/database/seed/index.d.ts +1 -1
  22. package/lib/commands/database/seed/index.js +59 -55
  23. package/lib/commands/database/seed/oidc-config.js +15 -18
  24. package/lib/commands/database/utilities.js +6 -11
  25. package/lib/commands/install/index.d.ts +1 -1
  26. package/lib/commands/install/index.js +14 -28
  27. package/lib/commands/install/utils.d.ts +0 -1
  28. package/lib/commands/install/utils.js +59 -106
  29. package/lib/constants.js +1 -4
  30. package/lib/database.js +33 -43
  31. package/lib/index.js +18 -23
  32. package/lib/meta-url.d.ts +1 -0
  33. package/lib/meta-url.js +4 -0
  34. package/lib/package-json.d.ts +7 -3
  35. package/lib/package-json.js +28 -27
  36. package/lib/queries/logto-config.js +18 -26
  37. package/lib/queries/logto-config.test.js +29 -30
  38. package/lib/test-utilities.d.ts +2 -2
  39. package/lib/test-utilities.js +1 -5
  40. package/lib/utilities.d.ts +5 -5
  41. package/lib/utilities.js +49 -64
  42. package/package.json +27 -23
  43. package/bin/logto +0 -2
package/bin/logto.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../lib/index.js';
@@ -1,7 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const utilities_1 = require("../../utilities");
4
- const utils_1 = require("./utils");
1
+ import { log } from '../../utilities.js';
2
+ import { addConnectors, addOfficialConnectors, inquireInstancePath } from './utils.js';
5
3
  const add = {
6
4
  command: ['add [packages...]', 'a', 'install', 'i'],
7
5
  describe: 'Add specific Logto connectors',
@@ -20,17 +18,17 @@ const add = {
20
18
  "If it's true, the specified package names will be ignored.",
21
19
  }),
22
20
  handler: async ({ packages: packageNames, path, official }) => {
23
- const instancePath = await (0, utils_1.inquireInstancePath)(path);
21
+ const instancePath = await inquireInstancePath(path);
24
22
  if (official) {
25
- await (0, utils_1.addOfficialConnectors)(instancePath);
23
+ await addOfficialConnectors(instancePath);
26
24
  }
27
25
  else {
28
26
  if (!packageNames?.length) {
29
- utilities_1.log.error('No connector name provided');
27
+ log.error('No connector name provided');
30
28
  }
31
- await (0, utils_1.addConnectors)(instancePath, packageNames);
29
+ await addConnectors(instancePath, packageNames);
32
30
  }
33
- utilities_1.log.info('Restart your Logto instance to get the changes reflected.');
31
+ log.info('Restart your Logto instance to get the changes reflected.');
34
32
  },
35
33
  };
36
- exports.default = add;
34
+ export default add;
@@ -1,12 +1,7 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const essentials_1 = require("@silverhand/essentials");
7
- const add_1 = __importDefault(require("./add"));
8
- const list_1 = __importDefault(require("./list"));
9
- const remove_1 = __importDefault(require("./remove"));
1
+ import { noop } from '@silverhand/essentials';
2
+ import add from './add.js';
3
+ import list from './list.js';
4
+ import remove from './remove.js';
10
5
  const connector = {
11
6
  command: ['connector', 'c', 'connectors'],
12
7
  describe: 'Command for Logto connectors',
@@ -16,10 +11,10 @@ const connector = {
16
11
  type: 'string',
17
12
  describe: 'The path to your Logto instance directory',
18
13
  })
19
- .command(add_1.default)
20
- .command(list_1.default)
21
- .command(remove_1.default)
14
+ .command(add)
15
+ .command(list)
16
+ .command(remove)
22
17
  .demandCommand(1),
23
- handler: essentials_1.noop,
18
+ handler: noop,
24
19
  };
25
- exports.default = connector;
20
+ export default connector;
@@ -1,28 +1,22 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const chalk_1 = __importDefault(require("chalk"));
7
- const utils_1 = require("./utils");
8
- const logConnectorNames = (type, names) => {
9
- if (names.length === 0) {
1
+ import chalk from 'chalk';
2
+ import { getConnectorPackagesFrom, isOfficialConnector } from './utils.js';
3
+ const logConnectorNames = (type, packages) => {
4
+ if (packages.length === 0) {
10
5
  return;
11
6
  }
12
7
  console.log();
13
- console.log(chalk_1.default.blue(type));
14
- console.log(names.map((value) => ' ' + value).join('\n'));
8
+ console.log(chalk.blue(type));
9
+ console.log(packages.map(({ name }) => ' ' + name).join('\n'));
15
10
  };
16
11
  const list = {
17
12
  command: ['list', 'l'],
18
13
  describe: 'List added Logto connectors',
19
14
  handler: async ({ path: inputPath }) => {
20
- const packages = await (0, utils_1.getConnectorPackagesFrom)(inputPath);
21
- const packageNames = packages.map(({ name }) => name);
22
- const officialPackages = packageNames.filter((name) => (0, utils_1.isOfficialConnector)(name));
23
- const thirdPartyPackages = packageNames.filter((name) => !(0, utils_1.isOfficialConnector)(name));
15
+ const packages = await getConnectorPackagesFrom(inputPath);
16
+ const officialPackages = packages.filter(({ name }) => isOfficialConnector(name));
17
+ const thirdPartyPackages = packages.filter(({ name }) => !isOfficialConnector(name));
24
18
  logConnectorNames('official'.toUpperCase(), officialPackages);
25
19
  logConnectorNames('3rd-party'.toUpperCase(), thirdPartyPackages);
26
20
  },
27
21
  };
28
- exports.default = list;
22
+ export default list;
@@ -1,12 +1,7 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const chalk_1 = __importDefault(require("chalk"));
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
8
- const utilities_1 = require("../../utilities");
9
- const utils_1 = require("./utils");
1
+ import fs from 'fs/promises';
2
+ import chalk from 'chalk';
3
+ import { log } from '../../utilities.js';
4
+ import { getConnectorPackagesFrom } from './utils.js';
10
5
  const remove = {
11
6
  command: ['remove [packages...]', 'rm', 'delete'],
12
7
  describe: 'Remove existing Logto connectors',
@@ -18,30 +13,30 @@ const remove = {
18
13
  }),
19
14
  handler: async ({ path: inputPath, packages: packageNames }) => {
20
15
  if (!packageNames?.length) {
21
- utilities_1.log.error('No connector name provided');
16
+ log.error('No connector name provided');
22
17
  }
23
- const existingPackages = await (0, utils_1.getConnectorPackagesFrom)(inputPath);
18
+ const existingPackages = await getConnectorPackagesFrom(inputPath);
24
19
  const notFoundPackageNames = packageNames.filter((current) => !existingPackages.some(({ name }) => current === name));
25
20
  if (notFoundPackageNames.length > 0) {
26
- utilities_1.log.error(`Cannot remove ${notFoundPackageNames
27
- .map((name) => chalk_1.default.green(name))
21
+ log.error(`Cannot remove ${notFoundPackageNames
22
+ .map((name) => chalk.green(name))
28
23
  .join(', ')}: not found in your Logto instance directory`);
29
24
  }
30
25
  const okSymbol = Symbol('Connector removed');
31
26
  const result = await Promise.all(packageNames.map(async (current) => {
32
27
  const packageInfo = existingPackages.find(({ name }) => name === current);
33
28
  try {
34
- await fs_extra_1.default.remove(packageInfo?.path ?? '');
29
+ await fs.rm(packageInfo?.path ?? '', { force: true, recursive: true });
35
30
  return okSymbol;
36
31
  }
37
32
  catch (error) {
38
- utilities_1.log.warn(`Error while removing ${chalk_1.default.green(packageInfo?.name)}`);
39
- utilities_1.log.warn(error);
33
+ log.warn(`Error while removing ${chalk.green(packageInfo?.name)}`);
34
+ log.warn(error);
40
35
  return error;
41
36
  }
42
37
  }));
43
38
  const errorCount = result.filter((value) => value !== okSymbol).length;
44
- utilities_1.log.info(`Removed ${result.length - errorCount} connectors`);
39
+ log.info(`Removed ${result.length - errorCount} connectors`);
45
40
  },
46
41
  };
47
- exports.default = remove;
42
+ export default remove;
@@ -15,6 +15,6 @@ export declare const npmPackResultGuard: z.ZodArray<z.ZodObject<{
15
15
  export declare const inquireInstancePath: (initialPath?: string) => Promise<string>;
16
16
  export declare const normalizePackageName: (name: string) => string;
17
17
  export declare const isOfficialConnector: (packageName: string) => boolean;
18
- export declare const getConnectorPackagesFrom: (instancePath?: string) => Promise<import("../../utilities").ConnectorPackage[]>;
18
+ export declare const getConnectorPackagesFrom: (instancePath?: string) => Promise<import("../../utilities.js").ConnectorPackage[]>;
19
19
  export declare const addConnectors: (instancePath: string, packageNames: string[]) => Promise<void>;
20
20
  export declare const addOfficialConnectors: (instancePath: string) => Promise<void>;
@@ -1,42 +1,37 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.addOfficialConnectors = exports.addConnectors = exports.getConnectorPackagesFrom = exports.isOfficialConnector = exports.normalizePackageName = exports.inquireInstancePath = exports.npmPackResultGuard = void 0;
7
- const child_process_1 = require("child_process");
8
- const fs_1 = require("fs");
9
- const promises_1 = require("fs/promises");
10
- const path_1 = __importDefault(require("path"));
11
- const util_1 = require("util");
12
- const essentials_1 = require("@silverhand/essentials");
13
- const chalk_1 = __importDefault(require("chalk"));
14
- const fs_extra_1 = require("fs-extra");
15
- const inquirer_1 = __importDefault(require("inquirer"));
16
- const p_retry_1 = __importDefault(require("p-retry"));
17
- const tar_1 = __importDefault(require("tar"));
18
- const zod_1 = require("zod");
19
- const constants_1 = require("../../constants");
20
- const utilities_1 = require("../../utilities");
21
- const utils_1 = require("../install/utils");
1
+ import { exec } from 'child_process';
2
+ import { existsSync } from 'fs';
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+ import { promisify } from 'util';
6
+ import { assert, conditionalString, trySafe } from '@silverhand/essentials';
7
+ import chalk from 'chalk';
8
+ import { got } from 'got';
9
+ import inquirer from 'inquirer';
10
+ import pLimit from 'p-limit';
11
+ import pRetry from 'p-retry';
12
+ import tar from 'tar';
13
+ import { z } from 'zod';
14
+ import { connectorDirectory } from '../../constants.js';
15
+ import { getConnectorPackagesFromDirectory, isTty, log, oraPromise } from '../../utilities.js';
16
+ import { defaultPath } from '../install/utils.js';
22
17
  const coreDirectory = 'packages/core';
23
- const execPromise = (0, util_1.promisify)(child_process_1.exec);
24
- exports.npmPackResultGuard = zod_1.z
18
+ const execPromise = promisify(exec);
19
+ export const npmPackResultGuard = z
25
20
  .object({
26
- name: zod_1.z.string(),
27
- version: zod_1.z.string(),
28
- filename: zod_1.z.string(),
21
+ name: z.string(),
22
+ version: z.string(),
23
+ filename: z.string(),
29
24
  })
30
25
  .array();
31
- const buildPathErrorMessage = (value) => `The path ${chalk_1.default.green(value)} does not contain a Logto instance, please try another.`;
26
+ const buildPathErrorMessage = (value) => `The path ${chalk.green(value)} does not contain a Logto instance, please try another.`;
32
27
  const validatePath = async (value) => {
33
- const corePackageJsonPath = path_1.default.resolve(path_1.default.join(value, coreDirectory, 'package.json'));
34
- if (!(0, fs_1.existsSync)(corePackageJsonPath)) {
28
+ const corePackageJsonPath = path.resolve(path.join(value, coreDirectory, 'package.json'));
29
+ if (!existsSync(corePackageJsonPath)) {
35
30
  return buildPathErrorMessage(value);
36
31
  }
37
- const packageJson = await (0, promises_1.readFile)(corePackageJsonPath, { encoding: 'utf8' });
38
- const packageName = await zod_1.z
39
- .object({ name: zod_1.z.string() })
32
+ const packageJson = await fs.readFile(corePackageJsonPath, { encoding: 'utf8' });
33
+ const packageName = await z
34
+ .object({ name: z.string() })
40
35
  .parseAsync(JSON.parse(packageJson))
41
36
  .then(({ name }) => name)
42
37
  .catch(() => '');
@@ -45,17 +40,17 @@ const validatePath = async (value) => {
45
40
  }
46
41
  return true;
47
42
  };
48
- const inquireInstancePath = async (initialPath) => {
43
+ export const inquireInstancePath = async (initialPath) => {
49
44
  const inquire = async () => {
50
- if (!(0, utilities_1.isTty)()) {
51
- (0, essentials_1.assert)(initialPath, new Error('Path is missing'));
45
+ if (!isTty()) {
46
+ assert(initialPath, new Error('Path is missing'));
52
47
  return initialPath;
53
48
  }
54
- const { instancePath } = await inquirer_1.default.prompt({
49
+ const { instancePath } = await inquirer.prompt({
55
50
  name: 'instancePath',
56
51
  message: 'Where is your Logto instance?',
57
52
  type: 'input',
58
- default: utils_1.defaultPath,
53
+ default: defaultPath,
59
54
  filter: (value) => value.trim(),
60
55
  validate: validatePath,
61
56
  }, { instancePath: initialPath });
@@ -64,86 +59,105 @@ const inquireInstancePath = async (initialPath) => {
64
59
  const instancePath = await inquire();
65
60
  const validated = await validatePath(instancePath);
66
61
  if (validated !== true) {
67
- utilities_1.log.error(validated);
62
+ log.error(validated);
68
63
  }
69
64
  return instancePath;
70
65
  };
71
- exports.inquireInstancePath = inquireInstancePath;
72
66
  const packagePrefix = 'connector-';
73
- const normalizePackageName = (name) => name
67
+ export const normalizePackageName = (name) => name
74
68
  .split('/')
75
69
  // Prepend prefix to the last fragment if needed
76
70
  .map((fragment, index, array) => index === array.length - 1 && !fragment.startsWith(packagePrefix) && !fragment.startsWith('@')
77
71
  ? packagePrefix + fragment
78
72
  : fragment)
79
73
  .join('/');
80
- exports.normalizePackageName = normalizePackageName;
81
- const getConnectorDirectory = (instancePath) => path_1.default.join(instancePath, coreDirectory, constants_1.connectorDirectory);
82
- const isOfficialConnector = (packageName) => packageName.startsWith('@logto/connector-');
83
- exports.isOfficialConnector = isOfficialConnector;
84
- const getConnectorPackagesFrom = async (instancePath) => {
85
- const directory = getConnectorDirectory(await (0, exports.inquireInstancePath)(instancePath));
86
- return (0, utilities_1.getConnectorPackagesFromDirectory)(directory);
74
+ const getConnectorDirectory = (instancePath) => path.join(instancePath, coreDirectory, connectorDirectory);
75
+ export const isOfficialConnector = (packageName) => packageName.startsWith('@logto/connector-');
76
+ export const getConnectorPackagesFrom = async (instancePath) => {
77
+ const directory = getConnectorDirectory(await inquireInstancePath(instancePath));
78
+ const packages = await trySafe(getConnectorPackagesFromDirectory(directory));
79
+ if (!packages || packages.length === 0) {
80
+ throw new Error('No connector found');
81
+ }
82
+ return packages;
87
83
  };
88
- exports.getConnectorPackagesFrom = getConnectorPackagesFrom;
89
- const addConnectors = async (instancePath, packageNames) => {
84
+ export const addConnectors = async (instancePath, packageNames) => {
90
85
  const cwd = getConnectorDirectory(instancePath);
91
- if (!(0, fs_1.existsSync)(cwd)) {
92
- await (0, promises_1.mkdir)(cwd);
86
+ if (!existsSync(cwd)) {
87
+ await fs.mkdir(cwd, { recursive: true });
93
88
  }
94
- utilities_1.log.info('Fetch connector metadata');
89
+ log.info('Fetch connector metadata');
90
+ const limit = pLimit(10);
95
91
  const results = await Promise.all(packageNames
96
- .map((name) => (0, exports.normalizePackageName)(name))
92
+ .map((name) => normalizePackageName(name))
97
93
  .map(async (packageName) => {
98
94
  const run = async () => {
99
95
  const { stdout } = await execPromise(`npm pack ${packageName} --json`, { cwd });
100
- const result = exports.npmPackResultGuard.parse(JSON.parse(stdout));
96
+ const result = npmPackResultGuard.parse(JSON.parse(stdout));
101
97
  if (!result[0]) {
102
- throw new Error(`Unable to execute ${chalk_1.default.green('npm pack')} on package ${chalk_1.default.green(packageName)}`);
98
+ throw new Error(`Unable to execute ${chalk.green('npm pack')} on package ${chalk.green(packageName)}`);
103
99
  }
104
- const { filename, name } = result[0];
100
+ const { filename, name, version } = result[0];
105
101
  const escapedFilename = filename.replace(/\//g, '-').replace(/@/g, '');
106
- const tarPath = path_1.default.join(cwd, escapedFilename);
107
- const packageDirectory = path_1.default.join(cwd, name.replace(/\//g, '-'));
108
- await (0, fs_extra_1.remove)(packageDirectory);
109
- await (0, fs_extra_1.ensureDir)(packageDirectory);
110
- await tar_1.default.extract({ cwd: packageDirectory, file: tarPath, strip: 1 });
111
- await (0, promises_1.unlink)(tarPath);
112
- utilities_1.log.succeed(`Added ${chalk_1.default.green(name)}`);
102
+ const tarPath = path.join(cwd, escapedFilename);
103
+ const packageDirectory = path.join(cwd, name.replace(/\//g, '-'));
104
+ await fs.rm(packageDirectory, { force: true, recursive: true });
105
+ await fs.mkdir(packageDirectory, { recursive: true });
106
+ await tar.extract({ cwd: packageDirectory, file: tarPath, strip: 1 });
107
+ await fs.unlink(tarPath);
108
+ log.succeed(`Added ${chalk.green(name)} v${version}`);
113
109
  };
114
- try {
115
- await (0, p_retry_1.default)(run, { retries: 2 });
116
- }
117
- catch (error) {
118
- console.warn(`[${packageName}]`, error);
119
- return packageName;
120
- }
110
+ return limit(async () => {
111
+ try {
112
+ await pRetry(run, { retries: 2 });
113
+ }
114
+ catch (error) {
115
+ console.warn(`[${packageName}]`, error);
116
+ return packageName;
117
+ }
118
+ });
121
119
  }));
122
120
  const errorPackages = results.filter(Boolean);
123
121
  const errorCount = errorPackages.length;
124
- utilities_1.log.info(errorCount
125
- ? `Finished with ${errorCount} error${(0, essentials_1.conditionalString)(errorCount > 1 && 's')}.`
122
+ log.info(errorCount
123
+ ? `Finished with ${errorCount} error${conditionalString(errorCount > 1 && 's')}.`
126
124
  : 'Finished');
127
125
  if (errorCount) {
128
- utilities_1.log.warn('Failed to add ' + errorPackages.map((name) => chalk_1.default.green(name)).join(', '));
126
+ log.warn('Failed to add ' + errorPackages.map((name) => chalk.green(name)).join(', '));
129
127
  }
130
128
  };
131
- exports.addConnectors = addConnectors;
132
129
  const officialConnectorPrefix = '@logto/connector-';
133
130
  const fetchOfficialConnectorList = async () => {
134
- const { stdout } = await execPromise(`npm search ${officialConnectorPrefix} --json`);
135
- const packages = zod_1.z
136
- .object({ name: zod_1.z.string() })
137
- .transform(({ name }) => name)
138
- .array()
139
- .parse(JSON.parse(stdout));
140
- return packages.filter((name) => ['mock', 'kit'].every((excluded) => !name.slice(officialConnectorPrefix.length).startsWith(excluded)));
131
+ const fetchList = async (from = 0, size = 20) => {
132
+ const parameters = new URLSearchParams({
133
+ text: officialConnectorPrefix,
134
+ from: String(from),
135
+ size: String(size),
136
+ });
137
+ return got(`https://registry.npmjs.org/-/v1/search?${parameters.toString()}`).json();
138
+ };
139
+ const packages = [];
140
+ // Disable lint rules for business need
141
+ // eslint-disable-next-line @silverhand/fp/no-let, @silverhand/fp/no-mutation
142
+ for (let page = 0;; ++page) {
143
+ // eslint-disable-next-line no-await-in-loop
144
+ const { objects } = await fetchList(page * 20, 20);
145
+ // eslint-disable-next-line @silverhand/fp/no-mutating-methods
146
+ packages.push(...objects
147
+ .filter(({ package: { name, scope } }) => scope === 'logto' &&
148
+ ['mock', 'kit'].every((excluded) => !name.slice(officialConnectorPrefix.length).startsWith(excluded)))
149
+ .map(({ package: data }) => data));
150
+ if (objects.length < 20) {
151
+ break;
152
+ }
153
+ }
154
+ return packages;
141
155
  };
142
- const addOfficialConnectors = async (instancePath) => {
143
- const packages = await (0, utilities_1.oraPromise)(fetchOfficialConnectorList(), {
156
+ export const addOfficialConnectors = async (instancePath) => {
157
+ const packages = await oraPromise(fetchOfficialConnectorList(), {
144
158
  text: 'Fetch official connector list',
145
- prefixText: chalk_1.default.blue('[info]'),
159
+ prefixText: chalk.blue('[info]'),
146
160
  });
147
- await (0, exports.addConnectors)(instancePath, packages);
161
+ log.info(`Found ${packages.length} official connectors`);
162
+ await addConnectors(instancePath, packages.map(({ name }) => name));
148
163
  };
149
- exports.addOfficialConnectors = addOfficialConnectors;
@@ -1,7 +1,6 @@
1
1
  import type { DatabasePool } from 'slonik';
2
2
  import type { CommandModule } from 'yargs';
3
- import type { AlterationFile } from './type';
4
- export declare const getAlterationFiles: () => Promise<AlterationFile[]>;
3
+ import type { AlterationFile } from './type.js';
5
4
  export declare const getLatestAlterationTimestamp: () => Promise<number>;
6
5
  export declare const getUndeployedAlterations: (pool: DatabasePool) => Promise<AlterationFile[]>;
7
6
  declare const alteration: CommandModule<unknown, {
@@ -1,122 +1,52 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.getUndeployedAlterations = exports.getLatestAlterationTimestamp = exports.getAlterationFiles = void 0;
30
- const path_1 = __importDefault(require("path"));
31
- const shared_1 = require("@logto/shared");
32
- const essentials_1 = require("@silverhand/essentials");
33
- const chalk_1 = __importDefault(require("chalk"));
34
- const fs_extra_1 = require("fs-extra");
35
- const database_1 = require("../../../database");
36
- const logto_config_1 = require("../../../queries/logto-config");
37
- const utilities_1 = require("../../../utilities");
38
- const version_1 = require("./version");
39
- const alterationFilenameRegex = /-(\d+)-?.*\.js$/;
40
- const getTimestampFromFilename = (filename) => {
41
- const match = alterationFilenameRegex.exec(filename);
42
- if (!match?.[1]) {
43
- throw new Error(`Can not get timestamp: ${filename}`);
44
- }
45
- return Number(match[1]);
46
- };
1
+ import { conditionalString } from '@silverhand/essentials';
2
+ import chalk from 'chalk';
3
+ import { createPoolFromConfig } from '../../../database.js';
4
+ import { getCurrentDatabaseAlterationTimestamp, updateDatabaseTimestamp, } from '../../../queries/logto-config.js';
5
+ import { log } from '../../../utilities.js';
6
+ import { getAlterationFiles, getTimestampFromFilename } from './utils.js';
7
+ import { chooseAlterationsByVersion } from './version.js';
47
8
  const importAlterationScript = async (filePath) => {
48
9
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
49
- const module = await Promise.resolve().then(() => __importStar(require(filePath)));
10
+ const module = await import(filePath);
50
11
  // eslint-disable-next-line no-restricted-syntax
51
12
  return module.default;
52
13
  };
53
- const getAlterationFiles = async () => {
54
- const alterationDirectory = (0, utilities_1.getPathInModule)('@logto/schemas', 'alterations-js');
55
- /**
56
- * We copy all alteration scripts to the CLI package root directory,
57
- * since they need a proper context that includes required dependencies (such as slonik) in `node_modules/`.
58
- * While the original `@logto/schemas` may remove them in production.
59
- */
60
- const packageDirectory = await (0, shared_1.findPackage)(
61
- // Until we migrate to ESM
62
- // eslint-disable-next-line unicorn/prefer-module
63
- __dirname);
64
- const localAlterationDirectory = path_1.default.resolve(
65
- // Until we migrate to ESM
66
- // eslint-disable-next-line unicorn/prefer-module
67
- packageDirectory ?? __dirname, 'alteration-scripts');
68
- if (!(0, fs_extra_1.existsSync)(alterationDirectory)) {
69
- return [];
70
- }
71
- // We need to copy alteration files to execute in the CLI context to make `slonik` available
72
- await (0, fs_extra_1.remove)(localAlterationDirectory);
73
- await (0, fs_extra_1.copy)(alterationDirectory, localAlterationDirectory);
74
- const directory = await (0, fs_extra_1.readdir)(localAlterationDirectory);
75
- const files = directory.filter((file) => alterationFilenameRegex.test(file));
76
- return files
77
- .slice()
78
- .sort((file1, file2) => getTimestampFromFilename(file1) - getTimestampFromFilename(file2))
79
- .map((filename) => ({ path: path_1.default.join(localAlterationDirectory, filename), filename }));
80
- };
81
- exports.getAlterationFiles = getAlterationFiles;
82
- const getLatestAlterationTimestamp = async () => {
83
- const files = await (0, exports.getAlterationFiles)();
14
+ export const getLatestAlterationTimestamp = async () => {
15
+ const files = await getAlterationFiles();
84
16
  const lastFile = files[files.length - 1];
85
17
  if (!lastFile) {
86
18
  return 0;
87
19
  }
88
20
  return getTimestampFromFilename(lastFile.filename);
89
21
  };
90
- exports.getLatestAlterationTimestamp = getLatestAlterationTimestamp;
91
- const getUndeployedAlterations = async (pool) => {
92
- const databaseTimestamp = await (0, logto_config_1.getCurrentDatabaseAlterationTimestamp)(pool);
93
- const files = await (0, exports.getAlterationFiles)();
22
+ export const getUndeployedAlterations = async (pool) => {
23
+ const databaseTimestamp = await getCurrentDatabaseAlterationTimestamp(pool);
24
+ const files = await getAlterationFiles();
94
25
  return files.filter(({ filename }) => getTimestampFromFilename(filename) > databaseTimestamp);
95
26
  };
96
- exports.getUndeployedAlterations = getUndeployedAlterations;
97
27
  const deployAlteration = async (pool, { path: filePath, filename }) => {
98
28
  const { up } = await importAlterationScript(filePath);
99
29
  try {
100
30
  await pool.transaction(async (connection) => {
101
31
  await up(connection);
102
- await (0, logto_config_1.updateDatabaseTimestamp)(connection, getTimestampFromFilename(filename));
32
+ await updateDatabaseTimestamp(connection, getTimestampFromFilename(filename));
103
33
  });
104
34
  }
105
35
  catch (error) {
106
36
  console.error(error);
107
37
  await pool.end();
108
- utilities_1.log.error(`Error ocurred during running alteration ${chalk_1.default.blue(filename)}.\n\n` +
38
+ log.error(`Error ocurred during running alteration ${chalk.blue(filename)}.\n\n` +
109
39
  " This alteration didn't change anything since it was in a transaction.\n" +
110
40
  ' Try to fix the error and deploy again.');
111
41
  }
112
- utilities_1.log.info(`Run alteration ${filename} succeeded`);
42
+ log.info(`Run alteration ${filename} succeeded`);
113
43
  };
114
44
  const alteration = {
115
45
  command: ['alteration <action> [target]', 'alt', 'alter'],
116
46
  describe: 'Perform database alteration',
117
47
  builder: (yargs) => yargs
118
48
  .positional('action', {
119
- describe: 'The action to perform, now it only accepts `deploy`',
49
+ describe: 'The action to perform, now it only accepts `deploy` and `list`',
120
50
  type: 'string',
121
51
  demandOption: true,
122
52
  })
@@ -125,18 +55,26 @@ const alteration = {
125
55
  type: 'string',
126
56
  }),
127
57
  handler: async ({ action, target }) => {
128
- if (action !== 'deploy') {
129
- utilities_1.log.error('Unsupported action');
58
+ if (action === 'list') {
59
+ const files = await getAlterationFiles();
60
+ for (const file of files) {
61
+ console.log(file.filename);
62
+ }
130
63
  }
131
- const pool = await (0, database_1.createPoolFromConfig)();
132
- const alterations = await (0, version_1.chooseAlterationsByVersion)(await (0, exports.getUndeployedAlterations)(pool), target);
133
- utilities_1.log.info(`Found ${alterations.length} alteration${(0, essentials_1.conditionalString)(alterations.length > 1 && 's')} to deploy`);
134
- // The await inside the loop is intended, alterations should run in order
135
- for (const alteration of alterations) {
136
- // eslint-disable-next-line no-await-in-loop
137
- await deployAlteration(pool, alteration);
64
+ else if (action === 'deploy') {
65
+ const pool = await createPoolFromConfig();
66
+ const alterations = await chooseAlterationsByVersion(await getUndeployedAlterations(pool), target);
67
+ log.info(`Found ${alterations.length} alteration${conditionalString(alterations.length > 1 && 's')} to deploy`);
68
+ // The await inside the loop is intended, alterations should run in order
69
+ for (const alteration of alterations) {
70
+ // eslint-disable-next-line no-await-in-loop
71
+ await deployAlteration(pool, alteration);
72
+ }
73
+ await pool.end();
74
+ }
75
+ else {
76
+ log.error('Unsupported action');
138
77
  }
139
- await pool.end();
140
78
  },
141
79
  };
142
- exports.default = alteration;
80
+ export default alteration;