customer-registration 0.0.23 → 0.0.47

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 (32) hide show
  1. package/.medusa/server/src/api/auth/customer/emailpass/route.js +58 -0
  2. package/.medusa/server/src/api/store/customers/email/otp/resend/route.js +78 -0
  3. package/.medusa/server/src/api/store/customers/email/otp/verify/route.js +54 -40
  4. package/.medusa/server/src/api/store/customers/forget-password/otp/resend/route.js +52 -0
  5. package/.medusa/server/src/api/store/customers/forget-password/otp/verify/route.js +42 -0
  6. package/.medusa/server/src/api/store/customers/phone/otp/resend/route.js +81 -0
  7. package/.medusa/server/src/api/store/customers/phone/otp/verify/route.js +60 -41
  8. package/.medusa/server/src/errors/otp-errors.js +29 -0
  9. package/.medusa/server/src/loaders/index.js +29 -9
  10. package/.medusa/server/src/modules/customer-registration/index.js +43 -9
  11. package/.medusa/server/src/modules/customer-registration/migrations/Migration20251122112915AddEmailPhoneVerifiedColumns.js +67 -0
  12. package/.medusa/server/src/modules/customer-registration/migrations/Migration20251122112916CreateCustomerOtpTable.js +56 -0
  13. package/.medusa/server/src/modules/customer-registration/models/customer-otp.js +65 -32
  14. package/.medusa/server/src/modules/customer-registration/services/otp-service.js +226 -0
  15. package/.medusa/server/src/services/notification-service.js +81 -0
  16. package/.medusa/server/src/subscribers/customer-created.js +42 -0
  17. package/.medusa/server/src/types/plugin-options.js +30 -0
  18. package/.medusa/server/src/utils/crypto.js +52 -0
  19. package/.medusa/server/src/utils/customer-update.js +48 -0
  20. package/.medusa/server/src/utils/otp-generator.js +27 -0
  21. package/.medusa/server/src/utils/token-generator.js +11 -0
  22. package/README.md +156 -32
  23. package/package.json +3 -1
  24. package/.medusa/server/src/api/store/customers/phone/otp/send/route.js +0 -48
  25. package/.medusa/server/src/api/store/customers/route.js +0 -77
  26. package/.medusa/server/src/modules/customer-registration/__tests__/config.spec.js +0 -61
  27. package/.medusa/server/src/modules/customer-registration/config.js +0 -73
  28. package/.medusa/server/src/modules/customer-registration/constants.js +0 -5
  29. package/.medusa/server/src/modules/customer-registration/migrations/Migration20250118000000AddEmailVerifiedColumn.js +0 -21
  30. package/.medusa/server/src/modules/customer-registration/migrations/Migration20250118001000CreateCustomerOtpTable.js +0 -48
  31. package/.medusa/server/src/modules/customer-registration/service.js +0 -242
  32. package/.medusa/server/src/modules/customer-registration/types.js +0 -3
@@ -1,12 +1,32 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const awilix_1 = require("@medusajs/framework/awilix");
4
- const constants_1 = require("../modules/customer-registration/constants");
5
- const config_1 = require("../modules/customer-registration/config");
6
- exports.default = async ({ container, options }) => {
7
- const resolvedOptions = (0, config_1.buildCustomerRegistrationOptions)((options ?? {}));
8
- container.register({
9
- [constants_1.CUSTOMER_REGISTRATION_OPTIONS_KEY]: (0, awilix_1.asValue)(resolvedOptions),
10
- });
6
+ const plugin_options_1 = require("../types/plugin-options");
7
+ const customer_registration_1 = __importDefault(require("../modules/customer-registration"));
8
+ const customer_registration_2 = require("../modules/customer-registration");
9
+ exports.default = {
10
+ name: "customer-registration",
11
+ resolve: "customer-registration",
12
+ options: {},
13
+ moduleService: customer_registration_1.default,
14
+ load: async (container, options) => {
15
+ // Normalize and validate plugin options
16
+ const normalizedOptions = (0, plugin_options_1.normalizePluginOptions)(options);
17
+ // Register plugin options in container
18
+ container.register({
19
+ pluginOptions: {
20
+ resolve: () => normalizedOptions,
21
+ },
22
+ });
23
+ // Register module
24
+ if (customer_registration_1.default.load) {
25
+ customer_registration_1.default.load(container, normalizedOptions);
26
+ }
27
+ return {
28
+ [customer_registration_2.CUSTOMER_REGISTRATION_MODULE]: container.resolve(customer_registration_2.CUSTOMER_REGISTRATION_MODULE),
29
+ };
30
+ },
11
31
  };
12
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbG9hZGVycy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHVEQUFvRDtBQUVwRCwwRUFBOEY7QUFDOUYsb0VBQTBGO0FBRzFGLGtCQUFlLEtBQUssRUFBRSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQWlCLEVBQUUsRUFBRTtJQUM3RCxNQUFNLGVBQWUsR0FBRyxJQUFBLHlDQUFnQyxFQUN0RCxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQXNDLENBQ3JELENBQUE7SUFFRCxTQUFTLENBQUMsUUFBUSxDQUFDO1FBQ2pCLENBQUMsNkNBQWlDLENBQUMsRUFBRSxJQUFBLGdCQUFPLEVBQUMsZUFBZSxDQUFDO0tBQzlELENBQUMsQ0FBQTtBQUNKLENBQUMsQ0FBQSJ9
32
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbG9hZGVycy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLDREQUFvRjtBQUNwRiw2RkFBeUU7QUFDekUsNEVBQStFO0FBRS9FLGtCQUFlO0lBQ2IsSUFBSSxFQUFFLHVCQUF1QjtJQUM3QixPQUFPLEVBQUUsdUJBQXVCO0lBQ2hDLE9BQU8sRUFBRSxFQUFtQjtJQUM1QixhQUFhLEVBQUUsK0JBQTBCO0lBQ3pDLElBQUksRUFBRSxLQUFLLEVBQUUsU0FBYyxFQUFFLE9BQVksRUFBRSxFQUFFO1FBQzNDLHdDQUF3QztRQUN4QyxNQUFNLGlCQUFpQixHQUFHLElBQUEsdUNBQXNCLEVBQUMsT0FBd0IsQ0FBQyxDQUFBO1FBRTFFLHVDQUF1QztRQUN2QyxTQUFTLENBQUMsUUFBUSxDQUFDO1lBQ2pCLGFBQWEsRUFBRTtnQkFDYixPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUMsaUJBQWlCO2FBQ2pDO1NBQ0YsQ0FBQyxDQUFBO1FBRUYsa0JBQWtCO1FBQ2xCLElBQUksK0JBQTBCLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDcEMsK0JBQTBCLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxpQkFBaUIsQ0FBQyxDQUFBO1FBQy9ELENBQUM7UUFFRCxPQUFPO1lBQ0wsQ0FBQyxvREFBNEIsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxPQUFPLENBQUMsb0RBQTRCLENBQUM7U0FDaEYsQ0FBQTtJQUNILENBQUM7Q0FDRixDQUFBIn0=
@@ -1,13 +1,47 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
4
15
  };
