@saasicat/core 1.0.0-rc.17 → 1.0.0-rc.19

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.
package/dist/.build-stamp CHANGED
@@ -1 +1 @@
1
- 5a529cc8f17f234940c3b0844d6f20fce18fadd196d3b3485274b711635819af
1
+ ddacfff58341810ccf3e690339f1e89ac04afebe5b2306f827ca21350b3a522b
package/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ __export(index_exports, {
30
30
  ERROR_MESSAGES_DE: () => ERROR_MESSAGES_DE,
31
31
  ERROR_MESSAGES_EN: () => ERROR_MESSAGES_EN,
32
32
  FEATURE_NOT_LICENSED: () => FEATURE_NOT_LICENSED,
33
+ LEGAL_IDENTITY_FIELDS: () => LEGAL_IDENTITY_FIELDS,
33
34
  MARKETING_PRIORITY_MAX: () => MARKETING_PRIORITY_MAX,
34
35
  MARKETING_PRIORITY_MIN: () => MARKETING_PRIORITY_MIN,
35
36
  OTP_RATE_LIMIT_MAX_SENDS: () => OTP_RATE_LIMIT_MAX_SENDS,
@@ -53,7 +54,6 @@ __export(index_exports, {
53
54
  SETTINGS_ERROR_CODES: () => SETTINGS_ERROR_CODES,
54
55
  SETUP_ERROR_CODES: () => SETUP_ERROR_CODES,
55
56
  SUBSCRIBER_ERROR_CODES: () => SUBSCRIBER_ERROR_CODES,
56
- SUBSCRIBER_IDENTITY_FIELDS: () => SUBSCRIBER_IDENTITY_FIELDS,
57
57
  applyPromo: () => applyPromo,
58
58
  assertPersistenceCapabilities: () => assertPersistenceCapabilities,
59
59
  buildActivePlanVersionWhere: () => buildActivePlanVersionWhere,
@@ -63,6 +63,7 @@ __export(index_exports, {
63
63
  bundleStemDefaults: () => bundleStemDefaults,
64
64
  canonicalJson: () => canonicalJson,
65
65
  classifyBundleVersionDiff: () => classifyBundleVersionDiff,
66
+ classifyIssuerChange: () => classifyIssuerChange,
66
67
  classifyPlanDiff: () => classifyPlanDiff,
67
68
  collectUnsatisfiedRequires: () => collectUnsatisfiedRequires,
68
69
  contractPartiesOf: () => contractPartiesOf,
@@ -76,16 +77,20 @@ __export(index_exports, {
76
77
  isPaymentCallbackRejectedError: () => isPaymentCallbackRejectedError,
77
78
  isPlatformUserExistsError: () => isPlatformUserExistsError,
78
79
  isVersionEditable: () => isVersionEditable,
80
+ issuerIdentityOf: () => issuerIdentityOf,
79
81
  keepOneRecommended: () => keepOneRecommended,
80
82
  missingRequiresFor: () => missingRequiresFor,
83
+ movedIdentityFields: () => movedIdentityFields,
81
84
  pickActivePromo: () => pickActivePromo,
82
85
  planCatalogSettingsOf: () => planCatalogSettingsOf,
83
86
  previousUtcDay: () => previousUtcDay,
84
87
  promoStatus: () => promoStatus,
85
88
  readQuotaRecord: () => readQuotaRecord,
86
89
  readQuotaValue: () => readQuotaValue,
90
+ recordedIssuerIdentity: () => recordedIssuerIdentity,
87
91
  resolveBundleAvailability: () => resolveBundleAvailability,
88
92
  resolveErrorMessage: () => resolveErrorMessage,
93
+ sameLegalIdentity: () => sameLegalIdentity,
89
94
  selectChargeableBundles: () => selectChargeableBundles,
90
95
  settingsSubtreeOf: () => settingsSubtreeOf,
91
96
  startOfUtcDay: () => startOfUtcDay,
@@ -95,6 +100,7 @@ __export(index_exports, {
95
100
  toContractLineItemRecord: () => toContractLineItemRecord,
96
101
  toPlanRow: () => toPlanRow,
97
102
  toPlanVersionRow: () => toPlanVersionRow,
103
+ toRunningContractIssuer: () => toRunningContractIssuer,
98
104
  toSubscriberCorrectionRecord: () => toSubscriberCorrectionRecord,
99
105
  toSubscriberPaymentMethodRecord: () => toSubscriberPaymentMethodRecord,
100
106
  toSubscriberRecord: () => toSubscriberRecord,
@@ -975,12 +981,86 @@ function toQuotaEffects(value) {
975
981
  return quotas;
976
982
  }
977
983
 
978
- // src/subscriber-mapping.ts
979
- var SUBSCRIBER_IDENTITY_FIELDS = [
984
+ // src/legal-identity.ts
985
+ var LEGAL_IDENTITY_FIELDS = [
980
986
  "legalName",
981
987
  "vatId",
982
988
  "taxNumber"
983
989
  ];
990
+ function sameLegalIdentity(before, after) {
991
+ return LEGAL_IDENTITY_FIELDS.every((field) => before[field] === after[field]);
992
+ }
993
+ function movedIdentityFields(before, after) {
994
+ return LEGAL_IDENTITY_FIELDS.filter((field) => before[field] !== after[field]);
995
+ }
996
+
997
+ // src/issuer-identity.ts
998
+ function issuerIdentityOf(issuer) {
999
+ if (!issuer) return null;
1000
+ return identityOf({
1001
+ legalName: issuer.legalName,
1002
+ vatId: issuer.vatId,
1003
+ taxNumber: issuer.taxNumber
1004
+ });
1005
+ }
1006
+ function recordedIssuerIdentity(settings) {
1007
+ const issuer = settings?.issuer;
1008
+ if (issuer === null || typeof issuer !== "object" || Array.isArray(issuer)) return null;
1009
+ return identityOf(issuer);
1010
+ }
1011
+ function identityOf(source) {
1012
+ const legalName = textOrNull(source.legalName);
1013
+ if (legalName === null) return null;
1014
+ return {
1015
+ legalName,
1016
+ vatId: textOrNull(source.vatId),
1017
+ taxNumber: textOrNull(source.taxNumber)
1018
+ };
1019
+ }
1020
+ function classifyIssuerChange(recorded, issuer) {
1021
+ const current = issuerIdentityOf(issuer);
1022
+ if (!recorded)
1023
+ return current ? { kind: "first-naming", identity: current } : { kind: "none-named" };
1024
+ if (current && sameLegalIdentity(recorded, current))
1025
+ return { kind: "unchanged", identity: current };
1026
+ if (!current) {
1027
+ const moved2 = LEGAL_IDENTITY_FIELDS.filter((field) => recorded[field] !== null);
1028
+ return {
1029
+ kind: "undeclared",
1030
+ recorded,
1031
+ current: null,
1032
+ moved: moved2,
1033
+ fault: { kind: "names-no-issuer" }
1034
+ };
1035
+ }
1036
+ const moved = movedIdentityFields(recorded, current);
1037
+ const fault = faultIn(issuer?.correctionOf, recorded, moved);
1038
+ if (fault) return { kind: "undeclared", recorded, current, moved, fault };
1039
+ const declaration = issuer?.correctionOf;
1040
+ return { kind: "corrected", recorded, current, moved, reason: declaration.reason };
1041
+ }
1042
+ function faultIn(declaration, recorded, moved) {
1043
+ if (!declaration) return { kind: "absent" };
1044
+ for (const field of LEGAL_IDENTITY_FIELDS) {
1045
+ if (!Object.prototype.hasOwnProperty.call(declaration, field)) continue;
1046
+ const declared = textOrNull(declaration[field]);
1047
+ if (declared !== recorded[field]) {
1048
+ return { kind: "names-another-value", field, declared, recorded: recorded[field] };
1049
+ }
1050
+ }
1051
+ for (const field of moved) {
1052
+ if (Object.prototype.hasOwnProperty.call(declaration, field)) continue;
1053
+ return { kind: "leaves-a-field-out", field, recorded: recorded[field] };
1054
+ }
1055
+ return null;
1056
+ }
1057
+ function textOrNull(value) {
1058
+ if (typeof value !== "string") return null;
1059
+ const trimmed = value.trim();
1060
+ return trimmed === "" ? null : trimmed;
1061
+ }
1062
+
1063
+ // src/subscriber-mapping.ts
984
1064
  function formatCustomerNumber(prefix, sequence) {
985
1065
  return `${prefix}${sequence}`;
986
1066
  }
@@ -1017,7 +1097,7 @@ function toSubscriberCorrectionRecord(row) {
1017
1097
  function identityCorrectionDelta(current, corrected) {
1018
1098
  const previous = {};
1019
1099
  const next = {};
1020
- for (const field of SUBSCRIBER_IDENTITY_FIELDS) {
1100
+ for (const field of LEGAL_IDENTITY_FIELDS) {
1021
1101
  const value = corrected[field];
1022
1102
  if (value === void 0 || value === current[field]) continue;
1023
1103
  previous[field] = current[field];
@@ -1047,17 +1127,26 @@ function contractPartiesOf(subscriber, issuer) {
1047
1127
  };
1048
1128
  }
1049
1129
  function issuerPartyOf(issuer) {
1130
+ const identity = issuerIdentityOf(issuer);
1050
1131
  return {
1051
- legalName: issuer.legalName,
1052
- vatId: issuer.vatId ?? null,
1053
- taxNumber: issuer.taxNumber ?? null,
1054
- addressLine1: issuer.addressLine1 ?? null,
1055
- addressLine2: issuer.addressLine2 ?? null,
1056
- postalCode: issuer.postalCode ?? null,
1057
- city: issuer.city ?? null,
1058
- country: issuer.country ?? null
1132
+ legalName: identity?.legalName ?? "",
1133
+ vatId: identity?.vatId ?? null,
1134
+ taxNumber: identity?.taxNumber ?? null,
1135
+ // Settled like the three above rather than copied verbatim: one copy
1136
+ // whose halves were settled differently is a row that reads two ways,
1137
+ // and a city written with a trailing space is not another city.
1138
+ addressLine1: settledText(issuer.addressLine1),
1139
+ addressLine2: settledText(issuer.addressLine2),
1140
+ postalCode: settledText(issuer.postalCode),
1141
+ city: settledText(issuer.city),
1142
+ country: settledText(issuer.country)
1059
1143
  };
1060
1144
  }
1145
+ function settledText(value) {
1146
+ if (typeof value !== "string") return null;
1147
+ const trimmed = value.trim();
1148
+ return trimmed === "" ? null : trimmed;
1149
+ }
1061
1150
  function subscriberFromRegistration(pending) {
1062
1151
  return {
1063
1152
  legalName: pending.tenantName,
@@ -1075,7 +1164,7 @@ function toIdentityValues(value) {
1075
1164
  if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
1076
1165
  const source = value;
1077
1166
  const values = {};
1078
- for (const field of SUBSCRIBER_IDENTITY_FIELDS) {
1167
+ for (const field of LEGAL_IDENTITY_FIELDS) {
1079
1168
  if (!Object.prototype.hasOwnProperty.call(source, field)) continue;
1080
1169
  const entry = source[field];
1081
1170
  values[field] = typeof entry === "string" ? entry : null;
@@ -1192,6 +1281,15 @@ function toSubscriberParty(value) {
1192
1281
  ...toIssuerParty(source)
1193
1282
  };
1194
1283
  }
1284
+ function toRunningContractIssuer(row) {
1285
+ const issuer = isPlainObject2(row.issuerSnapshot) ? row.issuerSnapshot : null;
1286
+ return {
1287
+ id: row.id,
1288
+ tenantId: row.tenantId,
1289
+ issuerLegalName: issuer ? stringOrNull(issuer.legalName) : null,
1290
+ effectiveFrom: row.effectiveFrom
1291
+ };
1292
+ }
1195
1293
  function toIssuerParty(value) {
1196
1294
  const source = value;
1197
1295
  return {
@@ -1683,6 +1781,7 @@ var ERROR_MESSAGES_DE = {
1683
1781
  ERROR_MESSAGES_DE,
1684
1782
  ERROR_MESSAGES_EN,
1685
1783
  FEATURE_NOT_LICENSED,
1784
+ LEGAL_IDENTITY_FIELDS,
1686
1785
  MARKETING_PRIORITY_MAX,
1687
1786
  MARKETING_PRIORITY_MIN,
1688
1787
  OTP_RATE_LIMIT_MAX_SENDS,
@@ -1706,7 +1805,6 @@ var ERROR_MESSAGES_DE = {
1706
1805
  SETTINGS_ERROR_CODES,
1707
1806
  SETUP_ERROR_CODES,
1708
1807
  SUBSCRIBER_ERROR_CODES,
1709
- SUBSCRIBER_IDENTITY_FIELDS,
1710
1808
  applyPromo,
1711
1809
  assertPersistenceCapabilities,
1712
1810
  buildActivePlanVersionWhere,
@@ -1716,6 +1814,7 @@ var ERROR_MESSAGES_DE = {
1716
1814
  bundleStemDefaults,
1717
1815
  canonicalJson,
1718
1816
  classifyBundleVersionDiff,
1817
+ classifyIssuerChange,
1719
1818
  classifyPlanDiff,
1720
1819
  collectUnsatisfiedRequires,
1721
1820
  contractPartiesOf,
@@ -1729,16 +1828,20 @@ var ERROR_MESSAGES_DE = {
1729
1828
  isPaymentCallbackRejectedError,
1730
1829
  isPlatformUserExistsError,
1731
1830
  isVersionEditable,
1831
+ issuerIdentityOf,
1732
1832
  keepOneRecommended,
1733
1833
  missingRequiresFor,
1834
+ movedIdentityFields,
1734
1835
  pickActivePromo,
1735
1836
  planCatalogSettingsOf,
1736
1837
  previousUtcDay,
1737
1838
  promoStatus,
1738
1839
  readQuotaRecord,
1739
1840
  readQuotaValue,
1841
+ recordedIssuerIdentity,
1740
1842
  resolveBundleAvailability,
1741
1843
  resolveErrorMessage,
1844
+ sameLegalIdentity,
1742
1845
  selectChargeableBundles,
1743
1846
  settingsSubtreeOf,
1744
1847
  startOfUtcDay,
@@ -1748,6 +1851,7 @@ var ERROR_MESSAGES_DE = {
1748
1851
  toContractLineItemRecord,
1749
1852
  toPlanRow,
1750
1853
  toPlanVersionRow,
1854
+ toRunningContractIssuer,
1751
1855
  toSubscriberCorrectionRecord,
1752
1856
  toSubscriberPaymentMethodRecord,
1753
1857
  toSubscriberRecord,