@saasicat/core 1.0.0-rc.22 → 1.0.0-rc.23
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 +169 -4
- package/dist/index.d.cts +312 -3
- package/dist/index.d.ts +312 -3
- package/dist/index.js +157 -4
- package/package.json +1 -1
package/dist/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
f3fb737468397b73304896bb7a522f80e2155d9d8df9a49bb3c83267b5014d3d
|
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,10 @@ __export(index_exports, {
|
|
|
32
32
|
FEATURE_NOT_LICENSED: () => FEATURE_NOT_LICENSED,
|
|
33
33
|
ForeignPaymentMethodReferenceError: () => ForeignPaymentMethodReferenceError,
|
|
34
34
|
LEGAL_IDENTITY_FIELDS: () => LEGAL_IDENTITY_FIELDS,
|
|
35
|
+
MAINTENANCE_CAPABILITY: () => MAINTENANCE_CAPABILITY,
|
|
36
|
+
MAINTENANCE_ERROR_CODES: () => MAINTENANCE_ERROR_CODES,
|
|
37
|
+
MAINTENANCE_MESSAGE_MAX_LENGTH: () => MAINTENANCE_MESSAGE_MAX_LENGTH,
|
|
38
|
+
MAINTENANCE_STATE_MAX_AGE_SECONDS: () => MAINTENANCE_STATE_MAX_AGE_SECONDS,
|
|
35
39
|
MARKETING_PRIORITY_MAX: () => MARKETING_PRIORITY_MAX,
|
|
36
40
|
MARKETING_PRIORITY_MIN: () => MARKETING_PRIORITY_MIN,
|
|
37
41
|
OTP_RATE_LIMIT_MAX_SENDS: () => OTP_RATE_LIMIT_MAX_SENDS,
|
|
@@ -77,13 +81,20 @@ __export(index_exports, {
|
|
|
77
81
|
identityCorrectionDelta: () => identityCorrectionDelta,
|
|
78
82
|
isBundleRedundant: () => isBundleRedundant,
|
|
79
83
|
isForeignPaymentMethodReferenceError: () => isForeignPaymentMethodReferenceError,
|
|
84
|
+
isMaintenanceAnnouncementLapsed: () => isMaintenanceAnnouncementLapsed,
|
|
85
|
+
isMaintenanceOverrun: () => isMaintenanceOverrun,
|
|
80
86
|
isPaymentCallbackRejectedError: () => isPaymentCallbackRejectedError,
|
|
81
87
|
isPlatformUserExistsError: () => isPlatformUserExistsError,
|
|
82
88
|
isVersionEditable: () => isVersionEditable,
|
|
83
89
|
issuerIdentityOf: () => issuerIdentityOf,
|
|
84
90
|
keepOneRecommended: () => keepOneRecommended,
|
|
91
|
+
maintenanceRetryAfterSeconds: () => maintenanceRetryAfterSeconds,
|
|
92
|
+
maintenanceStatusOf: () => maintenanceStatusOf,
|
|
93
|
+
maintenanceWindowStatusOf: () => maintenanceWindowStatusOf,
|
|
94
|
+
maintenanceWindowViewOf: () => maintenanceWindowViewOf,
|
|
85
95
|
missingRequiresFor: () => missingRequiresFor,
|
|
86
96
|
movedIdentityFields: () => movedIdentityFields,
|
|
97
|
+
parseZonedInstant: () => parseZonedInstant,
|
|
87
98
|
pickActivePromo: () => pickActivePromo,
|
|
88
99
|
planCatalogSettingsOf: () => planCatalogSettingsOf,
|
|
89
100
|
previousUtcDay: () => previousUtcDay,
|
|
@@ -104,6 +115,7 @@ __export(index_exports, {
|
|
|
104
115
|
subscriberPaymentMethodColumns: () => subscriberPaymentMethodColumns,
|
|
105
116
|
toBundleStemRow: () => toBundleStemRow,
|
|
106
117
|
toContractLineItemRecord: () => toContractLineItemRecord,
|
|
118
|
+
toMaintenanceWindowRecord: () => toMaintenanceWindowRecord,
|
|
107
119
|
toPlanRow: () => toPlanRow,
|
|
108
120
|
toPlanVersionRow: () => toPlanVersionRow,
|
|
109
121
|
toRunningContractIssuer: () => toRunningContractIssuer,
|
|
@@ -401,6 +413,100 @@ function appendNullableDecimalChange(out, field, oldValue, newValue, polarity) {
|
|
|
401
413
|
appendDecimalChange(out, field, oldValue, newValue, polarity);
|
|
402
414
|
}
|
|
403
415
|
|
|
416
|
+
// src/maintenance-window.types.ts
|
|
417
|
+
var MAINTENANCE_MESSAGE_MAX_LENGTH = 500;
|
|
418
|
+
var MAINTENANCE_STATE_MAX_AGE_SECONDS = 5;
|
|
419
|
+
var MAINTENANCE_CAPABILITY = "maintenance.manage";
|
|
420
|
+
|
|
421
|
+
// src/maintenance-window-mapping.ts
|
|
422
|
+
function toMaintenanceWindowRecord(row) {
|
|
423
|
+
return {
|
|
424
|
+
id: row.id,
|
|
425
|
+
startsAt: row.startsAt,
|
|
426
|
+
endsAt: row.endsAt,
|
|
427
|
+
message: row.message,
|
|
428
|
+
createdAt: row.createdAt,
|
|
429
|
+
createdBy: row.createdBy,
|
|
430
|
+
lockedAt: row.lockedAt,
|
|
431
|
+
lockedBy: row.lockedBy,
|
|
432
|
+
endedAt: row.endedAt,
|
|
433
|
+
endedBy: row.endedBy
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/maintenance-window-views.ts
|
|
438
|
+
var RETRY_AFTER_WITHOUT_END_SECONDS = 60;
|
|
439
|
+
var iso = (date) => date ? date.toISOString() : null;
|
|
440
|
+
function maintenanceWindowStatusOf(window) {
|
|
441
|
+
if (window.endedAt) return window.lockedAt ? "ended" : "cancelled";
|
|
442
|
+
return window.lockedAt ? "locked" : "announced";
|
|
443
|
+
}
|
|
444
|
+
function isMaintenanceOverrun(window, now) {
|
|
445
|
+
return maintenanceWindowStatusOf(window) === "locked" && window.endsAt !== null && window.endsAt.getTime() <= now.getTime();
|
|
446
|
+
}
|
|
447
|
+
function isMaintenanceAnnouncementLapsed(window, now) {
|
|
448
|
+
return maintenanceWindowStatusOf(window) === "announced" && window.endsAt !== null && window.endsAt.getTime() <= now.getTime();
|
|
449
|
+
}
|
|
450
|
+
function maintenanceStatusOf(open, now) {
|
|
451
|
+
if (!open) return { state: "none" };
|
|
452
|
+
const status = maintenanceWindowStatusOf(open);
|
|
453
|
+
if (status === "locked" && open.lockedAt) {
|
|
454
|
+
return {
|
|
455
|
+
state: "locked",
|
|
456
|
+
lockedAt: open.lockedAt.toISOString(),
|
|
457
|
+
endsAt: iso(open.endsAt),
|
|
458
|
+
message: open.message,
|
|
459
|
+
overrun: isMaintenanceOverrun(open, now)
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
if (status === "announced" && open.startsAt && open.endsAt && !isMaintenanceAnnouncementLapsed(open, now)) {
|
|
463
|
+
return {
|
|
464
|
+
state: "announced",
|
|
465
|
+
startsAt: open.startsAt.toISOString(),
|
|
466
|
+
endsAt: open.endsAt.toISOString(),
|
|
467
|
+
message: open.message
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
return { state: "none" };
|
|
471
|
+
}
|
|
472
|
+
function maintenanceWindowViewOf(window, now) {
|
|
473
|
+
return {
|
|
474
|
+
id: window.id,
|
|
475
|
+
status: maintenanceWindowStatusOf(window),
|
|
476
|
+
startsAt: iso(window.startsAt),
|
|
477
|
+
endsAt: iso(window.endsAt),
|
|
478
|
+
message: window.message,
|
|
479
|
+
createdAt: window.createdAt.toISOString(),
|
|
480
|
+
createdBy: window.createdBy,
|
|
481
|
+
lockedAt: iso(window.lockedAt),
|
|
482
|
+
lockedBy: window.lockedBy,
|
|
483
|
+
endedAt: iso(window.endedAt),
|
|
484
|
+
endedBy: window.endedBy,
|
|
485
|
+
overrun: isMaintenanceOverrun(window, now),
|
|
486
|
+
lapsed: isMaintenanceAnnouncementLapsed(window, now)
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function maintenanceRetryAfterSeconds(window, now) {
|
|
490
|
+
if (!window.endsAt || window.endsAt.getTime() <= now.getTime()) {
|
|
491
|
+
return RETRY_AFTER_WITHOUT_END_SECONDS;
|
|
492
|
+
}
|
|
493
|
+
return Math.max(1, Math.ceil((window.endsAt.getTime() - now.getTime()) / 1e3));
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/zoned-instant.ts
|
|
497
|
+
function endsInOffset(text) {
|
|
498
|
+
const sign = text.charAt(text.length - 6);
|
|
499
|
+
return (sign === "+" || sign === "-") && text.charAt(text.length - 3) === ":";
|
|
500
|
+
}
|
|
501
|
+
function parseZonedInstant(text) {
|
|
502
|
+
const value = text.trim();
|
|
503
|
+
if (!value.includes("T")) return null;
|
|
504
|
+
const zoned = value.endsWith("Z") || value.endsWith("z") || endsInOffset(value);
|
|
505
|
+
if (!zoned) return null;
|
|
506
|
+
const moment = new Date(value);
|
|
507
|
+
return Number.isNaN(moment.getTime()) ? null : moment;
|
|
508
|
+
}
|
|
509
|
+
|
|
404
510
|
// src/settings-subtree.ts
|
|
405
511
|
var CATALOGUE_KEYS = /* @__PURE__ */ new Set([
|
|
406
512
|
"schemaVersion",
|
|
@@ -731,7 +837,15 @@ var CONTRACT_ERROR_CODES = {
|
|
|
731
837
|
CHECKOUT_OFFER_CHANGED: "CHECKOUT_OFFER_CHANGED",
|
|
732
838
|
SUBSCRIPTION_CONTRACT_NOT_FOUND: "SUBSCRIPTION_CONTRACT_NOT_FOUND",
|
|
733
839
|
NO_ACTIVE_SUBSCRIPTION_CONTRACT: "NO_ACTIVE_SUBSCRIPTION_CONTRACT",
|
|
734
|
-
SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED"
|
|
840
|
+
SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED",
|
|
841
|
+
/**
|
|
842
|
+
* A successor would run beside another contract of the tenant: the
|
|
843
|
+
* contract in force moved while it was being written — another successor
|
|
844
|
+
* took its place, or a cancellation capped it — and kept moving on a
|
|
845
|
+
* second attempt, or a contract begins after the moment the successor
|
|
846
|
+
* would take effect. Nothing was written.
|
|
847
|
+
*/
|
|
848
|
+
SUBSCRIPTION_CONTRACT_CHANGED: "SUBSCRIPTION_CONTRACT_CHANGED"
|
|
735
849
|
};
|
|
736
850
|
var SUBSCRIBER_ERROR_CODES = {
|
|
737
851
|
/**
|
|
@@ -842,6 +956,24 @@ var SETTINGS_ERROR_CODES = {
|
|
|
842
956
|
/** No recorded change has this id, or the installation keeps no record at all. */
|
|
843
957
|
SETTINGS_CHANGE_NOT_FOUND: "SETTINGS_CHANGE_NOT_FOUND"
|
|
844
958
|
};
|
|
959
|
+
var MAINTENANCE_ERROR_CODES = {
|
|
960
|
+
/** The application is locked for maintenance; the body carries the window. */
|
|
961
|
+
MAINTENANCE: "MAINTENANCE",
|
|
962
|
+
/** A window is already open: move or cancel it before announcing another. */
|
|
963
|
+
MAINTENANCE_WINDOW_ALREADY_OPEN: "MAINTENANCE_WINDOW_ALREADY_OPEN",
|
|
964
|
+
/** No open window has this id — it ended, or it never existed. */
|
|
965
|
+
MAINTENANCE_WINDOW_NOT_OPEN: "MAINTENANCE_WINDOW_NOT_OPEN",
|
|
966
|
+
/** The window is locked: its start is history, and unlocking is what ends it. */
|
|
967
|
+
MAINTENANCE_WINDOW_LOCKED: "MAINTENANCE_WINDOW_LOCKED",
|
|
968
|
+
/** The announced end is not after the announced start. */
|
|
969
|
+
MAINTENANCE_WINDOW_END_NOT_AFTER_START: "MAINTENANCE_WINDOW_END_NOT_AFTER_START",
|
|
970
|
+
/** The announced end has already passed. */
|
|
971
|
+
MAINTENANCE_WINDOW_END_IN_PAST: "MAINTENANCE_WINDOW_END_IN_PAST",
|
|
972
|
+
/** A time without its zone, or not a date and time at all; `field` names which. */
|
|
973
|
+
MAINTENANCE_TIME_INVALID: "MAINTENANCE_TIME_INVALID",
|
|
974
|
+
/** The message is longer than `MAINTENANCE_MESSAGE_MAX_LENGTH`. */
|
|
975
|
+
MAINTENANCE_MESSAGE_TOO_LONG: "MAINTENANCE_MESSAGE_TOO_LONG"
|
|
976
|
+
};
|
|
845
977
|
var PLATFORM_ERROR_CODES = {
|
|
846
978
|
...SETUP_ERROR_CODES,
|
|
847
979
|
...AUTH_ERROR_CODES,
|
|
@@ -852,7 +984,8 @@ var PLATFORM_ERROR_CODES = {
|
|
|
852
984
|
...SUBSCRIBER_ERROR_CODES,
|
|
853
985
|
...REGISTRATION_ERROR_CODES,
|
|
854
986
|
...PAYMENT_ERROR_CODES,
|
|
855
|
-
...SETTINGS_ERROR_CODES
|
|
987
|
+
...SETTINGS_ERROR_CODES,
|
|
988
|
+
...MAINTENANCE_ERROR_CODES
|
|
856
989
|
};
|
|
857
990
|
|
|
858
991
|
// src/errors.ts
|
|
@@ -1688,6 +1821,7 @@ var ERROR_MESSAGES_EN = {
|
|
|
1688
1821
|
SUBSCRIPTION_CONTRACT_NOT_FOUND: "SubscriptionContract '{contractId}' not found",
|
|
1689
1822
|
NO_ACTIVE_SUBSCRIPTION_CONTRACT: "No active subscription contract for tenant {tenantId}",
|
|
1690
1823
|
SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SubscriptionContract '{contractId}' is already closed",
|
|
1824
|
+
SUBSCRIPTION_CONTRACT_CHANGED: "The contracts of tenant '{tenantId}' changed while a successor was being written, or one begins after the moment it would take effect, so it would run beside another. Nothing was written.",
|
|
1691
1825
|
// ── subscriber ──
|
|
1692
1826
|
SUBSCRIBER_REQUIRED: "Tenant '{tenantId}' has no subscriber. Nothing is agreed or charged without the party to it: create the tenant's subscriber first.",
|
|
1693
1827
|
SUBSCRIBER_ALREADY_EXISTS: "Tenant '{tenantId}' already has a subscriber.",
|
|
@@ -1723,7 +1857,16 @@ var ERROR_MESSAGES_EN = {
|
|
|
1723
1857
|
// ── entitlement (code lives in upsell.types.ts) ──
|
|
1724
1858
|
[FEATURE_NOT_LICENSED]: "Feature {featureKeys} is not included in the current plan.",
|
|
1725
1859
|
// ── settings ──
|
|
1726
|
-
SETTINGS_CHANGE_NOT_FOUND: "No recorded settings change has this id."
|
|
1860
|
+
SETTINGS_CHANGE_NOT_FOUND: "No recorded settings change has this id.",
|
|
1861
|
+
// ── maintenance ──
|
|
1862
|
+
MAINTENANCE: "The application is under maintenance.",
|
|
1863
|
+
MAINTENANCE_WINDOW_ALREADY_OPEN: "A maintenance window is already open. Move or cancel it before announcing another.",
|
|
1864
|
+
MAINTENANCE_WINDOW_NOT_OPEN: "No open maintenance window has this id.",
|
|
1865
|
+
MAINTENANCE_WINDOW_LOCKED: "This maintenance window is locked. Its start cannot move, and unlocking is what ends it.",
|
|
1866
|
+
MAINTENANCE_WINDOW_END_NOT_AFTER_START: "The end of a maintenance window has to be after its start.",
|
|
1867
|
+
MAINTENANCE_WINDOW_END_IN_PAST: "The end of a maintenance window cannot be in the past.",
|
|
1868
|
+
MAINTENANCE_TIME_INVALID: "The {field} has to be a date and time with its zone, such as 2026-10-02T22:00+02:00.",
|
|
1869
|
+
MAINTENANCE_MESSAGE_TOO_LONG: "The message may be at most {max} characters long."
|
|
1727
1870
|
};
|
|
1728
1871
|
function formatErrorMessage(template, params = {}) {
|
|
1729
1872
|
return template.replace(/\{(\w+)\}/g, (match, name) => {
|
|
@@ -1899,6 +2042,7 @@ var ERROR_MESSAGES_DE = {
|
|
|
1899
2042
|
SUBSCRIPTION_CONTRACT_NOT_FOUND: "SubscriptionContract '{contractId}' nicht gefunden",
|
|
1900
2043
|
NO_ACTIVE_SUBSCRIPTION_CONTRACT: "Kein aktiver Abo-Vertrag f\xFCr Mandant {tenantId}",
|
|
1901
2044
|
SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SubscriptionContract '{contractId}' ist bereits geschlossen",
|
|
2045
|
+
SUBSCRIPTION_CONTRACT_CHANGED: "Die Vertr\xE4ge von Mandant '{tenantId}' haben sich ge\xE4ndert, w\xE4hrend ein Nachfolger geschrieben wurde, oder einer beginnt nach dem Zeitpunkt, ab dem er g\xE4lte, sodass er neben einem anderen liefe. Es wurde nichts geschrieben.",
|
|
1902
2046
|
// ── subscriber ──
|
|
1903
2047
|
SUBSCRIBER_REQUIRED: "Mandant '{tenantId}' hat keinen Vertragspartner. Ohne ihn wird nichts vereinbart oder berechnet: Zuerst muss der Vertragspartner des Mandanten angelegt werden.",
|
|
1904
2048
|
SUBSCRIBER_ALREADY_EXISTS: "Mandant '{tenantId}' hat bereits einen Vertragspartner.",
|
|
@@ -1934,7 +2078,16 @@ var ERROR_MESSAGES_DE = {
|
|
|
1934
2078
|
// ── entitlement (code lives in upsell.types.ts) ──
|
|
1935
2079
|
[FEATURE_NOT_LICENSED]: "Das Feature {featureKeys} ist im aktuellen Plan nicht enthalten.",
|
|
1936
2080
|
// ── settings ──
|
|
1937
|
-
SETTINGS_CHANGE_NOT_FOUND: "Keine aufgezeichnete Einstellungs\xE4nderung hat diese ID."
|
|
2081
|
+
SETTINGS_CHANGE_NOT_FOUND: "Keine aufgezeichnete Einstellungs\xE4nderung hat diese ID.",
|
|
2082
|
+
// ── maintenance ──
|
|
2083
|
+
MAINTENANCE: "Die Anwendung wird gerade gewartet.",
|
|
2084
|
+
MAINTENANCE_WINDOW_ALREADY_OPEN: "Es ist bereits ein Wartungsfenster offen. Verschiebe oder streiche es, bevor du ein weiteres ank\xFCndigst.",
|
|
2085
|
+
MAINTENANCE_WINDOW_NOT_OPEN: "Kein offenes Wartungsfenster hat diese ID.",
|
|
2086
|
+
MAINTENANCE_WINDOW_LOCKED: "Dieses Wartungsfenster ist gesperrt. Sein Beginn l\xE4sst sich nicht mehr verschieben, und beendet wird es durch Entsperren.",
|
|
2087
|
+
MAINTENANCE_WINDOW_END_NOT_AFTER_START: "Das Ende eines Wartungsfensters muss nach seinem Beginn liegen.",
|
|
2088
|
+
MAINTENANCE_WINDOW_END_IN_PAST: "Das Ende eines Wartungsfensters darf nicht in der Vergangenheit liegen.",
|
|
2089
|
+
MAINTENANCE_TIME_INVALID: "{field} muss Datum und Uhrzeit mit Zeitzone sein, etwa 2026-10-02T22:00+02:00.",
|
|
2090
|
+
MAINTENANCE_MESSAGE_TOO_LONG: "Die Nachricht darf h\xF6chstens {max} Zeichen lang sein."
|
|
1938
2091
|
};
|
|
1939
2092
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1940
2093
|
0 && (module.exports = {
|
|
@@ -1950,6 +2103,10 @@ var ERROR_MESSAGES_DE = {
|
|
|
1950
2103
|
FEATURE_NOT_LICENSED,
|
|
1951
2104
|
ForeignPaymentMethodReferenceError,
|
|
1952
2105
|
LEGAL_IDENTITY_FIELDS,
|
|
2106
|
+
MAINTENANCE_CAPABILITY,
|
|
2107
|
+
MAINTENANCE_ERROR_CODES,
|
|
2108
|
+
MAINTENANCE_MESSAGE_MAX_LENGTH,
|
|
2109
|
+
MAINTENANCE_STATE_MAX_AGE_SECONDS,
|
|
1953
2110
|
MARKETING_PRIORITY_MAX,
|
|
1954
2111
|
MARKETING_PRIORITY_MIN,
|
|
1955
2112
|
OTP_RATE_LIMIT_MAX_SENDS,
|
|
@@ -1995,13 +2152,20 @@ var ERROR_MESSAGES_DE = {
|
|
|
1995
2152
|
identityCorrectionDelta,
|
|
1996
2153
|
isBundleRedundant,
|
|
1997
2154
|
isForeignPaymentMethodReferenceError,
|
|
2155
|
+
isMaintenanceAnnouncementLapsed,
|
|
2156
|
+
isMaintenanceOverrun,
|
|
1998
2157
|
isPaymentCallbackRejectedError,
|
|
1999
2158
|
isPlatformUserExistsError,
|
|
2000
2159
|
isVersionEditable,
|
|
2001
2160
|
issuerIdentityOf,
|
|
2002
2161
|
keepOneRecommended,
|
|
2162
|
+
maintenanceRetryAfterSeconds,
|
|
2163
|
+
maintenanceStatusOf,
|
|
2164
|
+
maintenanceWindowStatusOf,
|
|
2165
|
+
maintenanceWindowViewOf,
|
|
2003
2166
|
missingRequiresFor,
|
|
2004
2167
|
movedIdentityFields,
|
|
2168
|
+
parseZonedInstant,
|
|
2005
2169
|
pickActivePromo,
|
|
2006
2170
|
planCatalogSettingsOf,
|
|
2007
2171
|
previousUtcDay,
|
|
@@ -2022,6 +2186,7 @@ var ERROR_MESSAGES_DE = {
|
|
|
2022
2186
|
subscriberPaymentMethodColumns,
|
|
2023
2187
|
toBundleStemRow,
|
|
2024
2188
|
toContractLineItemRecord,
|
|
2189
|
+
toMaintenanceWindowRecord,
|
|
2025
2190
|
toPlanRow,
|
|
2026
2191
|
toPlanVersionRow,
|
|
2027
2192
|
toRunningContractIssuer,
|