5
16
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.CUSTOMER_REGISTRATION_MODULE = void 0;
17
+ exports.OTPService = exports.CustomerOtp = exports.CUSTOMER_REGISTRATION_MODULE = void 0;
7
18
  const utils_1 = require("@medusajs/framework/utils");
8
- const service_1 = __importDefault(require("./service"));
9
- exports.CUSTOMER_REGISTRATION_MODULE = "customer-registration";
10
- exports.default = (0, utils_1.Module)(exports.CUSTOMER_REGISTRATION_MODULE, {
11
- service: service_1.default,
12
- });
13
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9jdXN0b21lci1yZWdpc3RyYXRpb24vaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEscURBQWtEO0FBQ2xELHdEQUF5RDtBQUU1QyxRQUFBLDRCQUE0QixHQUFHLHVCQUF1QixDQUFBO0FBRW5FLGtCQUFlLElBQUEsY0FBTSxFQUFDLG9DQUE0QixFQUFFO0lBQ2xELE9BQU8sRUFBRSxpQkFBaUM7Q0FDM0MsQ0FBQyxDQUFBIn0=
19
+ const otp_service_1 = require("./services/otp-service");
20
+ Object.defineProperty(exports, "OTPService", { enumerable: true, get: function () { return otp_service_1.OTPService; } });
21
+ const customer_otp_1 = require("./models/customer-otp");
22
+ Object.defineProperty(exports, "CustomerOtp", { enumerable: true, get: function () { return customer_otp_1.CustomerOtp; } });
23
+ exports.CUSTOMER_REGISTRATION_MODULE = "customerRegistration";
24
+ exports.default = {
25
+ service: otp_service_1.OTPService,
26
+ load: (container, options) => {
27
+ const config = options;
28
+ container.register({
29
+ [exports.CUSTOMER_REGISTRATION_MODULE]: {
30
+ resolve: (cradle) => {
31
+ const customerService = cradle[utils_1.Modules.CUSTOMER];
32
+ const notificationService = cradle[utils_1.Modules.NOTIFICATION];
33
+ const manager = cradle.manager;
34
+ return new otp_service_1.OTPService({
35
+ customerService,
36
+ notificationService,
37
+ manager,
38
+ config,
39
+ });
40
+ },
41
+ },
42
+ });
43
+ },
44
+ };
45
+ __exportStar(require("./migrations/Migration20251122112915AddEmailPhoneVerifiedColumns"), exports);
46
+ __exportStar(require("./migrations/Migration20251122112916CreateCustomerOtpTable"), exports);
47
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9jdXN0b21lci1yZWdpc3RyYXRpb24vaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxxREFBbUQ7QUFDbkQsd0RBQW1EO0FBZ0M3QiwyRkFoQ2Isd0JBQVUsT0FnQ2E7QUEvQmhDLHdEQUFtRDtBQStCMUMsNEZBL0JBLDBCQUFXLE9BK0JBO0FBMUJQLFFBQUEsNEJBQTRCLEdBQUcsc0JBQXNCLENBQUE7QUFFbEUsa0JBQWU7SUFDYixPQUFPLEVBQUUsd0JBQVU7SUFDbkIsSUFBSSxFQUFFLENBQUMsU0FBYyxFQUFFLE9BQVksRUFBRSxFQUFFO1FBQ3JDLE1BQU0sTUFBTSxHQUFHLE9BQWtDLENBQUE7UUFFakQsU0FBUyxDQUFDLFFBQVEsQ0FBQztZQUNqQixDQUFDLG9DQUE0QixDQUFDLEVBQUU7Z0JBQzlCLE9BQU8sRUFBRSxDQUFDLE1BQVcsRUFBRSxFQUFFO29CQUN2QixNQUFNLGVBQWUsR0FBRyxNQUFNLENBQUMsZUFBTyxDQUFDLFFBQVEsQ0FBQyxDQUFBO29CQUNoRCxNQUFNLG1CQUFtQixHQUFHLE1BQU0sQ0FBQyxlQUFPLENBQUMsWUFBWSxDQUFDLENBQUE7b0JBQ3hELE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUE7b0JBRTlCLE9BQU8sSUFBSSx3QkFBVSxDQUFDO3dCQUNwQixlQUFlO3dCQUNmLG1CQUFtQjt3QkFDbkIsT0FBTzt3QkFDUCxNQUFNO3FCQUNQLENBQUMsQ0FBQTtnQkFDSixDQUFDO2FBQ0Y7U0FDRixDQUFDLENBQUE7SUFDSixDQUFDO0NBQ0YsQ0FBQTtBQUdELG1HQUFnRjtBQUNoRiw2RkFBMEUifQ==
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Migration20251122112915AddEmailPhoneVerifiedColumns = void 0;
4
+ const migrations_1 = require("@mikro-orm/migrations");
5
+ class Migration20251122112915AddEmailPhoneVerifiedColumns extends migrations_1.Migration {
6
+ async up() {
7
+ // Add email_verified column if it doesn't exist
8
+ this.addSql(`
9
+ do $$
10
+ begin
11
+ if not exists (
12
+ select 1 from information_schema.columns
13
+ where table_name = 'customer' and column_name = 'email_verified'
14
+ ) then
15
+ alter table "customer"
16
+ add column "email_verified" boolean not null default false;
17
+ end if;
18
+ end $$;
19
+ `);
20
+ // Add phone_verified column if it doesn't exist
21
+ this.addSql(`
22
+ do $$
23
+ begin
24
+ if not exists (
25
+ select 1 from information_schema.columns
26
+ where table_name = 'customer' and column_name = 'phone_verified'
27
+ ) then
28
+ alter table "customer"
29
+ add column "phone_verified" boolean not null default false;
30
+ end if;
31
+ end $$;
32
+ `);
33
+ // Create indexes for better query performance
34
+ this.addSql(`
35
+ create index if not exists "customer_email_verified_index" on "customer" ("email_verified");
36
+ `);
37
+ this.addSql(`
38
+ create index if not exists "customer_phone_verified_index" on "customer" ("phone_verified");
39
+ `);
40
+ }
41
+ async down() {
42
+ this.addSql(`
43
+ do $$
44
+ begin
45
+ if exists (
46
+ select 1 from information_schema.columns
47
+ where table_name = 'customer' and column_name = 'email_verified'
48
+ ) then
49
+ alter table "customer" drop column "email_verified";
50
+ end if;
51
+ end $$;
52
+ `);
53
+ this.addSql(`
54
+ do $$
55
+ begin
56
+ if exists (
57
+ select 1 from information_schema.columns
58
+ where table_name = 'customer' and column_name = 'phone_verified'
59
+ ) then
60
+ alter table "customer" drop column "phone_verified";
61
+ end if;
62
+ end $$;
63
+ `);
64
+ }
65
+ }
66
+ exports.Migration20251122112915AddEmailPhoneVerifiedColumns = Migration20251122112915AddEmailPhoneVerifiedColumns;
67
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uMjAyNTExMjIxMTI5MTVBZGRFbWFpbFBob25lVmVyaWZpZWRDb2x1bW5zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvY3VzdG9tZXItcmVnaXN0cmF0aW9uL21pZ3JhdGlvbnMvTWlncmF0aW9uMjAyNTExMjIxMTI5MTVBZGRFbWFpbFBob25lVmVyaWZpZWRDb2x1bW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHNEQUFpRDtBQUVqRCxNQUFhLG1EQUFvRCxTQUFRLHNCQUFTO0lBQ2hGLEtBQUssQ0FBQyxFQUFFO1FBQ04sZ0RBQWdEO1FBQ2hELElBQUksQ0FBQyxNQUFNLENBQUM7Ozs7Ozs7Ozs7O0tBV1gsQ0FBQyxDQUFBO1FBRUYsZ0RBQWdEO1FBQ2hELElBQUksQ0FBQyxNQUFNLENBQUM7Ozs7Ozs7Ozs7O0tBV1gsQ0FBQyxDQUFBO1FBRUYsOENBQThDO1FBQzlDLElBQUksQ0FBQyxNQUFNLENBQUM7O0tBRVgsQ0FBQyxDQUFBO1FBRUYsSUFBSSxDQUFDLE1BQU0sQ0FBQzs7S0FFWCxDQUFDLENBQUE7SUFDSixDQUFDO0lBRUQsS0FBSyxDQUFDLElBQUk7UUFDUixJQUFJLENBQUMsTUFBTSxDQUFDOzs7Ozs7Ozs7O0tBVVgsQ0FBQyxDQUFBO1FBRUYsSUFBSSxDQUFDLE1BQU0sQ0FBQzs7Ozs7Ozs7OztLQVVYLENBQUMsQ0FBQTtJQUNKLENBQUM7Q0FDRjtBQWpFRCxrSEFpRUMifQ==
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Migration20251122112916CreateCustomerOtpTable = void 0;
4
+ const migrations_1 = require("@mikro-orm/migrations");
5
+ class Migration20251122112916CreateCustomerOtpTable extends migrations_1.Migration {
6
+ async up() {
7
+ this.addSql(`
8
+ create table if not exists "customer_registration_otp" (
9
+ "id" text not null,
10
+ "customer_id" text null,
11
+ "channel_type" text check ("channel_type" in ('email', 'phone')) not null,
12
+ "address" text not null,
13
+ "otp_hash" text not null,
14
+ "otp_type" text check ("otp_type" in ('email_verification', 'phone_verification', 'forget_password')) not null,
15
+ "token" text not null,
16
+ "expires_at" timestamptz not null,
17
+ "verified_at" timestamptz null,
18
+ "attempts" integer not null default 0,
19
+ "created_at" timestamptz not null default now(),
20
+ "updated_at" timestamptz not null default now(),
21
+ constraint "customer_registration_otp_pkey" primary key ("id")
22
+ );
23
+ `);
24
+ this.addSql(`
25
+ create unique index if not exists "customer_registration_otp_token_unique" on "customer_registration_otp" ("token");
26
+ `);
27
+ this.addSql(`
28
+ create index if not exists "customer_registration_otp_customer_id_index" on "customer_registration_otp" ("customer_id");
29
+ `);
30
+ this.addSql(`
31
+ create index if not exists "customer_registration_otp_address_index" on "customer_registration_otp" ("address");
32
+ `);
33
+ this.addSql(`
34
+ create index if not exists "customer_registration_otp_channel_type_index" on "customer_registration_otp" ("channel_type");
35
+ `);
36
+ this.addSql(`
37
+ create index if not exists "customer_registration_otp_otp_type_index" on "customer_registration_otp" ("otp_type");
38
+ `);
39
+ // Add foreign key constraint to customer table if it exists
40
+ this.addSql(`
41
+ do $$
42
+ begin
43
+ if exists (select 1 from information_schema.tables where table_name = 'customer') then
44
+ alter table "customer_registration_otp"
45
+ add constraint "customer_registration_otp_customer_id_foreign"
46
+ foreign key ("customer_id") references "customer" ("id") on delete cascade;
47
+ end if;
48
+ end $$;
49
+ `);
50
+ }
51
+ async down() {
52
+ this.addSql(`drop table if exists "customer_registration_otp" cascade;`);
53
+ }
54
+ }
55
+ exports.Migration20251122112916CreateCustomerOtpTable = Migration20251122112916CreateCustomerOtpTable;
56
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uMjAyNTExMjIxMTI5MTZDcmVhdGVDdXN0b21lck90cFRhYmxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvY3VzdG9tZXItcmVnaXN0cmF0aW9uL21pZ3JhdGlvbnMvTWlncmF0aW9uMjAyNTExMjIxMTI5MTZDcmVhdGVDdXN0b21lck90cFRhYmxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHNEQUFpRDtBQUVqRCxNQUFhLDZDQUE4QyxTQUFRLHNCQUFTO0lBQzFFLEtBQUssQ0FBQyxFQUFFO1FBQ04sSUFBSSxDQUFDLE1BQU0sQ0FBQzs7Ozs7Ozs7Ozs7Ozs7OztLQWdCWCxDQUFDLENBQUE7UUFFRixJQUFJLENBQUMsTUFBTSxDQUFDOztLQUVYLENBQUMsQ0FBQTtRQUVGLElBQUksQ0FBQyxNQUFNLENBQUM7O0tBRVgsQ0FBQyxDQUFBO1FBRUYsSUFBSSxDQUFDLE1BQU0sQ0FBQzs7S0FFWCxDQUFDLENBQUE7UUFFRixJQUFJLENBQUMsTUFBTSxDQUFDOztLQUVYLENBQUMsQ0FBQTtRQUVGLElBQUksQ0FBQyxNQUFNLENBQUM7O0tBRVgsQ0FBQyxDQUFBO1FBRUYsNERBQTREO1FBQzVELElBQUksQ0FBQyxNQUFNLENBQUM7Ozs7Ozs7OztLQVNYLENBQUMsQ0FBQTtJQUNKLENBQUM7SUFFRCxLQUFLLENBQUMsSUFBSTtRQUNSLElBQUksQ0FBQyxNQUFNLENBQUMsMkRBQTJELENBQUMsQ0FBQTtJQUMxRSxDQUFDO0NBQ0Y7QUF4REQsc0dBd0RDIn0=
@@ -1,35 +1,68 @@
1
1
  "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.CustomerOtp = void 0;
