@unchainedshop/utils 1.1.4 → 1.2.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 (37) hide show
  1. package/jest.config.js +5 -0
  2. package/lib/generate-random-hash.d.ts +1 -1
  3. package/lib/generate-random-hash.js +1 -1
  4. package/lib/generate-random-hash.js.map +1 -1
  5. package/lib/locale-helpers.d.ts +3 -2
  6. package/lib/locale-helpers.js +2 -1
  7. package/lib/locale-helpers.js.map +1 -1
  8. package/package.json +12 -10
  9. package/src/buildSortOption.ts +16 -0
  10. package/src/db/build-db-indexes.ts +35 -0
  11. package/src/db/check-id.ts +5 -0
  12. package/src/db/generate-db-filter-by-id.ts +9 -0
  13. package/src/db/generate-db-mutations.ts +101 -0
  14. package/src/db/generate-db-object-id.ts +25 -0
  15. package/src/director/BaseAdapter.ts +8 -0
  16. package/src/director/BaseDirector.ts +36 -0
  17. package/src/director/BaseDiscountAdapter.ts +58 -0
  18. package/src/director/BaseDiscountDirector.ts +62 -0
  19. package/src/director/BasePricingAdapter.ts +41 -0
  20. package/src/director/BasePricingDirector.ts +106 -0
  21. package/src/director/BasePricingSheet.ts +78 -0
  22. package/src/find-localized-text.js +50 -0
  23. package/src/find-preserving-ids.ts +42 -0
  24. package/src/find-unused-slug.js +26 -0
  25. package/src/generate-random-hash.ts +8 -0
  26. package/src/locale-helpers.js +43 -0
  27. package/src/object-invert.js +8 -0
  28. package/src/pipe-promises.js +2 -0
  29. package/src/random-value-hex.ts +8 -0
  30. package/src/schemas/AddressSchema.js +16 -0
  31. package/src/schemas/ContactSchema.js +9 -0
  32. package/src/schemas/UsersSchema.js +67 -0
  33. package/src/schemas/commonSchemaFields.js +32 -0
  34. package/src/slugify.js +10 -0
  35. package/src/utils-index.js +52 -0
  36. package/tsconfig.json +26 -5
  37. package/tsconfig.build.json +0 -26
package/jest.config.js ADDED
@@ -0,0 +1,5 @@
1
+ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2
+ export default {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ };
@@ -1,2 +1,2 @@
1
- declare function _default(): string;
1
+ declare const _default: () => string;
2
2
  export default _default;
