mongo-data-anonymizer 0.2.1 → 0.3.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 (55) hide show
  1. package/README.md +268 -18
  2. package/dist/anonymization/anonymize.d.ts +50 -8
  3. package/dist/anonymization/anonymize.js +747 -96
  4. package/dist/anonymization/anonymize.js.map +1 -1
  5. package/dist/anonymization/collections.d.ts +12 -0
  6. package/dist/anonymization/collections.js +14 -0
  7. package/dist/anonymization/collections.js.map +1 -0
  8. package/dist/anonymization/database.d.ts +69 -9
  9. package/dist/anonymization/database.js +233 -61
  10. package/dist/anonymization/database.js.map +1 -1
  11. package/dist/anonymization/randomizer.d.ts +8 -0
  12. package/dist/anonymization/randomizer.js +32 -0
  13. package/dist/anonymization/randomizer.js.map +1 -0
  14. package/dist/anonymization/rules.d.ts +60 -0
  15. package/dist/anonymization/rules.js +193 -0
  16. package/dist/anonymization/rules.js.map +1 -0
  17. package/dist/cli.js +11 -4
  18. package/dist/cli.js.map +1 -1
  19. package/dist/config.d.ts +7 -0
  20. package/dist/config.js +182 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/index.d.ts +5 -0
  23. package/dist/index.js +6 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/logger.d.ts +7 -0
  26. package/dist/logger.js +14 -0
  27. package/dist/logger.js.map +1 -0
  28. package/dist/progress.d.ts +25 -0
  29. package/dist/progress.js +74 -0
  30. package/dist/progress.js.map +1 -0
  31. package/dist/run.d.ts +62 -0
  32. package/dist/run.js +365 -0
  33. package/dist/run.js.map +1 -0
  34. package/package.json +43 -29
  35. package/.eslintignore +0 -1
  36. package/.eslintrc.js +0 -25
  37. package/.github/workflows/release.yml +0 -37
  38. package/.github/workflows/test.yml +0 -22
  39. package/.nvmrc +0 -1
  40. package/.prettierrc +0 -7
  41. package/dist/main.d.ts +0 -1
  42. package/dist/main.js +0 -95
  43. package/dist/main.js.map +0 -1
  44. package/dist/utils/field-utils.d.ts +0 -1
  45. package/dist/utils/field-utils.js +0 -18
  46. package/dist/utils/field-utils.js.map +0 -1
  47. package/jest.config.js +0 -8
  48. package/src/anonymization/anonymize.ts +0 -116
  49. package/src/anonymization/database.ts +0 -50
  50. package/src/cli.ts +0 -4
  51. package/src/main.ts +0 -98
  52. package/src/utils/field-utils.ts +0 -16
  53. package/test/anonymizer/anonymizer.test.ts +0 -164
  54. package/test/utils/field-utils.test.ts +0 -31
  55. package/tsconfig.json +0 -14