4
- const utils_1 = require("@medusajs/framework/utils");
5
- exports.CustomerOtp = utils_1.model
6
- .define("customer_registration_otp", {
7
- id: utils_1.model.id({ prefix: "otp" }),
8
- customer_id: utils_1.model.text(),
9
- type: utils_1.model.enum(["email", "phone"]),
10
- code_hash: utils_1.model.text(),
11
- delivery_target: utils_1.model.text(),
12
- channel: utils_1.model.text(),
13
- attempt_count: utils_1.model.number().default(0),
14
- expires_at: utils_1.model.dateTime(),
15
- consumed_at: utils_1.model.dateTime().nullable(),
16
- metadata: utils_1.model.json().nullable(),
17
- })
18
- .indexes([
19
- {
20
- name: "customer_registration_otp_customer_type_idx",
21
- on: ["customer_id", "type"],
22
- where: "deleted_at IS NULL",
23
- },
24
- {
25
- name: "customer_registration_otp_expires_idx",
26
- on: ["expires_at"],
27
- where: "deleted_at IS NULL",
28
- },
29
- {
30
- name: "customer_registration_otp_consumed_idx",
31
- on: ["consumed_at"],
32
- where: "deleted_at IS NULL",
33
- },
34
- ]);
35
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItb3RwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvY3VzdG9tZXItcmVnaXN0cmF0aW9uL21vZGVscy9jdXN0b21lci1vdHAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEscURBQWlEO0FBRXBDLFFBQUEsV0FBVyxHQUFHLGFBQUs7S0FDN0IsTUFBTSxDQUFDLDJCQUEyQixFQUFFO0lBQ25DLEVBQUUsRUFBRSxhQUFLLENBQUMsRUFBRSxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxDQUFDO0lBQy9CLFdBQVcsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFO0lBQ3pCLElBQUksRUFBRSxhQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3BDLFNBQVMsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFO0lBQ3ZCLGVBQWUsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFO0lBQzdCLE9BQU8sRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFO0lBQ3JCLGFBQWEsRUFBRSxhQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUN4QyxVQUFVLEVBQUUsYUFBSyxDQUFDLFFBQVEsRUFBRTtJQUM1QixXQUFXLEVBQUUsYUFBSyxDQUFDLFFBQVEsRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUN4QyxRQUFRLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRSxDQUFDLFFBQVEsRUFBRTtDQUNsQyxDQUFDO0tBQ0QsT0FBTyxDQUFDO0lBQ1A7UUFDRSxJQUFJLEVBQUUsNkNBQTZDO1FBQ25ELEVBQUUsRUFBRSxDQUFDLGFBQWEsRUFBRSxNQUFNLENBQUM7UUFDM0IsS0FBSyxFQUFFLG9CQUFvQjtLQUM1QjtJQUNEO1FBQ0UsSUFBSSxFQUFFLHVDQUF1QztRQUM3QyxFQUFFLEVBQUUsQ0FBQyxZQUFZLENBQUM7UUFDbEIsS0FBSyxFQUFFLG9CQUFvQjtLQUM1QjtJQUNEO1FBQ0UsSUFBSSxFQUFFLHdDQUF3QztRQUM5QyxFQUFFLEVBQUUsQ0FBQyxhQUFhLENBQUM7UUFDbkIsS0FBSyxFQUFFLG9CQUFvQjtLQUM1QjtDQUNGLENBQUMsQ0FBQSJ9
13
+ const core_1 = require("@mikro-orm/core");
14
+ let CustomerOtp = class CustomerOtp {
15
+ };
16
+ exports.CustomerOtp = CustomerOtp;
17
+ __decorate([
18
+ (0, core_1.PrimaryKey)({ type: "text" }),
19
+ __metadata("design:type", String)
20
+ ], CustomerOtp.prototype, "id", void 0);
21
+ __decorate([
22
+ (0, core_1.Property)({ type: "text", nullable: true }),
23
+ __metadata("design:type", String)
24
+ ], CustomerOtp.prototype, "customer_id", void 0);
25
+ __decorate([
26
+ (0, core_1.Enum)(() => ["email", "phone"]),
27
+ __metadata("design:type", String)
28
+ ], CustomerOtp.prototype, "channel_type", void 0);
29
+ __decorate([
30
+ (0, core_1.Property)({ type: "text" }),
31
+ __metadata("design:type", String)
32
+ ], CustomerOtp.prototype, "address", void 0);
33
+ __decorate([
34
+ (0, core_1.Property)({ type: "text" }),
35
+ __metadata("design:type", String)
36
+ ], CustomerOtp.prototype, "otp_hash", void 0);
37
+ __decorate([
38
+ (0, core_1.Enum)(() => ["email_verification", "phone_verification", "forget_password"]),
39
+ __metadata("design:type", String)
40
+ ], CustomerOtp.prototype, "otp_type", void 0);
41
+ __decorate([
42
+ (0, core_1.Property)({ type: "text", unique: true }),
43
+ __metadata("design:type", String)
44
+ ], CustomerOtp.prototype, "token", void 0);
45
+ __decorate([
46
+ (0, core_1.Property)({ type: "timestamptz" }),
47
+ __metadata("design:type", Date)
48
+ ], CustomerOtp.prototype, "expires_at", void 0);
49
+ __decorate([
50
+ (0, core_1.Property)({ type: "timestamptz", nullable: true }),
51
+ __metadata("design:type", Date)
52
+ ], CustomerOtp.prototype, "verified_at", void 0);
53
+ __decorate([
54
+ (0, core_1.Property)({ type: "integer", default: 0 }),
55
+ __metadata("design:type", Number)
56
+ ], CustomerOtp.prototype, "attempts", void 0);
57
+ __decorate([
58
+ (0, core_1.Property)({ type: "timestamptz" }),
59
+ __metadata("design:type", Date)
60
+ ], CustomerOtp.prototype, "created_at", void 0);
61
+ __decorate([
62
+ (0, core_1.Property)({ type: "timestamptz", onUpdate: () => new Date() }),
63
+ __metadata("design:type", Date)
64
+ ], CustomerOtp.prototype, "updated_at", void 0);
65
+ exports.CustomerOtp = CustomerOtp = __decorate([
66
+ (0, core_1.Entity)({ tableName: "customer_registration_otp" })
67
+ ], CustomerOtp);
68
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItb3RwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvY3VzdG9tZXItcmVnaXN0cmF0aW9uL21vZGVscy9jdXN0b21lci1vdHAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBQUEsMENBS3dCO0FBTWpCLElBQU0sV0FBVyxHQUFqQixNQUFNLFdBQVc7Q0FvQ3ZCLENBQUE7QUFwQ1ksa0NBQVc7QUFFdEI7SUFEQyxJQUFBLGlCQUFVLEVBQUMsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLENBQUM7O3VDQUNsQjtBQUdYO0lBREMsSUFBQSxlQUFRLEVBQUMsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQzs7Z0RBQ3ZCO0FBR3BCO0lBREMsSUFBQSxXQUFJLEVBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7O2lEQUNMO0FBRzFCO0lBREMsSUFBQSxlQUFRLEVBQUMsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLENBQUM7OzRDQUNYO0FBR2hCO0lBREMsSUFBQSxlQUFRLEVBQUMsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLENBQUM7OzZDQUNWO0FBR2pCO0lBREMsSUFBQSxXQUFJLEVBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxvQkFBb0IsRUFBRSxvQkFBb0IsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDOzs2Q0FDMUQ7QUFHbEI7SUFEQyxJQUFBLGVBQVEsRUFBQyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxDQUFDOzswQ0FDM0I7QUFHZDtJQURDLElBQUEsZUFBUSxFQUFDLEVBQUUsSUFBSSxFQUFFLGFBQWEsRUFBRSxDQUFDOzhCQUNyQixJQUFJOytDQUFBO0FBR2pCO0lBREMsSUFBQSxlQUFRLEVBQUMsRUFBRSxJQUFJLEVBQUUsYUFBYSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQzs4QkFDcEMsSUFBSTtnREFBQTtBQUdsQjtJQURDLElBQUEsZUFBUSxFQUFDLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsQ0FBQyxFQUFFLENBQUM7OzZDQUN6QjtBQUdqQjtJQURDLElBQUEsZUFBUSxFQUFDLEVBQUUsSUFBSSxFQUFFLGFBQWEsRUFBRSxDQUFDOzhCQUNyQixJQUFJOytDQUFBO0FBR2pCO0lBREMsSUFBQSxlQUFRLEVBQUMsRUFBRSxJQUFJLEVBQUUsYUFBYSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLElBQUksRUFBRSxFQUFFLENBQUM7OEJBQ2pELElBQUk7K0NBQUE7c0JBbkNOLFdBQVc7SUFEdkIsSUFBQSxhQUFNLEVBQUMsRUFBRSxTQUFTLEVBQUUsMkJBQTJCLEVBQUUsQ0FBQztHQUN0QyxXQUFXLENBb0N2QiJ9
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OTPService = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const customer_otp_1 = require("../models/customer-otp");
6
+ const otp_generator_1 = require("../../../utils/otp-generator");
7
+ const crypto_1 = require("../../../utils/crypto");
8
+ const token_generator_1 = require("../../../utils/token-generator");
9
+ const otp_errors_1 = require("../../../errors/otp-errors");
10
+ class OTPService {
11
+ constructor({ customerService, notificationService, manager, config, }) {
12
+ this.customerService_ = customerService;
13
+ this.notificationService_ = notificationService;
14
+ this.manager_ = manager;
15
+ this.config_ = config;
16
+ }
17
+ /**
18
+ * Get the EntityManager instance (for use in API routes)
19
+ */
20
+ getManager() {
21
+ return this.manager_;
22
+ }
23
+ /**
24
+ * Generate OTP based on length and charset
25
+ */
26
+ generateOTP(length, charset) {
27
+ if (charset === "numeric") {
28
+ return (0, otp_generator_1.generateNumericOTP)(length);
29
+ }
30
+ return (0, otp_generator_1.generateAlphanumericOTP)(length);
31
+ }
32
+ /**
33
+ * Hash OTP
34
+ */
35
+ async hashOTP(otp) {
36
+ return await (0, crypto_1.hashOTP)(otp);
37
+ }
38
+ /**
39
+ * Verify OTP
40
+ */
41
+ async verifyOTP(input, hash) {
42
+ return await (0, crypto_1.compareOTP)(input, hash);
43
+ }
44
+ /**
45
+ * Create and store OTP record
46
+ */
47
+ async createOTP(customerId, channelType, address, otpType, config) {
48
+ try {
49
+ if (!address || address.trim().length === 0) {
50
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Address (email or phone) is required to create OTP");
51
+ }
52
+ // Generate OTP
53
+ const otp = this.generateOTP(config.otpLength, config.otpCharset);
54
+ const otpHash = await this.hashOTP(otp);
55
+ // Generate unique token
56
+ const token = (0, token_generator_1.generateToken)();
57
+ // Calculate expiry
58
+ const expiresAt = new Date();
59
+ expiresAt.setMinutes(expiresAt.getMinutes() + config.otpExpiryMinutes);
60
+ // Create OTP record
61
+ const otpRecord = this.manager_.create(customer_otp_1.CustomerOtp, {
62
+ id: `otp_${Date.now()}_${Math.random().toString(36).substring(7)}`,
63
+ customer_id: customerId ?? undefined,
64
+ channel_type: channelType,
65
+ address: address.trim(),
66
+ otp_hash: otpHash,
67
+ otp_type: otpType,
68
+ token,
69
+ expires_at: expiresAt,
70
+ attempts: 0,
71
+ created_at: new Date(),
72
+ updated_at: new Date(),
73
+ });
74
+ await this.manager_.persistAndFlush(otpRecord);
75
+ return { otp, token, expiresAt };
76
+ }
77
+ catch (error) {
78
+ if (error instanceof utils_1.MedusaError) {
79
+ throw error;
80
+ }
81
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to create OTP: ${error instanceof Error ? error.message : "Unknown error"}`);
82
+ }
83
+ }
84
+ /**
85
+ * Verify OTP using token
86
+ */
87
+ async verifyOTPByToken(token, code, otpType, config) {
88
+ try {
89
+ if (!token || !code) {
90
+ throw new otp_errors_1.OTPInvalidError("Token and code are required");
91
+ }
92
+ const otpRecord = await this.manager_.findOne(customer_otp_1.CustomerOtp, {
93
+ token,
94
+ otp_type: otpType,
95
+ });
96
+ if (!otpRecord) {
97
+ throw new otp_errors_1.OTPInvalidError("OTP not found or invalid token");
98
+ }
99
+ // Check if expired
100
+ if (new Date() > otpRecord.expires_at) {
101
+ throw new otp_errors_1.OTPExpiredError("OTP has expired. Please request a new one.");
102
+ }
103
+ // Check if already verified
104
+ if (otpRecord.verified_at) {
105
+ throw new otp_errors_1.OTPInvalidError("OTP has already been verified");
106
+ }
107
+ // Check max attempts
108
+ if (otpRecord.attempts >= config.maxAttempts) {
109
+ throw new otp_errors_1.OTPMaxAttemptsError(`Maximum verification attempts (${config.maxAttempts}) exceeded. Please request a new OTP.`);
110
+ }
111
+ // Verify OTP
112
+ const isValid = await this.verifyOTP(code, otpRecord.otp_hash);
113
+ // Increment attempts before checking validity to prevent timing attacks
114
+ otpRecord.attempts += 1;
115
+ await this.manager_.flush();
116
+ if (!isValid) {
117
+ const remainingAttempts = config.maxAttempts - otpRecord.attempts;
118
+ throw new otp_errors_1.OTPInvalidError(remainingAttempts > 0
119
+ ? `Invalid OTP code. ${remainingAttempts} attempt(s) remaining.`
120
+ : "Invalid OTP code. Maximum attempts reached.");
121
+ }
122
+ // Mark as verified
123
+ otpRecord.verified_at = new Date();
124
+ await this.manager_.flush();
125
+ return {
126
+ verified: true,
127
+ customerId: otpRecord.customer_id ?? null,
128
+ };
129
+ }
130
+ catch (error) {
131
+ // Re-throw custom errors as-is
132
+ if (error instanceof otp_errors_1.OTPExpiredError ||
133
+ error instanceof otp_errors_1.OTPInvalidError ||
134
+ error instanceof otp_errors_1.OTPMaxAttemptsError ||
135
+ error instanceof otp_errors_1.OTPThrottleError) {
136
+ throw error;
137
+ }
138
+ // Wrap unexpected errors
139
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to verify OTP: ${error instanceof Error ? error.message : "Unknown error"}`);
140
+ }
141
+ }
142
+ /**
143
+ * Resend OTP for existing token
144
+ */
145
+ async resendOTP(token, config) {
146
+ try {
147
+ if (!token) {
148
+ throw new otp_errors_1.OTPInvalidError("Token is required to resend OTP");
149
+ }
150
+ const existingRecord = await this.manager_.findOne(customer_otp_1.CustomerOtp, { token });
151
+ if (!existingRecord) {
152
+ throw new otp_errors_1.OTPInvalidError("OTP token not found");
153
+ }
154
+ // Check throttle
155
+ await this.checkThrottle(existingRecord.channel_type, existingRecord.address, existingRecord.otp_type, config);
156
+ // Generate new OTP
157
+ const otp = this.generateOTP(config.otpLength, config.otpCharset);
158
+ const otpHash = await this.hashOTP(otp);
159
+ // Generate new token
160
+ const newToken = (0, token_generator_1.generateToken)();
161
+ // Calculate new expiry
162
+ const expiresAt = new Date();
163
+ expiresAt.setMinutes(expiresAt.getMinutes() + config.otpExpiryMinutes);
164
+ // Update existing record
165
+ existingRecord.otp_hash = otpHash;
166
+ existingRecord.token = newToken;
167
+ existingRecord.expires_at = expiresAt;
168
+ existingRecord.attempts = 0;
169
+ existingRecord.verified_at = undefined;
170
+ existingRecord.updated_at = new Date();
171
+ await this.manager_.flush();
172
+ return { otp, token: newToken, expiresAt };
173
+ }
174
+ catch (error) {
175
+ // Re-throw custom errors as-is
176
+ if (error instanceof otp_errors_1.OTPInvalidError ||
177
+ error instanceof otp_errors_1.OTPThrottleError) {
178
+ throw error;
179
+ }
180
+ // Wrap unexpected errors
181
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to resend OTP: ${error instanceof Error ? error.message : "Unknown error"}`);
182
+ }
183
+ }
184
+ /**
185
+ * Check if resend is allowed (throttle check)
186
+ */
187
+ async checkThrottle(channelType, address, otpType, config) {
188
+ let throttleSeconds;
189
+ switch (otpType) {
190
+ case "email_verification":
191
+ throttleSeconds = config.email.resendThrottleSeconds;
192
+ break;
193
+ case "phone_verification":
194
+ throttleSeconds = config.phone.resendThrottleSeconds;
195
+ break;
196
+ case "forget_password":
197
+ throttleSeconds = config.forgetPassword.resendThrottleSeconds;
198
+ break;
199
+ default:
200
+ throttleSeconds = 60;
201
+ }
202
+ const throttleTime = new Date();
203
+ throttleTime.setSeconds(throttleTime.getSeconds() - throttleSeconds);
204
+ const recentOTP = await this.manager_.findOne(customer_otp_1.CustomerOtp, {
205
+ channel_type: channelType,
206
+ address,
207
+ otp_type: otpType,
208
+ created_at: { $gte: throttleTime },
209
+ }, { orderBy: { created_at: "DESC" } });
210
+ if (recentOTP) {
211
+ throw new otp_errors_1.OTPThrottleError(`Please wait ${throttleSeconds} seconds before requesting a new OTP`);
212
+ }
213
+ }
214
+ /**
215
+ * Cleanup expired OTPs
216
+ */
217
+ async cleanupExpiredOTPs() {
218
+ const now = new Date();
219
+ await this.manager_.nativeDelete(customer_otp_1.CustomerOtp, {
220
+ expires_at: { $lt: now },
221
+ verified_at: null,
222
+ });
223
+ }
224
+ }
225
+ exports.OTPService = OTPService;
226
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3RwLXNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9jdXN0b21lci1yZWdpc3RyYXRpb24vc2VydmljZXMvb3RwLXNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQ0EscURBQWdFO0FBRWhFLHlEQUEwRTtBQUMxRSxnRUFBMEY7QUFDMUYsa0RBQTJEO0FBQzNELG9FQUE4RDtBQUU5RCwyREFLbUM7QUFFbkMsTUFBYSxVQUFVO0lBTXJCLFlBQ0UsRUFDRSxlQUFlLEVBQ2YsbUJBQW1CLEVBQ25CLE9BQU8sRUFDUCxNQUFNLEdBTVA7UUFFRCxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsZUFBZSxDQUFBO1FBQ3ZDLElBQUksQ0FBQyxvQkFBb0IsR0FBRyxtQkFBbUIsQ0FBQTtRQUMvQyxJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQTtRQUN2QixJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQTtJQUN2QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxVQUFVO1FBQ1IsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFBO0lBQ3RCLENBQUM7SUFFRDs7T0FFRztJQUNILFdBQVcsQ0FBQyxNQUFjLEVBQUUsT0FBbUI7UUFDN0MsSUFBSSxPQUFPLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDMUIsT0FBTyxJQUFBLGtDQUFrQixFQUFDLE1BQU0sQ0FBQyxDQUFBO1FBQ25DLENBQUM7UUFDRCxPQUFPLElBQUEsdUNBQXVCLEVBQUMsTUFBTSxDQUFDLENBQUE7SUFDeEMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFXO1FBQ3ZCLE9BQU8sTUFBTSxJQUFBLGdCQUFPLEVBQUMsR0FBRyxDQUFDLENBQUE7SUFDM0IsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLFNBQVMsQ0FBQyxLQUFhLEVBQUUsSUFBWTtRQUN6QyxPQUFPLE1BQU0sSUFBQSxtQkFBVSxFQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsQ0FBQTtJQUN0QyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsU0FBUyxDQUNiLFVBQXlCLEVBQ3pCLFdBQXdCLEVBQ3hCLE9BQWUsRUFDZixPQUFnQixFQUNoQixNQUErQjtRQUUvQixJQUFJLENBQUM7WUFDSCxJQUFJLENBQUMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQzVDLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLG9EQUFvRCxDQUNyRCxDQUFBO1lBQ0gsQ0FBQztZQUVELGVBQWU7WUFDZixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFBO1lBQ2pFLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQTtZQUV2Qyx3QkFBd0I7WUFDeEIsTUFBTSxLQUFLLEdBQUcsSUFBQSwrQkFBYSxHQUFFLENBQUE7WUFFN0IsbUJBQW1CO1lBQ25CLE1BQU0sU0FBUyxHQUFHLElBQUksSUFBSSxFQUFFLENBQUE7WUFDNUIsU0FBUyxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsVUFBVSxFQUFFLEdBQUcsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUE7WUFFdEUsb0JBQW9CO1lBQ3BCLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLDBCQUFXLEVBQUU7Z0JBQ2xELEVBQUUsRUFBRSxPQUFPLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRTtnQkFDbEUsV0FBVyxFQUFFLFVBQVUsSUFBSSxTQUFTO2dCQUNwQyxZQUFZLEVBQUUsV0FBVztnQkFDekIsT0FBTyxFQUFFLE9BQU8sQ0FBQyxJQUFJLEVBQUU7Z0JBQ3ZCLFFBQVEsRUFBRSxPQUFPO2dCQUNqQixRQUFRLEVBQUUsT0FBTztnQkFDakIsS0FBSztnQkFDTCxVQUFVLEVBQUUsU0FBUztnQkFDckIsUUFBUSxFQUFFLENBQUM7Z0JBQ1gsVUFBVSxFQUFFLElBQUksSUFBSSxFQUFFO2dCQUN0QixVQUFVLEVBQUUsSUFBSSxJQUFJLEVBQUU7YUFDdkIsQ0FBQyxDQUFBO1lBRUYsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLGVBQWUsQ0FBQyxTQUFTLENBQUMsQ0FBQTtZQUU5QyxPQUFPLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsQ0FBQTtRQUNsQyxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksS0FBSyxZQUFZLG1CQUFXLEVBQUUsQ0FBQztnQkFDakMsTUFBTSxLQUFLLENBQUE7WUFDYixDQUFDO1lBQ0QsTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLGdCQUFnQixFQUNsQyx5QkFBeUIsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsZUFBZSxFQUFFLENBQ3BGLENBQUE7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLGdCQUFnQixDQUNwQixLQUFhLEVBQ2IsSUFBWSxFQUNaLE9BQWdCLEVBQ2hCLE1BQStCO1FBRS9CLElBQUksQ0FBQztZQUNILElBQUksQ0FBQyxLQUFLLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztnQkFDcEIsTUFBTSxJQUFJLDRCQUFlLENBQUMsNkJBQTZCLENBQUMsQ0FBQTtZQUMxRCxDQUFDO1lBRUQsTUFBTSxTQUFTLEdBQUcsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQywwQkFBVyxFQUFFO2dCQUN6RCxLQUFLO2dCQUNMLFFBQVEsRUFBRSxPQUFPO2FBQ2xCLENBQUMsQ0FBQTtZQUVGLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDZixNQUFNLElBQUksNEJBQWUsQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFBO1lBQzdELENBQUM7WUFFRCxtQkFBbUI7WUFDbkIsSUFBSSxJQUFJLElBQUksRUFBRSxHQUFHLFNBQVMsQ0FBQyxVQUFVLEVBQUUsQ0FBQztnQkFDdEMsTUFBTSxJQUFJLDRCQUFlLENBQUMsNENBQTRDLENBQUMsQ0FBQTtZQUN6RSxDQUFDO1lBRUQsNEJBQTRCO1lBQzVCLElBQUksU0FBUyxDQUFDLFdBQVcsRUFBRSxDQUFDO2dCQUMxQixNQUFNLElBQUksNEJBQWUsQ0FBQywrQkFBK0IsQ0FBQyxDQUFBO1lBQzVELENBQUM7WUFFRCxxQkFBcUI7WUFDckIsSUFBSSxTQUFTLENBQUMsUUFBUSxJQUFJLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDN0MsTUFBTSxJQUFJLGdDQUFtQixDQUMzQixrQ0FBa0MsTUFBTSxDQUFDLFdBQVcsdUNBQXVDLENBQzVGLENBQUE7WUFDSCxDQUFDO1lBRUQsYUFBYTtZQUNiLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFBO1lBRTlELHdFQUF3RTtZQUN4RSxTQUFTLENBQUMsUUFBUSxJQUFJLENBQUMsQ0FBQTtZQUN2QixNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUE7WUFFM0IsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO2dCQUNiLE1BQU0saUJBQWlCLEdBQUcsTUFBTSxDQUFDLFdBQVcsR0FBRyxTQUFTLENBQUMsUUFBUSxDQUFBO2dCQUNqRSxNQUFNLElBQUksNEJBQWUsQ0FDdkIsaUJBQWlCLEdBQUcsQ0FBQztvQkFDbkIsQ0FBQyxDQUFDLHFCQUFxQixpQkFBaUIsd0JBQXdCO29CQUNoRSxDQUFDLENBQUMsNkNBQTZDLENBQ2xELENBQUE7WUFDSCxDQUFDO1lBRUQsbUJBQW1CO1lBQ25CLFNBQVMsQ0FBQyxXQUFXLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQTtZQUNsQyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUE7WUFFM0IsT0FBTztnQkFDTCxRQUFRLEVBQUUsSUFBSTtnQkFDZCxVQUFVLEVBQUUsU0FBUyxDQUFDLFdBQVcsSUFBSSxJQUFJO2FBQzFDLENBQUE7UUFDSCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLCtCQUErQjtZQUMvQixJQUNFLEtBQUssWUFBWSw0QkFBZTtnQkFDaEMsS0FBSyxZQUFZLDRCQUFlO2dCQUNoQyxLQUFLLFlBQVksZ0NBQW1CO2dCQUNwQyxLQUFLLFlBQVksNkJBQWdCLEVBQ2pDLENBQUM7Z0JBQ0QsTUFBTSxLQUFLLENBQUE7WUFDYixDQUFDO1lBQ0QseUJBQXlCO1lBQ3pCLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsRUFDbEMseUJBQXlCLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLGVBQWUsRUFBRSxDQUNwRixDQUFBO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxTQUFTLENBQ2IsS0FBYSxFQUNiLE1BQStCO1FBRS9CLElBQUksQ0FBQztZQUNILElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDWCxNQUFNLElBQUksNEJBQWUsQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFBO1lBQzlELENBQUM7WUFFRCxNQUFNLGNBQWMsR0FBRyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLDBCQUFXLEVBQUUsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFBO1lBRTFFLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDcEIsTUFBTSxJQUFJLDRCQUFlLENBQUMscUJBQXFCLENBQUMsQ0FBQTtZQUNsRCxDQUFDO1lBRUQsaUJBQWlCO1lBQ2pCLE1BQU0sSUFBSSxDQUFDLGFBQWEsQ0FDdEIsY0FBYyxDQUFDLFlBQVksRUFDM0IsY0FBYyxDQUFDLE9BQU8sRUFDdEIsY0FBYyxDQUFDLFFBQVEsRUFDdkIsTUFBTSxDQUNQLENBQUE7WUFFRCxtQkFBbUI7WUFDbkIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQTtZQUNqRSxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUE7WUFFdkMscUJBQXFCO1lBQ3JCLE1BQU0sUUFBUSxHQUFHLElBQUEsK0JBQWEsR0FBRSxDQUFBO1lBRWhDLHVCQUF1QjtZQUN2QixNQUFNLFNBQVMsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFBO1lBQzVCLFNBQVMsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLFVBQVUsRUFBRSxHQUFHLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFBO1lBRXRFLHlCQUF5QjtZQUN6QixjQUFjLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQTtZQUNqQyxjQUFjLENBQUMsS0FBSyxHQUFHLFFBQVEsQ0FBQTtZQUMvQixjQUFjLENBQUMsVUFBVSxHQUFHLFNBQVMsQ0FBQTtZQUNyQyxjQUFjLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQTtZQUMzQixjQUFjLENBQUMsV0FBVyxHQUFHLFNBQVMsQ0FBQTtZQUN0QyxjQUFjLENBQUMsVUFBVSxHQUFHLElBQUksSUFBSSxFQUFFLENBQUE7WUFFdEMsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFBO1lBRTNCLE9BQU8sRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQTtRQUM1QyxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLCtCQUErQjtZQUMvQixJQUNFLEtBQUssWUFBWSw0QkFBZTtnQkFDaEMsS0FBSyxZQUFZLDZCQUFnQixFQUNqQyxDQUFDO2dCQUNELE1BQU0sS0FBSyxDQUFBO1lBQ2IsQ0FBQztZQUNELHlCQUF5QjtZQUN6QixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLEVBQ2xDLHlCQUF5QixLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxlQUFlLEVBQUUsQ0FDcEYsQ0FBQTtRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsYUFBYSxDQUNqQixXQUF3QixFQUN4QixPQUFlLEVBQ2YsT0FBZ0IsRUFDaEIsTUFBK0I7UUFFL0IsSUFBSSxlQUF1QixDQUFBO1FBRTNCLFFBQVEsT0FBTyxFQUFFLENBQUM7WUFDaEIsS0FBSyxvQkFBb0I7Z0JBQ3ZCLGVBQWUsR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLHFCQUFxQixDQUFBO2dCQUNwRCxNQUFLO1lBQ1AsS0FBSyxvQkFBb0I7Z0JBQ3ZCLGVBQWUsR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLHFCQUFxQixDQUFBO2dCQUNwRCxNQUFLO1lBQ1AsS0FBSyxpQkFBaUI7Z0JBQ3BCLGVBQWUsR0FBRyxNQUFNLENBQUMsY0FBYyxDQUFDLHFCQUFxQixDQUFBO2dCQUM3RCxNQUFLO1lBQ1A7Z0JBQ0UsZUFBZSxHQUFHLEVBQUUsQ0FBQTtRQUN4QixDQUFDO1FBRUQsTUFBTSxZQUFZLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQTtRQUMvQixZQUFZLENBQUMsVUFBVSxDQUFDLFlBQVksQ0FBQyxVQUFVLEVBQUUsR0FBRyxlQUFlLENBQUMsQ0FBQTtRQUVwRSxNQUFNLFNBQVMsR0FBRyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUMzQywwQkFBVyxFQUNYO1lBQ0UsWUFBWSxFQUFFLFdBQVc7WUFDekIsT0FBTztZQUNQLFFBQVEsRUFBRSxPQUFPO1lBQ2pCLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUU7U0FDbkMsRUFDRCxFQUFFLE9BQU8sRUFBRSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsRUFBRSxDQUNwQyxDQUFBO1FBRUQsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNkLE1BQU0sSUFBSSw2QkFBZ0IsQ0FDeEIsZUFBZSxlQUFlLHNDQUFzQyxDQUNyRSxDQUFBO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxrQkFBa0I7UUFDdEIsTUFBTSxHQUFHLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQTtRQUN0QixNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLDBCQUFXLEVBQUU7WUFDNUMsVUFBVSxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRTtZQUN4QixXQUFXLEVBQUUsSUFBSTtTQUNsQixDQUFDLENBQUE7SUFDSixDQUFDO0NBQ0Y7QUE1VEQsZ0NBNFRDIn0=