@@ -1,4 +1,4 @@
1
- import Hashids from 'hashids/cjs';
1
+ import Hashids from 'hashids';
2
2
  const hashids = new Hashids('unchained', 6, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890');
3
3
  export default () => {
4
4
  const randomNumber = Math.floor(Math.random() * (999999999 - 1)) + 1;
@@ -1 +1 @@
1
- {"version":3,"file":"generate-random-hash.js","sourceRoot":"","sources":["../src/generate-random-hash.js"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,aAAa,CAAC;AAElC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,EAAE,sCAAsC,CAAC,CAAC;AAEpF,eAAe,GAAG,EAAE;IAClB,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrE,OAAO,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;AACtC,CAAC,CAAC"}
1
+ {"version":3,"file":"generate-random-hash.js","sourceRoot":"","sources":["../src/generate-random-hash.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,EAAE,sCAAsC,CAAC,CAAC;AAEpF,eAAe,GAAG,EAAE;IAClB,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrE,OAAO,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;AACtC,CAAC,CAAC"}
@@ -1,7 +1,8 @@
1
- export const systemLocale: any;
2
- export function resolveBestSupported(acceptLanguage: any, supportedLocales: any): any;
1
+ export const systemLocale: localePkg.Locale;
2
+ export function resolveBestSupported(acceptLanguage: any, supportedLocales: any): localePkg.Locale;
3
3
  export function resolveBestCountry(localeCountry: any, shopCountry: any, countries: any): any;
4
4
  export function resolveUserRemoteAddress(req: any): {
5
5
  remoteAddress: any;
6
6
  remotePort: any;
7
7
  };
8
+ import localePkg from "locale";
@@ -1,4 +1,5 @@
1
- import { Locales, Locale } from 'locale';
1
+ import localePkg from 'locale';
2
+ const { Locales, Locale } = localePkg;
2
3
  const { UNCHAINED_LANG = 'de', UNCHAINED_COUNTRY = 'CH' } = process.env;
3
4
  export const systemLocale = new Locale(`${UNCHAINED_LANG}-${UNCHAINED_COUNTRY}`);
4
5
  export const resolveBestSupported = (acceptLanguage, supportedLocales) => {
@@ -1 +1 @@
1
- {"version":3,"file":"locale-helpers.js","sourceRoot":"","sources":["../src/locale-helpers.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEzC,MAAM,EAAE,cAAc,GAAG,IAAI,EAAE,iBAAiB,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;AAExE,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,GAAG,cAAc,IAAI,iBAAiB,EAAE,CAAC,CAAC;AAEjF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,cAAc,EAAE,gBAAgB,EAAE,EAAE;IACvE,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACvD,IAAI,CAAC,UAAU;QAAE,OAAO,YAAY,CAAC;IACrC,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE;IAC1E,IAAI,WAAW,EAAE;QACf,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,YAAY,EAAE,OAAO,EAAE,EAAE;YACjE,IAAI,WAAW,KAAK,OAAO,CAAC,OAAO,EAAE;gBACnC,OAAO,OAAO,CAAC,OAAO,CAAC;aACxB;YACD,OAAO,YAAY,CAAC;QACtB,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,IAAI,eAAe,EAAE;YACnB,OAAO,eAAe,CAAC;SACxB;KACF;IACD,OAAO,aAAa,IAAI,YAAY,CAAC,OAAO,CAAC;AAC/C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,GAAG,EAAE,EAAE;IAC9C,MAAM,aAAa,GACjB,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC;QACxB,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC;QAC9B,GAAG,CAAC,UAAU,CAAC,aAAa;QAC5B,GAAG,CAAC,MAAM,CAAC,aAAa;QACxB,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,aAAa,CAAC;IAEtC,MAAM,UAAU,GACd,GAAG,CAAC,UAAU,EAAE,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,IAAI,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC;IAE7F,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AACvC,CAAC,CAAC"}
1
+ {"version":3,"file":"locale-helpers.js","sourceRoot":"","sources":["../src/locale-helpers.js"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,QAAQ,CAAC;AAE/B,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;AAEtC,MAAM,EAAE,cAAc,GAAG,IAAI,EAAE,iBAAiB,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;AAExE,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,GAAG,cAAc,IAAI,iBAAiB,EAAE,CAAC,CAAC;AAEjF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,cAAc,EAAE,gBAAgB,EAAE,EAAE;IACvE,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACvD,IAAI,CAAC,UAAU;QAAE,OAAO,YAAY,CAAC;IACrC,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE;IAC1E,IAAI,WAAW,EAAE;QACf,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,YAAY,EAAE,OAAO,EAAE,EAAE;YACjE,IAAI,WAAW,KAAK,OAAO,CAAC,OAAO,EAAE;gBACnC,OAAO,OAAO,CAAC,OAAO,CAAC;aACxB;YACD,OAAO,YAAY,CAAC;QACtB,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,IAAI,eAAe,EAAE;YACnB,OAAO,eAAe,CAAC;SACxB;KACF;IACD,OAAO,aAAa,IAAI,YAAY,CAAC,OAAO,CAAC;AAC/C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,GAAG,EAAE,EAAE;IAC9C,MAAM,aAAa,GACjB,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC;QACxB,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC;QAC9B,GAAG,CAAC,UAAU,CAAC,aAAa;QAC5B,GAAG,CAAC,MAAM,CAAC,aAAa;QACxB,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,aAAa,CAAC;IAEtC,MAAM,UAAU,GACd,GAAG,CAAC,UAAU,EAAE,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,IAAI,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC;IAE7F,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AACvC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,15 +1,19 @@
1
1
  {
2
2
  "name": "@unchainedshop/utils",
3
- "version": "1.1.4",
3
+ "version": "1.2.0",
4
4
  "main": "lib/utils-index.js",
5
+ "exports": {
6
+ ".": "./lib/utils-index.js",
7
+ "./*": "./lib/*"
8
+ },
5
9
  "types": "lib/utils-index.d.ts",
6
10
  "type": "module",
7
11
  "scripts": {
12
+ "prepublishOnly": "npm install && npm run build || :",
8
13
  "clean": "rm -rf lib",
9
- "build": "npm run clean && tsc -p tsconfig.build.json",
10
- "watch": "tsc --watch",
11
- "link:core": "npm link @unchainedshop/types && npm link @unchainedshop/logger",
12
- "test": "METEOR_PACKAGE_DIRS=../ TEST_CLIENT=0 TEST_WATCH=1 meteor test-packages ./ --driver-package meteortesting:mocha --port 4200"
14
+ "build": "npm run clean && tsc",
15
+ "watch": "tsc -w",
16
+ "test": "jest --watch"
13
17
  },
14
18
  "repository": {
15
19
  "type": "git",
@@ -26,7 +30,7 @@
26
30
  },
27
31
  "homepage": "https://github.com/unchainedshop/unchained#readme",
28
32
  "dependencies": {
29
- "@unchainedshop/logger": "1.1.3",
33
+ "@unchainedshop/logger": "^1.1.9",
30
34
  "abort-controller": "3.0.0",
31
35
  "bson": "^4.6.4",
32
36
  "hashids": "^2.2.10",
@@ -35,10 +39,8 @@
35
39
  "simpl-schema": "^1.12.2"
36
40
  },
37
41
  "devDependencies": {
38
- "@types/mocha": "^9.1.1",
39
- "@unchainedshop/types": "1.1.0",
40
- "chai": "^4.3.6",
41
- "mocha": "^10.0.0",
42
+ "@types/node": "^16.11.44",
43
+ "@unchainedshop/types": "^1.1.9",
42
44
  "typescript": "^4.7.4"
43
45
  }
44
46
  }
@@ -0,0 +1,16 @@
1
+ import { SortOption } from '@unchainedshop/types/api';
2
+
3
+ const SORT_DIRECTIONS = {
4
+ ASC: 1,
5
+ DESC: -1,
6
+ };
7
+
8
+ const buildSortOptions = (sort: Array<SortOption> = []): { [key: string]: [value: number] } => {
9
+ const sortBy = {};
10
+ sort?.forEach(({ key, value }) => {
11
+ sortBy[key] = SORT_DIRECTIONS[value];
12
+ });
13
+ return sortBy;
14
+ };
15
+
16
+ export default buildSortOptions;
@@ -0,0 +1,35 @@
1
+ import { Collection, Indexes, Document } from '@unchainedshop/types/common';
2
+ import { log, LogLevel } from '@unchainedshop/logger';
3
+
4
+ const buildIndexes = <T>(collection: Collection<T>, indexes: Indexes<T>): Promise<Array<void | Error>> =>
5
+ Promise.all(
6
+ indexes.map(async ({ index, options }) => {
7
+ await collection.createIndex(index, options).catch((error) => error as Error);
8
+ }),
9
+ );
10
+
11
+ export const buildDbIndexes = async <T extends Document>(
12
+ collection: Collection<T>,
13
+ indexes: Indexes<T>,
14
+ ) => {
15
+ let success = true;
16
+ const buildErrors = (await buildIndexes<T>(collection, indexes)).filter(Boolean);
17
+
18
+ if (buildErrors.length) {
19
+ const dropError = await collection.dropIndexes().catch((e) => e);
20
+
21
+ if (!dropError) {
22
+ const rebuildErrors = (await buildIndexes<T>(collection, indexes)).filter(Boolean);
23
+
24
+ if (rebuildErrors.length) {
25
+ log('Error building indexes', {
26
+ level: LogLevel.Error,
27
+ ...rebuildErrors,
28
+ });
29
+ success = false;
30
+ }
31
+ }
32
+ }
33
+
34
+ return success;
35
+ };
@@ -0,0 +1,5 @@
1
+ export const checkId = (value: string, error?: { message: string; path?: string }): void => {
2
+ if (typeof value !== 'string') {
3
+ throw error || { message: 'Invalid id' };
4
+ }
5
+ };
@@ -0,0 +1,9 @@
1
+ import { Filter, Query, _ID } from '@unchainedshop/types/common';
2
+
3
+ export const generateDbFilterById = (
4
+ id: unknown,
5
+ query: Query = {},
6
+ ): Filter<{ _id?: _ID; [x: string]: any }> => {
7
+ const _id = id || null; // never undefined, else it will get the first one
8
+ return { _id, ...query };
9
+ };
@@ -0,0 +1,101 @@
1
+ import SimpleSchema from 'simpl-schema';
2
+ import {
3
+ Collection,
4
+ ModuleMutations,
5
+ ModuleCreateMutation,
6
+ _ID,
7
+ Update,
8
+ TimestampFields,
9
+ } from '@unchainedshop/types/common';
10
+ import { checkId } from './check-id';
11
+ import { generateDbObjectId } from './generate-db-object-id';
12
+ import { generateDbFilterById } from './generate-db-filter-by-id';
13
+
14
+ export const generateDbMutations = <T extends TimestampFields & { _id?: _ID }>(
15
+ collection: Collection<T>,
16
+ schema: SimpleSchema,
17
+ options?: {
18
+ hasCreateOnly?: boolean;
19
+ permanentlyDeleteByDefault?: boolean;
20
+ },
21
+ ): ModuleMutations<T> | ModuleCreateMutation<T> => {
22
+ if (!collection) throw new Error('Collection is missing');
23
+ if (!schema) throw new Error('Schema is missing');
24
+
25
+ const { hasCreateOnly, permanentlyDeleteByDefault } = options || {
26
+ hasCreateOnly: false,
27
+ permanentlyDeleteByDefault: false,
28
+ };
29
+
30
+ const deletePermanently = async (_id) => {
31
+ checkId(_id);
32
+ const filter = generateDbFilterById(_id);
33
+ const result = await collection.deleteOne(filter);
34
+ return result.deletedCount;
35
+ };
36
+
37
+ return {
38
+ create: async (doc, userId) => {
39
+ const values = schema.clean(doc);
40
+ values.created = new Date();
41
+ values.createdBy = userId;
42
+ schema.validate(values);
43
+ values._id = doc._id || generateDbObjectId();
44
+
45
+ const result = await collection.insertOne(values);
46
+ return result.insertedId as string;
47
+ },
48
+
49
+ update: hasCreateOnly
50
+ ? undefined
51
+ : async (_id, doc, userId) => {
52
+ checkId(_id);
53
+
54
+ let modifier: Update<T>;
55
+
56
+ if ((doc as Update<T>)?.$set) {
57
+ const values = schema.clean(doc, { isModifier: true });
58
+ modifier = {
59
+ ...values,
60
+ $set: {
61
+ ...(values.$set || {}),
62
+ updated: new Date(),
63
+ updatedBy: userId,
64
+ },
65
+ };
66
+ } else {
67
+ const values = schema.clean(doc);
68
+ modifier = {
69
+ $set: {
70
+ ...values,
71
+ updated: new Date(),
72
+ updatedBy: userId,
73
+ },
74
+ };
75
+ }
76
+
77
+ schema.validate(modifier, { modifier: true });
78
+ const filter = generateDbFilterById(_id, { deleted: null });
79
+ await collection.updateOne(filter, modifier);
80
+
81
+ return _id;
82
+ },
83
+
84
+ deletePermanently: hasCreateOnly ? undefined : deletePermanently,
85
+
86
+ delete: hasCreateOnly
87
+ ? undefined
88
+ : async (_id, userId) => {
89
+ if (permanentlyDeleteByDefault) {
90
+ return deletePermanently(_id);
91
+ }
92
+ checkId(_id);
93
+ const filter = generateDbFilterById(_id, { deleted: null });
94
+ const modifier = { $set: { deleted: new Date(), deletedBy: userId } };
95
+ const values = schema.clean(modifier, { isModifier: true });
96
+ const result = await collection.updateOne(filter, values);
97
+
98
+ return result.modifiedCount;
99
+ },
100
+ };
101
+ };
@@ -0,0 +1,25 @@
1
+ import crypto from 'crypto';
2
+
3
+ // Source: https://github.com/meteor/meteor/blob/devel/packages/random/NodeRandomGenerator.js
4
+ /**
5
+ * @name Random.hexString
6
+ * @summary Return a random string of `n` hexadecimal digits.
7
+ * @locus Anywhere
8
+ * @param {Number} n Length of the string
9
+ */
10
+ export const generateDbObjectId = (digits = 24): string => {
11
+ const numBytes = Math.ceil(digits / 2);
12
+ let bytes;
13
+ // Try to get cryptographically strong randomness. Fall back to
14
+ // non-cryptographically strong if not available.
15
+ try {
16
+ bytes = crypto.randomBytes(numBytes);
17
+ } catch (e) {
18
+ // XXX should re-throw any error except insufficient entropy
19
+ bytes = crypto.pseudoRandomBytes(numBytes);
20
+ }
21
+ const result = bytes.toString('hex');
22
+ // If the number of digits is odd, we'll have generated an extra 4 bits
23
+ // of randomness, so we need to trim the last digit.
24
+ return result.substring(0, digits);
25
+ };
@@ -0,0 +1,8 @@
1
+ import { IBaseAdapter } from '@unchainedshop/types/common';
2
+ import { log, LogLevel } from '@unchainedshop/logger';
3
+
4
+ export const BaseAdapter: Omit<IBaseAdapter, 'key' | 'label' | 'version'> = {
5
+ log(message: string, { level = LogLevel.Debug, ...options } = {}) {
6
+ return log(message, { level, ...options });
7
+ },
8
+ };
@@ -0,0 +1,36 @@
1
+ import { IBaseAdapter, IBaseDirector } from '@unchainedshop/types/common';
2
+ import { log } from '@unchainedshop/logger';
3
+
4
+ export const BaseDirector = <AdapterType extends IBaseAdapter>(
5
+ directorName: string,
6
+ options?: {
7
+ adapterSortKey?: string;
8
+ adapterKeyField?: string; // Set to 'key' per default
9
+ },
10
+ ): IBaseDirector<AdapterType> => {
11
+ const Adapters = new Map<string, AdapterType>();
12
+ const keyField = options?.adapterKeyField || 'key';
13
+
14
+ return {
15
+ getAdapter: (key) => {
16
+ return Adapters.get(key);
17
+ },
18
+
19
+ getAdapters: ({ adapterFilter } = {}) => {
20
+ const sortKey = options?.adapterSortKey || keyField;
21
+ return Array.from(Adapters.values())
22
+ .sort((left, right) => left[sortKey] - right[sortKey])
23
+ .filter(adapterFilter || (() => true));
24
+ },
25
+
26
+ registerAdapter: (Adapter) => {
27
+ log(
28
+ `${directorName} -> Registered ${keyField !== 'key' ? `${Adapter[keyField]} ` : ' '} ${
29
+ Adapter.key
30
+ } ${Adapter.version} (${Adapter.label})`,
31
+ );
32
+
33
+ Adapters.set(Adapter[keyField], Adapter);
34
+ },
35
+ };
36
+ };
@@ -0,0 +1,58 @@
1
+ import { log, LogLevel } from '@unchainedshop/logger';
2
+
3
+ import { IDiscountAdapter } from '@unchainedshop/types/discount';
4
+
5
+ export const BaseDiscountAdapter: Omit<IDiscountAdapter, 'key' | 'label' | 'version'> = {
6
+ orderIndex: 0,
7
+
8
+ isManualAdditionAllowed: async () => {
9
+ return false;
10
+ },
11
+
12
+ // return true if a discount is allowed to get removed manually by a user
13
+ isManualRemovalAllowed: async () => {
14
+ return false;
15
+ },
16
+
17
+ actions: async () => ({
18
+ // return true if a discount is valid to be part of the order
19
+ // without input of a user. that could be a time based global discount
20
+ // like a 10% discount day
21
+ // if you return false, this discount will
22
+ // get removed from the order before any price calculation
23
+ // takes place.
24
+ // eslint-disable-next-line
25
+ isValidForSystemTriggering: async () => {
26
+ return false;
27
+ },
28
+
29
+ // return an arbitrary JSON serializable object with reservation data
30
+ // this method is called when a discount is added through a manual code and let's
31
+ // you manually deduct expendable discounts (coupon balances for ex.) before checkout
32
+ reserve: async () => {
33
+ return {};
34
+ },
35
+
36
+ // return void, allows you to free up any reservations in backend systems
37
+ release: async () => {
38
+ return null;
39
+ },
40
+
41
+ // return true if a discount is valid to be part of the order.
42
+ // if you return false, this discount will
43
+ // get removed from the order before any price calculation
44
+ // takes place.
45
+ isValidForCodeTriggering: async () => {
46
+ return false;
47
+ },
48
+
49
+ // returns the appropriate discount context for a calculation adapter
50
+ discountForPricingAdapterKey() {
51
+ return null;
52
+ },
53
+ }),
54
+
55
+ log(message: string, { level = LogLevel.Debug, ...options } = {}) {
56
+ return log(message, { level, ...options });
57
+ },
58
+ };
@@ -0,0 +1,62 @@
1
+ import { IDiscountAdapter, IDiscountDirector } from '@unchainedshop/types/discount';
2
+ import { log } from '@unchainedshop/logger';
3
+ import { BaseDirector } from './BaseDirector';
4
+
5
+ export const BaseDiscountDirector = (directorName: string): IDiscountDirector => {
6
+ const baseDirector = BaseDirector<IDiscountAdapter>(directorName, {
7
+ adapterSortKey: 'orderIndex',
8
+ });
9
+
10
+ return {
11
+ ...baseDirector,
12
+
13
+ actions: async (discountContext, requestContext) => {
14
+ const context = { ...discountContext, ...requestContext };
15
+
16
+ return {
17
+ resolveDiscountKeyFromStaticCode: async (options) => {
18
+ if (!context.order) return null;
19
+
20
+ log(`DiscountDirector -> Find user discount for static code ${options?.code}`);
21
+
22
+ const discounts = await Promise.all(
23
+ baseDirector
24
+ .getAdapters()
25
+ .filter((Adapter) => Adapter.isManualAdditionAllowed(options?.code))
26
+ .map(async (Adapter) => {
27
+ const adapter = await Adapter.actions({ context });
28
+ return {
29
+ key: Adapter.key,
30
+ isValid: await adapter.isValidForCodeTriggering(options),
31
+ };
32
+ }),
33
+ );
34
+
35
+ return discounts.find(({ isValid }) => isValid === true)?.key;
36
+ },
37
+
38
+ async findSystemDiscounts() {
39
+ if (!context.order) return [];
40
+ const discounts = await Promise.all(
41
+ baseDirector.getAdapters().map(async (Adapter) => {
42
+ const adapter = await Adapter.actions({ context });
43
+ return {
44
+ key: Adapter.key,
45
+ isValid: await adapter.isValidForSystemTriggering(),
46
+ };
47
+ }),
48
+ );
49
+
50
+ const validDiscounts = discounts
51
+ .filter(({ isValid }) => isValid === true)
52
+ .map(({ key }) => key);
53
+
54
+ if (validDiscounts.length > 0) {
55
+ log(`DiscountDirector -> Found ${validDiscounts.length} system discounts`);
56
+ }
57
+ return validDiscounts;
58
+ },
59
+ };
60
+ },
61
+ };
62
+ };
@@ -0,0 +1,41 @@
1
+ import {
2
+ BasePricingAdapterContext,
3
+ IPricingSheet,
4
+ IPricingAdapter,
5
+ PricingCalculation,
6
+ IPricingAdapterActions,
7
+ } from '@unchainedshop/types/pricing';
8
+ import { log, LogLevel } from '@unchainedshop/logger';
9
+
10
+ export const BasePricingAdapter = <
11
+ Context extends BasePricingAdapterContext,
12
+ Calculation extends PricingCalculation,
13
+ >(): IPricingAdapter<Context, Calculation, IPricingSheet<Calculation>> => ({
14
+ key: '',
15
+ label: '',
16
+ version: '',
17
+ orderIndex: 0,
18
+
19
+ isActivatedFor: () => {
20
+ return false;
21
+ },
22
+
23
+ actions: (params) => {
24
+ const calculation = [];
25
+ const actions: IPricingAdapterActions<Calculation, Context> = {
26
+ calculate: async () => {
27
+ return [];
28
+ },
29
+ getCalculation: () => calculation,
30
+ getContext: () => params.context,
31
+ };
32
+
33
+ return actions as IPricingAdapterActions<Calculation, Context> & {
34
+ resultSheet: () => IPricingSheet<Calculation>;
35
+ };
36
+ },
37
+
38
+ log(message: string, { level = LogLevel.Debug, ...options } = {}) {
39
+ return log(message, { level, ...options });
40
+ },
41
+ });
@@ -0,0 +1,106 @@
1
+ import { Discount } from '@unchainedshop/types/discount';
2
+ import {
3
+ BasePricingAdapterContext,
4
+ BasePricingContext,
5
+ IPricingDirector,
6
+ IPricingAdapter,
7
+ IPricingSheet,
8
+ PricingCalculation,
9
+ IPricingAdapterActions,
10
+ } from '@unchainedshop/types/pricing';
11
+ import { log, LogLevel } from '@unchainedshop/logger';
12
+ import { BaseDirector } from './BaseDirector';
13
+
14
+ export const BasePricingDirector = <
15
+ DirectorContext extends BasePricingContext,
16
+ AdapterContext extends BasePricingAdapterContext,
17
+ Calculation extends PricingCalculation,
18
+ PricingAdapter extends IPricingAdapter<AdapterContext, Calculation, IPricingSheet<Calculation>>,
19
+ >(
20
+ directorName: string,
21
+ ): IPricingDirector<
22
+ DirectorContext,
23
+ Calculation,
24
+ AdapterContext,
25
+ IPricingSheet<Calculation>,
26
+ PricingAdapter
27
+ > => {
28
+ const baseDirector = BaseDirector<PricingAdapter>(directorName, {
29
+ adapterSortKey: 'orderIndex',
30
+ });
31
+
32
+ const director: IPricingDirector<
33
+ DirectorContext,
34
+ Calculation,
35
+ AdapterContext,
36
+ IPricingSheet<Calculation>,
37
+ PricingAdapter
38
+ > = {
39
+ ...baseDirector,
40
+ buildPricingContext: async () => {
41
+ return {} as AdapterContext;
42
+ },
43
+ actions: async (pricingContext, requestContext, buildPricingContext) => {
44
+ const context = await buildPricingContext(pricingContext, requestContext);
45
+
46
+ let calculation: Array<Calculation> = [];
47
+
48
+ const actions: IPricingAdapterActions<Calculation, AdapterContext> = {
49
+ async calculate() {
50
+ const Adapters = baseDirector.getAdapters({
51
+ adapterFilter: (Adapter) => {
52
+ return Adapter.isActivatedFor(context);
53
+ },
54
+ });
55
+
56
+ calculation = await Adapters.reduce(async (previousPromise, Adapter) => {
57
+ const resolvedCalculation = await previousPromise;
58
+ if (!resolvedCalculation) return null;
59
+
60
+ const discounts: Array<Discount> = await Promise.all(
61
+ context.discounts.map(async (discount) => ({
62
+ discountId: discount._id,
63
+ configuration: await context.modules.orders.discounts.configurationForPricingAdapterKey(
64
+ discount,
65
+ Adapter.key,
66
+ this.calculationSheet(),
67
+ context,
68
+ ),
69
+ })),
70
+ );
71
+
72
+ try {
73
+ const adapter = Adapter.actions({
74
+ context,
75
+ calculationSheet: this.calculationSheet(),
76
+ discounts: discounts.filter(({ configuration }) => configuration !== null),
77
+ });
78
+
79
+ const nextCalculationResult = await adapter.calculate();
80
+ if (!nextCalculationResult) return null;
81
+ calculation = resolvedCalculation.concat(nextCalculationResult);
82
+ return calculation;
83
+ } catch (error) {
84
+ log(error, { level: LogLevel.Error });
85
+ }
86
+ return resolvedCalculation;
87
+ }, Promise.resolve([]));
88
+
89
+ return calculation;
90
+ },
91
+ getCalculation() {
92
+ return calculation;
93
+ },
94
+ getContext() {
95
+ return context;
96
+ },
97
+ };
98
+
99
+ return actions as IPricingAdapterActions<Calculation, AdapterContext> & {
100
+ calculationSheet: () => IPricingSheet<Calculation>;
101
+ };
102
+ },
103
+ };
104
+
105
+ return director;
106
+ };
@@ -0,0 +1,78 @@
1
+ import { PricingCalculation, IBasePricingSheet, PricingSheetParams } from '@unchainedshop/types/pricing';
2
+
3
+ export const BasePricingSheet = <Calculation extends PricingCalculation>(
4
+ params: PricingSheetParams<Calculation>,
5
+ ): IBasePricingSheet<Calculation> => {
6
+ const calculation = params.calculation || [];
7
+
8
+ const pricingSheet: IBasePricingSheet<Calculation> = {
9
+ calculation,
10
+ currency: params.currency,
11
+ quantity: params.quantity,
12
+
13
+ getRawPricingSheet() {
14
+ return calculation;
15
+ },
16
+
17
+ isValid() {
18
+ return calculation.length > 0;
19
+ },
20
+
21
+ sum(filter) {
22
+ return this.filterBy(filter)
23
+ .filter(Boolean)
24
+ .reduce((sum: number, calculationRow: Calculation) => sum + calculationRow.amount, 0);
25
+ },
26
+
27
+ taxSum() {
28
+ return 0;
29
+ },
30
+
31
+ gross() {
32
+ return this.sum();
33
+ },
34
+
35
+ net() {
36
+ return this.gross() - this.taxSum();
37
+ },
38
+
39
+ total({ category, useNetPrice } = { useNetPrice: false }) {
40
+ if (!category) {
41
+ return {
42
+ amount: Math.round(useNetPrice ? this.net() : this.gross()),
43
+ currency: params.currency,
44
+ };
45
+ }
46
+
47
+ return {
48
+ amount: Math.round(this.sum({ category } as any)),
49
+ currency: params.currency,
50
+ };
51
+ },
52
+
53
+ filterBy(filter) {
54
+ const filteredCalculation = Object.keys(filter || {}).reduce(
55
+ (oldCalculation, filterKey) =>
56
+ oldCalculation.filter(
57
+ (row: Calculation) =>
58
+ !!row && (filter[filterKey] === undefined || row[filterKey] === filter[filterKey]),
59
+ ),
60
+ calculation,
61
+ );
62
+
63
+ return filteredCalculation;
64
+ },
65
+
66
+ resetCalculation(calculationSheet) {
67
+ calculationSheet.filterBy().forEach(({ amount, ...row }: Calculation) => {
68
+ pricingSheet.calculation.push({
69
+ ...row,
70
+ amount: amount * -1,
71
+ } as Calculation);
72
+ });
73
+ return pricingSheet.calculation;
74
+ },
75
+ };
76
+
77
+ return pricingSheet;
78
+ };
@@ -0,0 +1,50 @@
1
+ import 'abort-controller/polyfill';
2
+ import LRU from 'lru-cache';
3
+ import { systemLocale } from './locale-helpers';
4
+
5
+ const { NODE_ENV } = process.env;
6
+
7
+ const ttl = NODE_ENV === 'production' ? 1000 * 30 : 0; // 30 seconds or 1 second
8
+
9
+ const textCache = new LRU({ max: 50000, ttl });
10
+
11
+ const extendSelectorWithLocale = (selector, locale) => {
12
+ const localeSelector = {
13
+ locale: { $in: [locale.normalized, locale.language] },
14
+ };
15
+ return { ...localeSelector, ...selector };
16
+ };
17
+
18
+ const findLocalizedText = async (collection, selector, locale) => {
19
+ const cacheKey = JSON.stringify({
20
+ n: collection._name, // eslint-disable-line
21
+ s: selector,
22
+ l: locale,
23
+ });
24
+
25
+ const cachedText = textCache.get(cacheKey);
26
+
27
+ if (cachedText) return cachedText;
28
+
29
+ const exactTranslation = await collection.findOne(extendSelectorWithLocale(selector, locale));
30
+ if (exactTranslation) {
31
+ textCache.set(cacheKey, exactTranslation);
32
+ return exactTranslation;
33
+ }
34
+
35
+ if (systemLocale.normalized !== locale.normalized) {
36
+ const fallbackTranslation = await collection.findOne(
37
+ extendSelectorWithLocale(selector, systemLocale),
38
+ );
39
+ if (fallbackTranslation) {
40
+ textCache.set(cacheKey, fallbackTranslation);
41
+ return fallbackTranslation;
42
+ }
43
+ }
44
+
45
+ const foundText = await collection.findOne(selector, {});
46
+ textCache.set(cacheKey, foundText);
47
+ return foundText;
48
+ };
49
+
50
+ export default findLocalizedText;
@@ -0,0 +1,42 @@
1
+ import { Collection, FindOptions, Query } from '@unchainedshop/types/common';
2
+
3
+ const { AMAZON_DOCUMENTDB_COMPAT_MODE } = process.env;
4
+
5
+ const sortByIndex = {
6
+ index: 1,
7
+ };
8
+
9
+ const sortBySequence = {
10
+ sequence: 1,
11
+ };
12
+
13
+ const defaultSort = AMAZON_DOCUMENTDB_COMPAT_MODE ? sortBySequence : sortByIndex;
14
+
15
+ export const findPreservingIds =
16
+ <T>(collection: Collection<T>) =>
17
+ async (selector: Query, ids: Array<string>, options?: FindOptions): Promise<Array<T>> => {
18
+ const { skip, limit, sort = defaultSort } = options || {};
19
+ const filteredSelector = {
20
+ ...selector,
21
+ _id: { $in: ids },
22
+ };
23
+
24
+ const filteredPipeline = [
25
+ {
26
+ $match: filteredSelector,
27
+ },
28
+ typeof sort === 'object' &&
29
+ 'index' in sort && {
30
+ $addFields: {
31
+ index: { $indexOfArray: [ids, '$_id'] },
32
+ },
33
+ },
34
+ sort && { $sort: sort },
35
+ skip && { $skip: skip },
36
+ limit && { $limit: limit },
37
+ ].filter(Boolean);
38
+
39
+ const aggregationPointer = collection.aggregate(filteredPipeline);
40
+ const items = await aggregationPointer.toArray();
41
+ return items as Array<T>;
42
+ };
@@ -0,0 +1,26 @@
1
+ const DELIMITER = '-';
2
+
3
+ const addSuffixToSlug = (slug, index = 1, delimiter = DELIMITER) => {
4
+ return `${slug}${delimiter}${index}`;
5
+ };
6
+
7
+ const incrementSuffixedSlug = (slugIncludingSuffix, delimiter = DELIMITER) => {
8
+ const slugParts = slugIncludingSuffix.split(delimiter);
9
+ const suffixedIndex = parseInt(slugParts.pop(), 10);
10
+ const slugWithoutSuffix = slugParts.join(delimiter);
11
+ return addSuffixToSlug(slugWithoutSuffix, suffixedIndex + 1);
12
+ };
13
+
14
+ export default (checkSlugIsUniqueFn, { slugify }) => {
15
+ const findUnusedSlug = async ({ title, existingSlug, newSlug }) => {
16
+ const slug = newSlug || existingSlug || `${slugify(title)}`;
17
+ if (!(await checkSlugIsUniqueFn(slug))) {
18
+ const isSlugAlreadySuffixed = !!newSlug;
19
+ return findUnusedSlug({
20
+ newSlug: isSlugAlreadySuffixed ? incrementSuffixedSlug(slug) : addSuffixToSlug(slug),
21
+ });
22
+ }
23
+ return slug;
24
+ };
25
+ return findUnusedSlug;
26
+ };
@@ -0,0 +1,8 @@
1
+ import Hashids from 'hashids';
2
+
3
+ const hashids = new Hashids('unchained', 6, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890');
4
+
5
+ export default () => {
6
+ const randomNumber = Math.floor(Math.random() * (999999999 - 1)) + 1;
7
+ return hashids.encode(randomNumber);
8
+ };
@@ -0,0 +1,43 @@
1
+ import localePkg from 'locale';
2
+
3
+ const { Locales, Locale } = localePkg;
4
+
5
+ const { UNCHAINED_LANG = 'de', UNCHAINED_COUNTRY = 'CH' } = process.env;
6
+
7
+ export const systemLocale = new Locale(`${UNCHAINED_LANG}-${UNCHAINED_COUNTRY}`);
8
+
9
+ export const resolveBestSupported = (acceptLanguage, supportedLocales) => {
10
+ const acceptLocale = new Locales(acceptLanguage);
11
+ const bestLocale = acceptLocale.best(supportedLocales);
12
+ if (!bestLocale) return systemLocale;
13
+ return bestLocale;
14
+ };
15
+
16
+ export const resolveBestCountry = (localeCountry, shopCountry, countries) => {
17
+ if (shopCountry) {
18
+ const resolvedCountry = countries.reduce((lastResolved, country) => {
19
+ if (shopCountry === country.isoCode) {
20
+ return country.isoCode;
21
+ }
22
+ return lastResolved;
23
+ }, null);
24
+ if (resolvedCountry) {
25
+ return resolvedCountry;
26
+ }
27
+ }
28
+ return localeCountry || systemLocale.country;
29
+ };
30
+
31
+ export const resolveUserRemoteAddress = (req) => {
32
+ const remoteAddress =
33
+ req.headers['x-real-ip'] ||
34
+ req.headers['x-forwarded-for'] ||
35
+ req.connection.remoteAddress ||
36
+ req.socket.remoteAddress ||
37
+ req.connection.socket.remoteAddress;
38
+
39
+ const remotePort =
40
+ req.connection?.remotePort || req.socket?.remotePort || req.connection?.socket?.remotePort;
41
+
42
+ return { remoteAddress, remotePort };
43
+ };
@@ -0,0 +1,8 @@
1
+ const invertMapObject = (theMapObject) =>
2
+ Object.keys(theMapObject).reduce((invertedObj, key) => {
3
+ const newObj = invertedObj;
4
+ newObj[theMapObject[key]] = key;
5
+ return newObj;
6
+ }, {});
7
+
8
+ export default invertMapObject;
@@ -0,0 +1,2 @@
1
+ export default (fns) => (initialValue) =>
2
+ fns.reduce((sum, fn) => Promise.resolve(sum).then(fn), initialValue);
@@ -0,0 +1,8 @@
1
+ import crypto from 'crypto';
2
+
3
+ export default function randomValueHex(len: number): string {
4
+ return crypto
5
+ .randomBytes(Math.ceil(len / 2))
6
+ .toString('hex') // convert to hexadecimal format
7
+ .slice(0, len); // return required number of characters
8
+ }
@@ -0,0 +1,16 @@
1
+ import SimpleSchema from 'simpl-schema';
2
+
3
+ export const AddressSchema = new SimpleSchema(
4
+ {
5
+ firstName: String,
6
+ lastName: String,
7
+ company: String,
8
+ addressLine: String,
9
+ addressLine2: String,
10
+ city: String,
11
+ postalCode: String,
12
+ regionCode: String,
13
+ countryCode: String,
14
+ },
15
+ { requiredByDefault: false },
16
+ );
@@ -0,0 +1,9 @@
1
+ import SimpleSchema from 'simpl-schema';
2
+
3
+ export const ContactSchema = new SimpleSchema(
4
+ {
5
+ telNumber: String,
6
+ emailAddress: String,
7
+ },
8
+ { requiredByDefault: false },
9
+ );
@@ -0,0 +1,67 @@
1
+ import SimpleSchema from 'simpl-schema';
2
+ import { AddressSchema } from './AddressSchema';
3
+ import { timestampFields } from './commonSchemaFields';
4
+
5
+ const ProfileSchema = new SimpleSchema(
6
+ {
7
+ displayName: String,
8
+ birthday: Date,
9
+ phoneMobile: String,
10
+ gender: String,
11
+ address: AddressSchema,
12
+ },
13
+ { requiredByDefault: false },
14
+ );
15
+
16
+ export const LastLoginSchema = new SimpleSchema(
17
+ {
18
+ timestamp: Date,
19
+ locale: String,
20
+ countryContext: String,
21
+ remoteAddress: String,
22
+ remotePort: String,
23
+ userAgent: String,
24
+ },
25
+ { requiredByDefault: false },
26
+ );
27
+
28
+ export const LastContactSchema = new SimpleSchema(
29
+ {
30
+ telNumber: String,
31
+ emailAddress: String,
32
+ },
33
+ { requiredByDefault: false },
34
+ );
35
+
36
+ export const UserSchema = new SimpleSchema(
37
+ {
38
+ emails: Array,
39
+ 'emails.$': Object,
40
+ 'emails.$.address': String,
41
+ 'emails.$.verified': Boolean,
42
+ username: String,
43
+ lastLogin: LastLoginSchema,
44
+ profile: ProfileSchema,
45
+ lastBillingAddress: AddressSchema,
46
+ lastContact: LastContactSchema,
47
+ guest: Boolean,
48
+ initialPassword: Boolean,
49
+ tags: Array,
50
+ 'tags.$': String,
51
+ avatarId: String,
52
+ meta: {
53
+ type: Object,
54
+ optional: true,
55
+ blackbox: true,
56
+ },
57
+ services: {
58
+ type: Object,
59
+ optional: true,
60
+ blackbox: true,
61
+ },
62
+ roles: Array,
63
+ 'roles.$': String,
64
+ ...timestampFields,
65
+ },
66
+ { requiredByDefault: false },
67
+ );
@@ -0,0 +1,32 @@
1
+ export const contextFields = {
2
+ context: {
3
+ type: Object,
4
+ blackbox: true,
5
+ required: false,
6
+ },
7
+ };
8
+
9
+ export const logFields = {
10
+ log: Array,
11
+ 'log.$': {
12
+ type: Object,
13
+ },
14
+ 'log.$.date': {
15
+ type: Date,
16
+ },
17
+ 'log.$.status': {
18
+ type: String,
19
+ },
20
+ 'log.$.info': {
21
+ type: String,
22
+ },
23
+ };
24
+
25
+ export const timestampFields = {
26
+ created: { type: Date, required: true },
27
+ createdBy: { type: String }, // Logically it is required but for backwards compatibility it's set to optional
28
+ updated: { type: Date },
29
+ updatedBy: { type: String },
30
+ deleted: { type: Date },
31
+ deletedBy: { type: String },
32
+ };
package/src/slugify.js ADDED
@@ -0,0 +1,10 @@
1
+ export default function slugify(text) {
2
+ return text
3
+ .toString()
4
+ .toLowerCase()
5
+ .replace(/\s+/g, '-') // Replace spaces with -
6
+ .replace(/[^\w-]+/g, '') // Remove all non-word chars
7
+ .replace(/--+/g, '-') // Replace multiple - with single -
8
+ .replace(/^-+/, '') // Trim - from start of text
9
+ .replace(/-+$/, ''); // Trim - from end of text
10
+ }
@@ -0,0 +1,52 @@
1
+ import { AddressSchema } from './schemas/AddressSchema';
2
+ import { ContactSchema } from './schemas/ContactSchema';
3
+ import { timestampFields, contextFields, logFields } from './schemas/commonSchemaFields';
4
+ import { UserSchema } from './schemas/UsersSchema';
5
+
6
+ export { default as findLocalizedText } from './find-localized-text';
7
+ export * from './locale-helpers';
8
+ export { default as objectInvert } from './object-invert';
9
+ export { default as findUnusedSlug } from './find-unused-slug';
10
+ export { default as slugify } from './slugify';
11
+ export { default as pipePromises } from './pipe-promises';
12
+ export { default as generateRandomHash } from './generate-random-hash';
13
+ export { default as randomValueHex } from './random-value-hex';
14
+ export { default as buildSortOptions } from './buildSortOption';
15
+
16
+ /*
17
+ * Db utils
18
+ */
19
+
20
+ export { checkId } from './db/check-id';
21
+ export { generateDbObjectId } from './db/generate-db-object-id';
22
+ export { generateDbFilterById } from './db/generate-db-filter-by-id';
23
+ export { generateDbMutations } from './db/generate-db-mutations';
24
+ export { buildDbIndexes } from './db/build-db-indexes';
25
+ export { findPreservingIds } from './find-preserving-ids';
26
+
27
+ /*
28
+ * Schemas
29
+ */
30
+
31
+ const Schemas = {
32
+ timestampFields,
33
+ contextFields,
34
+ logFields,
35
+ Address: AddressSchema,
36
+ Contact: ContactSchema,
37
+ User: UserSchema,
38
+ };
39
+
40
+ export { Schemas };
41
+
42
+ /*
43
+ * Director
44
+ */
45
+
46
+ export { BaseAdapter } from './director/BaseAdapter';
47
+ export { BaseDirector } from './director/BaseDirector';
48
+ export { BasePricingAdapter } from './director/BasePricingAdapter';
49
+ export { BasePricingDirector } from './director/BasePricingDirector';
50
+ export { BasePricingSheet } from './director/BasePricingSheet';
51
+ export { BaseDiscountAdapter } from './director/BaseDiscountAdapter';
52
+ export { BaseDiscountDirector } from './director/BaseDiscountDirector';
package/tsconfig.json CHANGED
@@ -1,8 +1,29 @@
1
1
  {
2
- "extends": "./tsconfig.build.json",
3
2
  "compilerOptions": {
4
- "noEmit": true,
5
- "types": ["node", "mocha"],
3
+ "allowJs": true,
4
+ "allowSyntheticDefaultImports": true,
5
+ "declaration": true,
6
+ "esModuleInterop": true,
7
+ "experimentalDecorators": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "lib": [
10
+ "esnext"
11
+ ],
12
+ "module": "esnext",
13
+ "moduleResolution": "node",
14
+ "noImplicitReturns": true,
15
+ "noUnusedLocals": false,
16
+ "outDir": "lib",
17
+ "preserveWatchOutput": true,
18
+ "skipLibCheck": true,
19
+ "sourceMap": true,
20
+ "target": "esnext",
21
+ "types": [
22
+ "node",
23
+ "jest"
24
+ ]
6
25
  },
7
- "include": ["src", "tests"]
8
- }
26
+ "include": [
27
+ "src"
28
+ ]
29
+ }
@@ -1,26 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "allowJs": true,
4
- "allowSyntheticDefaultImports": true,
5
- "declaration": true,
6
- "esModuleInterop": true,
7
- "experimentalDecorators": true,
8
- "forceConsistentCasingInFileNames": true,
9
- "lib": ["esnext"],
10
- "module": "esnext",
11
- "moduleResolution": "node",
12
- "noImplicitReturns": true,
13
- "noUnusedLocals": false,
14
- "outDir": "lib",
15
- "preserveWatchOutput": true,
16
- "skipLibCheck": true,
17
- "sourceMap": true,
18
- "target": "esnext",
19
- "types": ["node"],
20
- "baseUrl": ".", // This must be specified if "paths" is.
21
- "paths": {
22
- "meteor/unchained:*": ["node_modules/@unchainedshop/types/index.d.ts"]
23
- }
24
- },
25
- "include": ["src"]
26
- }