@saasicat/core 1.0.0-rc.18 → 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 +1 -1
- package/dist/index.cjs +118 -14
- package/dist/index.d.cts +313 -105
- package/dist/index.d.ts +313 -105
- package/dist/index.js +111 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -871,12 +871,86 @@ function toQuotaEffects(value) {
|
|
|
871
871
|
return quotas;
|
|
872
872
|
}
|
|
873
873
|
|
|
874
|
-
// src/
|
|
875
|
-
var
|
|
874
|
+
// src/legal-identity.ts
|
|
875
|
+
var LEGAL_IDENTITY_FIELDS = [
|
|
876
876
|
"legalName",
|
|
877
877
|
"vatId",
|
|
878
878
|
"taxNumber"
|
|
879
879
|
];
|
|
880
|
+
function sameLegalIdentity(before, after) {
|
|
881
|
+
return LEGAL_IDENTITY_FIELDS.every((field) => before[field] === after[field]);
|
|
882
|
+
}
|
|
883
|
+
function movedIdentityFields(before, after) {
|
|
884
|
+
return LEGAL_IDENTITY_FIELDS.filter((field) => before[field] !== after[field]);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// src/issuer-identity.ts
|
|
888
|
+
function issuerIdentityOf(issuer) {
|
|
889
|
+
if (!issuer) return null;
|
|
890
|
+
return identityOf({
|
|
891
|
+
legalName: issuer.legalName,
|
|
892
|
+
vatId: issuer.vatId,
|
|
893
|
+
taxNumber: issuer.taxNumber
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
function recordedIssuerIdentity(settings) {
|
|
897
|
+
const issuer = settings?.issuer;
|
|
898
|
+
if (issuer === null || typeof issuer !== "object" || Array.isArray(issuer)) return null;
|
|
899
|
+
return identityOf(issuer);
|
|
900
|
+
}
|
|
901
|
+
function identityOf(source) {
|
|
902
|
+
const legalName = textOrNull(source.legalName);
|
|
903
|
+
if (legalName === null) return null;
|
|
904
|
+
return {
|
|
905
|
+
legalName,
|
|
906
|
+
vatId: textOrNull(source.vatId),
|
|
907
|
+
taxNumber: textOrNull(source.taxNumber)
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
function classifyIssuerChange(recorded, issuer) {
|
|
911
|
+
const current = issuerIdentityOf(issuer);
|
|
912
|
+
if (!recorded)
|
|
913
|
+
return current ? { kind: "first-naming", identity: current } : { kind: "none-named" };
|
|
914
|
+
if (current && sameLegalIdentity(recorded, current))
|
|
915
|
+
return { kind: "unchanged", identity: current };
|
|
916
|
+
if (!current) {
|
|
917
|
+
const moved2 = LEGAL_IDENTITY_FIELDS.filter((field) => recorded[field] !== null);
|
|
918
|
+
return {
|
|
919
|
+
kind: "undeclared",
|
|
920
|
+
recorded,
|
|
921
|
+
current: null,
|
|
922
|
+
moved: moved2,
|
|
923
|
+
fault: { kind: "names-no-issuer" }
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
const moved = movedIdentityFields(recorded, current);
|
|
927
|
+
const fault = faultIn(issuer?.correctionOf, recorded, moved);
|
|
928
|
+
if (fault) return { kind: "undeclared", recorded, current, moved, fault };
|
|
929
|
+
const declaration = issuer?.correctionOf;
|
|
930
|
+
return { kind: "corrected", recorded, current, moved, reason: declaration.reason };
|
|
931
|
+
}
|
|
932
|
+
function faultIn(declaration, recorded, moved) {
|
|
933
|
+
if (!declaration) return { kind: "absent" };
|
|
934
|
+
for (const field of LEGAL_IDENTITY_FIELDS) {
|
|
935
|
+
if (!Object.prototype.hasOwnProperty.call(declaration, field)) continue;
|
|
936
|
+
const declared = textOrNull(declaration[field]);
|
|
937
|
+
if (declared !== recorded[field]) {
|
|
938
|
+
return { kind: "names-another-value", field, declared, recorded: recorded[field] };
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
for (const field of moved) {
|
|
942
|
+
if (Object.prototype.hasOwnProperty.call(declaration, field)) continue;
|
|
943
|
+
return { kind: "leaves-a-field-out", field, recorded: recorded[field] };
|
|
944
|
+
}
|
|
945
|
+
return null;
|
|
946
|
+
}
|
|
947
|
+
function textOrNull(value) {
|
|
948
|
+
if (typeof value !== "string") return null;
|
|
949
|
+
const trimmed = value.trim();
|
|
950
|
+
return trimmed === "" ? null : trimmed;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// src/subscriber-mapping.ts
|
|
880
954
|
function formatCustomerNumber(prefix, sequence) {
|
|
881
955
|
return `${prefix}${sequence}`;
|
|
882
956
|
}
|
|
@@ -913,7 +987,7 @@ function toSubscriberCorrectionRecord(row) {
|
|
|
913
987
|
function identityCorrectionDelta(current, corrected) {
|
|
914
988
|
const previous = {};
|
|
915
989
|
const next = {};
|
|
916
|
-
for (const field of
|
|
990
|
+
for (const field of LEGAL_IDENTITY_FIELDS) {
|
|
917
991
|
const value = corrected[field];
|
|
918
992
|
if (value === void 0 || value === current[field]) continue;
|
|
919
993
|
previous[field] = current[field];
|
|
@@ -943,17 +1017,26 @@ function contractPartiesOf(subscriber, issuer) {
|
|
|
943
1017
|
};
|
|
944
1018
|
}
|
|
945
1019
|
function issuerPartyOf(issuer) {
|
|
1020
|
+
const identity = issuerIdentityOf(issuer);
|
|
946
1021
|
return {
|
|
947
|
-
legalName:
|
|
948
|
-
vatId:
|
|
949
|
-
taxNumber:
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1022
|
+
legalName: identity?.legalName ?? "",
|
|
1023
|
+
vatId: identity?.vatId ?? null,
|
|
1024
|
+
taxNumber: identity?.taxNumber ?? null,
|
|
1025
|
+
// Settled like the three above rather than copied verbatim: one copy
|
|
1026
|
+
// whose halves were settled differently is a row that reads two ways,
|
|
1027
|
+
// and a city written with a trailing space is not another city.
|
|
1028
|
+
addressLine1: settledText(issuer.addressLine1),
|
|
1029
|
+
addressLine2: settledText(issuer.addressLine2),
|
|
1030
|
+
postalCode: settledText(issuer.postalCode),
|
|
1031
|
+
city: settledText(issuer.city),
|
|
1032
|
+
country: settledText(issuer.country)
|
|
955
1033
|
};
|
|
956
1034
|
}
|
|
1035
|
+
function settledText(value) {
|
|
1036
|
+
if (typeof value !== "string") return null;
|
|
1037
|
+
const trimmed = value.trim();
|
|
1038
|
+
return trimmed === "" ? null : trimmed;
|
|
1039
|
+
}
|
|
957
1040
|
function subscriberFromRegistration(pending) {
|
|
958
1041
|
return {
|
|
959
1042
|
legalName: pending.tenantName,
|
|
@@ -971,7 +1054,7 @@ function toIdentityValues(value) {
|
|
|
971
1054
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
|
|
972
1055
|
const source = value;
|
|
973
1056
|
const values = {};
|
|
974
|
-
for (const field of
|
|
1057
|
+
for (const field of LEGAL_IDENTITY_FIELDS) {
|
|
975
1058
|
if (!Object.prototype.hasOwnProperty.call(source, field)) continue;
|
|
976
1059
|
const entry = source[field];
|
|
977
1060
|
values[field] = typeof entry === "string" ? entry : null;
|
|
@@ -1088,6 +1171,15 @@ function toSubscriberParty(value) {
|
|
|
1088
1171
|
...toIssuerParty(source)
|
|
1089
1172
|
};
|
|
1090
1173
|
}
|
|
1174
|
+
function toRunningContractIssuer(row) {
|
|
1175
|
+
const issuer = isPlainObject2(row.issuerSnapshot) ? row.issuerSnapshot : null;
|
|
1176
|
+
return {
|
|
1177
|
+
id: row.id,
|
|
1178
|
+
tenantId: row.tenantId,
|
|
1179
|
+
issuerLegalName: issuer ? stringOrNull(issuer.legalName) : null,
|
|
1180
|
+
effectiveFrom: row.effectiveFrom
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1091
1183
|
function toIssuerParty(value) {
|
|
1092
1184
|
const source = value;
|
|
1093
1185
|
return {
|
|
@@ -1578,6 +1670,7 @@ export {
|
|
|
1578
1670
|
ERROR_MESSAGES_DE,
|
|
1579
1671
|
ERROR_MESSAGES_EN,
|
|
1580
1672
|
FEATURE_NOT_LICENSED,
|
|
1673
|
+
LEGAL_IDENTITY_FIELDS,
|
|
1581
1674
|
MARKETING_PRIORITY_MAX,
|
|
1582
1675
|
MARKETING_PRIORITY_MIN,
|
|
1583
1676
|
OTP_RATE_LIMIT_MAX_SENDS,
|
|
@@ -1601,7 +1694,6 @@ export {
|
|
|
1601
1694
|
SETTINGS_ERROR_CODES,
|
|
1602
1695
|
SETUP_ERROR_CODES,
|
|
1603
1696
|
SUBSCRIBER_ERROR_CODES,
|
|
1604
|
-
SUBSCRIBER_IDENTITY_FIELDS,
|
|
1605
1697
|
applyPromo,
|
|
1606
1698
|
assertPersistenceCapabilities,
|
|
1607
1699
|
buildActivePlanVersionWhere,
|
|
@@ -1611,6 +1703,7 @@ export {
|
|
|
1611
1703
|
bundleStemDefaults,
|
|
1612
1704
|
canonicalJson,
|
|
1613
1705
|
classifyBundleVersionDiff,
|
|
1706
|
+
classifyIssuerChange,
|
|
1614
1707
|
classifyPlanDiff,
|
|
1615
1708
|
collectUnsatisfiedRequires,
|
|
1616
1709
|
contractPartiesOf,
|
|
@@ -1624,16 +1717,20 @@ export {
|
|
|
1624
1717
|
isPaymentCallbackRejectedError,
|
|
1625
1718
|
isPlatformUserExistsError,
|
|
1626
1719
|
isVersionEditable,
|
|
1720
|
+
issuerIdentityOf,
|
|
1627
1721
|
keepOneRecommended,
|
|
1628
1722
|
missingRequiresFor,
|
|
1723
|
+
movedIdentityFields,
|
|
1629
1724
|
pickActivePromo,
|
|
1630
1725
|
planCatalogSettingsOf,
|
|
1631
1726
|
previousUtcDay,
|
|
1632
1727
|
promoStatus,
|
|
1633
1728
|
readQuotaRecord,
|
|
1634
1729
|
readQuotaValue,
|
|
1730
|
+
recordedIssuerIdentity,
|
|
1635
1731
|
resolveBundleAvailability,
|
|
1636
1732
|
resolveErrorMessage,
|
|
1733
|
+
sameLegalIdentity,
|
|
1637
1734
|
selectChargeableBundles,
|
|
1638
1735
|
settingsSubtreeOf,
|
|
1639
1736
|
startOfUtcDay,
|
|
@@ -1643,6 +1740,7 @@ export {
|
|
|
1643
1740
|
toContractLineItemRecord,
|
|
1644
1741
|
toPlanRow,
|
|
1645
1742
|
toPlanVersionRow,
|
|
1743
|
+
toRunningContractIssuer,
|
|
1646
1744
|
toSubscriberCorrectionRecord,
|
|
1647
1745
|
toSubscriberPaymentMethodRecord,
|
|
1648
1746
|
toSubscriberRecord,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasicat/core",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.19",
|
|
4
4
|
"description": "TypeScript contracts for SaaSiCat — capabilities, catalogs, subscriptions, entitlements, admin manifests, persistence ports — and the pure domain logic both server and browser run: error catalogues, plan-diff classification, promo arithmetic, feature requirements.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|