package/dist/run.js ADDED
@@ -0,0 +1,365 @@
1
+ import { BSON, MongoOperationTimeoutError, } from 'mongodb';
2
+ import { Anonymizer } from "./anonymization/anonymize.js";
3
+ import { decideCollectionAction, } from "./anonymization/collections.js";
4
+ import { Database, looksLikeSameDatabase, writesReachSource, } from "./anonymization/database.js";
5
+ import { CollectionRules, parseFieldRule, rulesForCollection, } from "./anonymization/rules.js";
6
+ import { consoleLogger } from "./logger.js";
7
+ import { DEFAULT_PROGRESS, Progress, describeProgress, formatDuration, } from "./progress.js";
8
+ /**
9
+ * Batches are also cut at this size, so collections of large documents
10
+ * (e.g. rendered HTML) don't hold hundreds of MB per batch in memory.
11
+ */
12
+ const MAX_BATCH_BYTES = 16 * 1024 * 1024;
13
+ const DEFAULT_SAMPLE_SIZE = 1000;
14
+ export async function run(config, logger = consoleLogger) {
15
+ const sampleSize = config.sampleSize ?? DEFAULT_SAMPLE_SIZE;
16
+ if (!Number.isInteger(sampleSize) || sampleSize < 0) {
17
+ throw new Error(`--sampleSize must be 0 or a positive integer, got ${sampleSize}`);
18
+ }
19
+ if (!Number.isInteger(config.batchSize) || config.batchSize < 1) {
20
+ throw new Error(`--batchSize must be a positive integer, got ${config.batchSize}`);
21
+ }
22
+ const warnings = [];
23
+ const warn = (message) => {
24
+ warnings.push(message);
25
+ logger.warn(message);
26
+ };
27
+ const rules = config.fieldList.map(parseFieldRule);
28
+ const anonymizer = new Anonymizer({
29
+ secret: config.secret,
30
+ strict: config.strict ?? false,
31
+ scrubEmails: config.scrubEmails ?? true,
32
+ }).onWarning(warn);
33
+ anonymizer.validateRules(rules);
34
+ if (!config.secret) {
35
+ warn('No secret given: using a random one. Fake values are consistent within this run but will differ on the next one; set ANONYMIZER_SECRET (or --secret) for reproducible output.');
36
+ }
37
+ const source = new Database(config.sourceUri, 'source');
38
+ const target = new Database(config.targetUri, 'target');
39
+ const progressOptions = { ...DEFAULT_PROGRESS, ...config.progress };
40
+ const progress = new Progress(progressOptions, warn);
41
+ try {
42
+ progress.start();
43
+ progress.operation('connecting');
44
+ await source.connect();
45
+ await target.connect();
46
+ const indexOptions = await indexBuildOptions(target, config);
47
+ await checkNotSameDatabase(source, target, config, warn);
48
+ const sourceCollections = await source.listCollections();
49
+ checkCollectionNames(sourceCollections, config, rules);
50
+ const planned = sourceCollections.map((info) => ({
51
+ info,
52
+ action: decideCollectionAction(info.name, config),
53
+ }));
54
+ const written = planned.filter(({ action }) => action !== 'skip');
55
+ const anonymized = planned.filter(({ info, action }) => action === 'anonymize' && info.type !== 'view');
56
+ if (written.length === 0) {
57
+ warn('Nothing to write: every collection is skipped. Check --collectionList and --ignoreCollections.');
58
+ }
59
+ else if (anonymized.length === 0) {
60
+ throw new Error('No collection would be anonymized; everything selected would be copied as-is. Check --collectionList and --ignoreCollections.');
61
+ }
62
+ logger.info(`Fields anonymized in every collection: ${describeRules(rules)}`);
63
+ await prepareTarget(target, written, config, logger);
64
+ const reports = [];
65
+ for (const { info, action } of planned) {
66
+ const collectionRules = rulesForCollection(rules, info.name);
67
+ const report = {
68
+ name: info.name,
69
+ type: info.type,
70
+ action,
71
+ documents: 0,
72
+ anonymizedFields: action === 'anonymize' ? collectionRules.fields : [],
73
+ };
74
+ reports.push(report);
75
+ if (info.type === 'view') {
76
+ continue; // Recreated once every collection is in place.
77
+ }
78
+ if (action === 'skip') {
79
+ logger.info(`Skipping ${info.name}.`);
80
+ continue;
81
+ }
82
+ if (config.dryRun) {
83
+ report.documents = await source.estimatedCount(info.name);
84
+ report.unmatchedPersonalKeys = await findUnmatchedPersonalKeys(source, info.name, anonymizer, action === 'anonymize' ? collectionRules : new CollectionRules([]), sampleSize, progress);
85
+ logger.info(`[dry run] Would ${action} ${info.name} (~${report.documents} documents)` +
86
+ (action === 'anonymize'
87
+ ? collectionSpecificNote(rules, info.name)
88
+ : ''));
89
+ if (report.unmatchedPersonalKeys.length > 0) {
90
+ warn(`${info.name}: these keys look like personal data but would be written unchanged: ${report.unmatchedPersonalKeys.join(', ')}`);
91
+ }
92
+ continue;
93
+ }
94
+ logger.info(`${action === 'anonymize' ? 'Anonymizing' : 'Copying'} ${info.name}...`);
95
+ const started = Date.now();
96
+ progress.operation(`createCollection ${info.name}`);
97
+ await createLike(target, info, warn);
98
+ report.documents = await copyDocuments({
99
+ source,
100
+ target,
101
+ name: info.name,
102
+ batchSize: config.batchSize,
103
+ transform: (batch) => action === 'anonymize'
104
+ ? anonymizer.anonymizeBatch(batch, collectionRules)
105
+ : batch,
106
+ progress,
107
+ logger,
108
+ reportEveryMs: progressOptions.reportEveryMs,
109
+ });
110
+ await copyIndexes(source, target, info.name, indexOptions, progress, warn);
111
+ const elapsed = Date.now() - started;
112
+ logger.info(`Done with ${info.name}: ${describeProgress(report.documents, 0, elapsed)} in ${formatDuration(elapsed)}.`);
113
+ }
114
+ progress.operation('recreating views');
115
+ for (const { info, action } of written.filter(({ info }) => info.type === 'view')) {
116
+ if (config.dryRun) {
117
+ logger.info(`[dry run] Would recreate view ${info.name} (${action}).`);
118
+ continue;
119
+ }
120
+ try {
121
+ await target.createCollection(info.name, info.options);
122
+ logger.info(`Recreated view ${info.name}.`);
123
+ }
124
+ catch (error) {
125
+ warn(`Could not recreate view ${info.name}: ${error.message}`);
126
+ }
127
+ }
128
+ logger.info(config.dryRun
129
+ ? 'Dry run finished; nothing was written.'
130
+ : `Anonymization finished${warnings.length ? ` with ${warnings.length} warning(s)` : ''}.`);
131
+ return { collections: reports, warnings };
132
+ }
133
+ finally {
134
+ progress.stop();
135
+ await Promise.allSettled([source.close(), target.close()]);
136
+ }
137
+ }
138
+ async function checkNotSameDatabase(source, target, config, warn) {
139
+ const names = `source "${source.name}", target "${target.name}"`;
140
+ if (!config.assumeDifferentTarget) {
141
+ const reason = await looksLikeSameDatabase(source, target);
142
+ if (reason) {
143
+ throw new Error((reason === 'uuid'
144
+ ? `The target shares collection UUIDs with the source (${names}). Either they are the same database, or the target is a physical copy of the source (e.g. restored with mongorestore --preserveUUID).`
145
+ : `The target has the same database name on the same hosts as the source (${names}), so they look like the same database.`) +
146
+ ' If you are sure the target is a different database, pass --assumeDifferentTarget; a real run still checks that writes to the target do not reach the source.');
147
+ }
148
+ }
149
+ const stale = await target.listProbes();
150
+ if (stale.length > 0) {
151
+ warn(`The target contains probe collections left by an interrupted run; you can drop them: ${stale.join(', ')}`);
152
+ }
153
+ if (!config.dryRun && (await writesReachSource(source, target, warn))) {
154
+ throw new Error(`Writes to the target reach the source (${names}): they are the same database. Refusing to write into the source.`);
155
+ }
156
+ }
157
+ function formatRule({ field, replacement }) {
158
+ return replacement === null ? field : `${field}:${replacement}`;
159
+ }
160
+ function describeRules(rules) {
161
+ const global = rules.filter(({ collection }) => collection === null);
162
+ return global.map(formatRule).join(', ') || '(none)';
163
+ }
164
+ /** The collection-scoped rules for one collection, for the dry-run log. */
165
+ function collectionSpecificNote(rules, name) {
166
+ const own = rules.filter(({ collection }) => collection === name);
167
+ return own.length > 0 ? `, plus: ${own.map(formatRule).join(', ')}` : '';
168
+ }
169
+ /**
170
+ * Every collection named in the options must exist in the source: a typo in
171
+ * --collectionList combined with --copyNonAnonymized would otherwise copy
172
+ * the collection it was meant to anonymize as-is.
173
+ */
174
+ function checkCollectionNames(sourceCollections, config, rules) {
175
+ const existing = new Set(sourceCollections.map(({ name }) => name));
176
+ const unknown = (names) => [...new Set(names)].filter((name) => !existing.has(name));
177
+ const problems = [
178
+ ['--collectionList', unknown(config.collectionList)],
179
+ ['--ignoreCollections', unknown(config.ignoreCollections)],
180
+ [
181
+ '--fieldList',
182
+ unknown(rules.flatMap(({ collection }) => collection ?? [])),
183
+ ],
184
+ ];
185
+ const messages = problems
186
+ .filter(([, names]) => names.length > 0)
187
+ .map(([option, names]) => `${option}: ${names.join(', ')}`);
188
+ if (messages.length > 0) {
189
+ throw new Error(`Unknown collections in the source (${messages.join('; ')}). Nothing was written.`);
190
+ }
191
+ }
192
+ async function findUnmatchedPersonalKeys(source, name, anonymizer, rules, sampleSize, progress) {
193
+ const found = new Set();
194
+ progress.operation(`sampling ${name}`);
195
+ for await (const document of source.sample(name, sampleSize)) {
196
+ progress.activity();
197
+ for (const key of anonymizer.findUnmatchedPersonalKeys(document, rules)) {
198
+ found.add(key);
199
+ }
200
+ }
201
+ return [...found].sort();
202
+ }
203
+ /**
204
+ * Checks every target collection up front, so a run never stops halfway
205
+ * because of leftovers from a previous one.
206
+ */
207
+ async function prepareTarget(target, written, config, logger) {
208
+ const existing = [];
209
+ for (const { info } of written) {
210
+ if (await target.collectionExists(info.name)) {
211
+ existing.push(info.name);
212
+ }
213
+ }
214
+ if (existing.length === 0) {
215
+ return;
216
+ }
217
+ if (!config.dropTarget) {
218
+ throw new Error(`The target already contains: ${existing.join(', ')}. Use --dropTarget to replace them.`);
219
+ }
220
+ for (const name of existing) {
221
+ if (config.dryRun) {
222
+ logger.info(`[dry run] Would drop target collection ${name}.`);
223
+ }
224
+ else {
225
+ await target.dropCollection(name);
226
+ logger.info(`Dropped target collection ${name}.`);
227
+ }
228
+ }
229
+ }
230
+ /** Creates the target collection with the source's options (validators, collation, time series, ...). */
231
+ async function createLike(target, info, warn) {
232
+ try {
233
+ await target.createCollection(info.name, info.options);
234
+ }
235
+ catch (error) {
236
+ warn(`Could not create ${info.name} with the source options (${error.message}); it is created with defaults.`);
237
+ }
238
+ }
239
+ /**
240
+ * Streams documents in batches (cut at `batchSize` documents or
241
+ * MAX_BATCH_BYTES), reading the next batch while the previous one is being
242
+ * inserted, and logs progress every `reportEveryMs`.
243
+ */
244
+ async function copyDocuments({ source, target, name, batchSize, transform, progress, logger, reportEveryMs, }) {
245
+ const total = await source.estimatedCount(name);
246
+ const started = Date.now();
247
+ let lastReport = started;
248
+ let count = 0;
249
+ let pending = Promise.resolve();
250
+ let batch = [];
251
+ let batchBytes = 0;
252
+ const flush = async () => {
253
+ const documents = transform(batch);
254
+ batch = [];
255
+ batchBytes = 0;
256
+ await pending;
257
+ pending = target
258
+ .insertMany(name, documents)
259
+ .then(() => progress.activity());
260
+ // Handled here so a failure while the next batch is being read isn't an
261
+ // unhandled rejection; it is still rethrown by the next flush or `finally`.
262
+ pending.catch(() => { });
263
+ count += documents.length;
264
+ const now = Date.now();
265
+ if (now - lastReport >= reportEveryMs) {
266
+ lastReport = now;
267
+ logger.info(`${name}: ${describeProgress(count, total, now - started)}`);
268
+ }
269
+ };
270
+ progress.operation(`copying ${name}`);
271
+ try {
272
+ for await (const document of source.find(name, batchSize)) {
273
+ progress.activity();
274
+ batch.push(document);
275
+ batchBytes += BSON.calculateObjectSize(document);
276
+ if (batch.length >= batchSize || batchBytes >= MAX_BATCH_BYTES) {
277
+ await flush();
278
+ }
279
+ }
280
+ await flush();
281
+ }
282
+ finally {
283
+ await pending;
284
+ }
285
+ return count;
286
+ }
287
+ async function indexBuildOptions(target, config) {
288
+ const quorum = config.indexCommitQuorum ?? 1;
289
+ const validQuorum = (typeof quorum === 'number' && Number.isInteger(quorum) && quorum >= 0) ||
290
+ quorum === 'majority' ||
291
+ quorum === 'votingMembers';
292
+ if (!validQuorum) {
293
+ throw new Error(`--indexCommitQuorum must be a number of members, "majority" or "votingMembers", got ${JSON.stringify(quorum)}`);
294
+ }
295
+ const timeoutSeconds = config.indexTimeoutSeconds ?? 900;
296
+ if (!Number.isFinite(timeoutSeconds) || timeoutSeconds < 0) {
297
+ throw new Error(`--indexTimeout must be 0 or a positive number of seconds, got ${timeoutSeconds}`);
298
+ }
299
+ return {
300
+ // timeoutMS (not maxTimeMS, which driver 7 no longer sends here) also
301
+ // makes the server abort the build when it expires.
302
+ ...(timeoutSeconds > 0 ? { timeoutMS: timeoutSeconds * 1000 } : {}),
303
+ // Standalones reject commitQuorum.
304
+ ...((await target.isReplicaSet()) ? { commitQuorum: quorum } : {}),
305
+ };
306
+ }
307
+ function isTimeout(error) {
308
+ const { code, codeName } = error;
309
+ return (error instanceof MongoOperationTimeoutError ||
310
+ code === 50 ||
311
+ codeName === 'MaxTimeMSExpired');
312
+ }
313
+ /**
314
+ * Builds a collection's indexes in one call, after the data is in, so a
315
+ * unique index that the fake values happen to violate costs a warning
316
+ * instead of a failed insert. One call builds all or none, so on an error
317
+ * each index is retried on its own to keep the ones that work, except after
318
+ * a timeout, which would only repeat the wait for every index.
319
+ */
320
+ async function copyIndexes(source, target, name, options, progress, warn) {
321
+ const indexes = await source.listIndexes(name);
322
+ if (indexes.length === 0)
323
+ return;
324
+ const names = indexes.map((index) => String(index.name)).join(', ');
325
+ // --indexTimeout covers the whole collection, retries included.
326
+ const deadline = options.timeoutMS === undefined
327
+ ? undefined
328
+ : Date.now() + options.timeoutMS;
329
+ const skipRest = (index) => warn(`Index ${index} on ${name} did not finish building within ${formatDuration(options.timeoutMS ?? 0)}; the remaining indexes of ${name} were skipped. Build them in the target by hand, or raise --indexTimeout.`);
330
+ progress.operation(`createIndexes on ${name} (${names})`);
331
+ try {
332
+ await target.createIndexes(name, indexes, options);
333
+ progress.activity();
334
+ return;
335
+ }
336
+ catch (error) {
337
+ if (isTimeout(error)) {
338
+ warn(`Indexes on ${name} did not finish building within ${formatDuration(options.timeoutMS ?? 0)} and were skipped: ${names}. Build them in the target by hand, or raise --indexTimeout.`);
339
+ return;
340
+ }
341
+ }
342
+ for (const index of indexes) {
343
+ const remaining = deadline === undefined ? undefined : deadline - Date.now();
344
+ if (remaining !== undefined && remaining <= 0) {
345
+ skipRest(String(index.name));
346
+ return;
347
+ }
348
+ progress.operation(`createIndexes on ${name} (${String(index.name)})`);
349
+ try {
350
+ await target.createIndexes(name, [index], {
351
+ ...options,
352
+ ...(remaining === undefined ? {} : { timeoutMS: remaining }),
353
+ });
354
+ progress.activity();
355
+ }
356
+ catch (error) {
357
+ if (isTimeout(error)) {
358
+ skipRest(String(index.name));
359
+ return;
360
+ }
361
+ warn(`Could not create index ${String(index.name)} on ${name}: ${error.message}`);
362
+ }
363
+ }
364
+ }
365
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,47 +1,61 @@
1
1
  {
2
2
  "name": "mongo-data-anonymizer",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Anonymize specified fields in MongoDB collections and copy them to a target database.",
5
- "main": "dist/main.js",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
6
14
  "bin": {
7
15
  "mongo-data-anonymizer": "dist/cli.js"
8
16
  },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "engines": {
21
+ "node": ">=22.13"
22
+ },
9
23
  "scripts": {
10
- "start": "ts-node src/main.ts",
11
- "build": "tsc",
12
- "test": "jest",
13
- "lint": "eslint 'src/**/*.{js,ts}'",
14
- "format": "prettier --write 'src/**/*.{js,ts}'",
15
- "run": "node dist/cli.js",
16
- "run:dev": "ts-node src/cli.ts",
24
+ "build": "tsc -p tsconfig.build.json",
25
+ "typecheck": "tsc --noEmit",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest",
28
+ "lint": "eslint .",
29
+ "format": "prettier --write .",
30
+ "format:check": "prettier --check .",
31
+ "start": "node dist/cli.js",
32
+ "dev": "node --experimental-strip-types --no-warnings src/cli.ts",
17
33
  "prepublishOnly": "npm run build"
18
34
  },
19
35
  "dependencies": {
20
- "bunyan": "^1.8.15",
21
- "faker": "^5.0.0",
22
- "mongodb": "^4.0.0",
23
- "yargs": "^17.7.2"
36
+ "@faker-js/faker": "10.6.0",
37
+ "mongodb": "^7.7.0",
38
+ "yargs": "^18.2.0"
24
39
  },
25
40
  "devDependencies": {
26
- "@faker-js/faker": "^8.4.1",
27
- "@types/bunyan": "^1.8.11",
28
- "@types/faker": "^6.6.9",
29
- "@types/jest": "^26.0.0",
30
- "@types/node": "^16.0.0",
31
- "@typescript-eslint/eslint-plugin": "^4.0.0",
32
- "@typescript-eslint/parser": "^4.0.0",
33
- "eslint": "^7.0.0",
34
- "eslint-config-prettier": "^8.0.0",
35
- "eslint-plugin-prettier": "^4.0.0",
36
- "jest": "^26.0.0",
37
- "prettier": "^2.0.0",
38
- "ts-jest": "^26.0.0",
39
- "ts-node": "^10.0.0",
40
- "typescript": "^4.0.0"
41
+ "@eslint/js": "^10.0.1",
42
+ "@types/node": "^24.19.0",
43
+ "@types/yargs": "^17.0.35",
44
+ "eslint": "^10.11.0",
45
+ "eslint-config-prettier": "^10.1.8",
46
+ "mongodb-memory-server": "^11.3.0",
47
+ "prettier": "^3.9.9",
48
+ "typescript": "~6.0.3",
49
+ "typescript-eslint": "^8.71.0",
50
+ "vitest": "^5.0.2"
41
51
  },
42
52
  "keywords": [
43
53
  "mongodb",
44
- "anonymizer"
54
+ "anonymizer",
55
+ "anonymization",
56
+ "gdpr",
57
+ "faker",
58
+ "pii"
45
59
  ],
46
60
  "author": "vanea vasco",
47
61
  "license": "MIT",
package/.eslintignore DELETED
@@ -1 +0,0 @@
1
- test/
package/.eslintrc.js DELETED
@@ -1,25 +0,0 @@
1
- module.exports = {
2
- root: true,
3
- parser: '@typescript-eslint/parser',
4
- parserOptions: {
5
- project: 'tsconfig.json',
6
- tsconfigRootDir: __dirname,
7
- sourceType: 'module',
8
- },
9
- plugins: ['@typescript-eslint', 'prettier'],
10
- extends: [
11
- 'eslint:recommended',
12
- 'plugin:@typescript-eslint/recommended',
13
- 'prettier',
14
- ],
15
- rules: {
16
- 'prettier/prettier': 'error',
17
- '@typescript-eslint/explicit-function-return-type': 'off',
18
- '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
19
- '@typescript-eslint/explicit-module-boundary-types': 'off',
20
- },
21
- env: {
22
- node: true,
23
- jest: true,
24
- },
25
- };
@@ -1,37 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- push:
5
- tags:
6
- - 'v*.*.*'
7
- workflow_dispatch:
8
-
9
- jobs:
10
- unit-tests:
11
- name: Unit tests
12
- uses: ./.github/workflows/test.yml
13
-
14
- release:
15
- name: Release package
16
- needs: [unit-tests]
17
- runs-on: ubuntu-latest
18
-
19
- steps:
20
- - name: Checkout code
21
- uses: actions/checkout@v2
22
-
23
- - name: Set up Node.js
24
- uses: actions/setup-node@v2
25
- with:
26
- node-version: '16'
27
-
28
- - name: Install dependencies
29
- run: npm install
30
-
31
- - name: Build package
32
- run: npm run build
33
-
34
- - name: Publish to npm
35
- run: npm publish
36
- env:
37
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,22 +0,0 @@
1
- name: Test
2
-
3
- on: [push, pull_request, workflow_call]
4
-
5
- jobs:
6
- test:
7
- runs-on: ubuntu-latest
8
-
9
- steps:
10
- - name: Checkout code
11
- uses: actions/checkout@v2
12
-
13
- - name: Set up Node.js
14
- uses: actions/setup-node@v2
15
- with:
16
- node-version: '16'
17
-
18
- - name: Install dependencies
19
- run: npm install
20
-
21
- - name: Run test
22
- run: npm test
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- v18
package/.prettierrc DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "singleQuote": true,
3
- "trailingComma": "all",
4
- "printWidth": 80,
5
- "tabWidth": 2,
6
- "semi": true
7
- }
package/dist/main.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare function main(): Promise<void>;