deepline 0.3.85 → 0.3.87
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/bundling-sources/sdk/src/client.ts +2 -2
- package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +1 -1
- package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +1 -1
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/integration-contracts/AGENTS.md +9 -0
- package/dist/bundling-sources/shared_libs/integration-contracts/README.md +3 -0
- package/dist/bundling-sources/shared_libs/integration-contracts/index.ts +2 -0
- package/dist/bundling-sources/shared_libs/monitors/analytics-install-guides.ts +77 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/charges.ts +125 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/policy-builders.ts +686 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/pricing.ts +54 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/public-pricing.ts +72 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/session.ts +45 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/state.ts +454 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/tamradar.ts +260 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/types.ts +215 -0
- package/dist/bundling-sources/shared_libs/monitors/callback-security.ts +333 -0
- package/dist/bundling-sources/shared_libs/monitors/cursor-page.ts +42 -0
- package/dist/bundling-sources/shared_libs/monitors/framework-managed-payload-fields.ts +71 -0
- package/dist/bundling-sources/shared_libs/monitors/generated/provider-monitor-callback-admission-targets.ts +16 -0
- package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +267 -0
- package/dist/bundling-sources/shared_libs/monitors/inbound-publish-key.ts +39 -0
- package/dist/bundling-sources/shared_libs/monitors/inbound-queue.ts +82 -0
- package/dist/bundling-sources/shared_libs/monitors/lifecycle-notifications.ts +473 -0
- package/dist/bundling-sources/shared_libs/monitors/org-monitor-limits.ts +1 -1
- package/dist/bundling-sources/shared_libs/monitors/package.json +29 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-callback-verification.ts +1125 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-capability.ts +964 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-consumer-health.ts +56 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-errors.ts +131 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +527 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-legacy-aliases.ts +16 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-priority-execution.ts +56 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-public-aliases.ts +28 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-runtime-read-eligibility.ts +38 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-status.ts +10 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-upstream-identity.ts +112 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-webhook-monitor-runtime.ts +300 -0
- package/dist/bundling-sources/shared_libs/monitors/quickstart-prompts.ts +22 -0
- package/dist/bundling-sources/shared_libs/monitors/snitcher-install-validation.ts +93 -0
- package/dist/bundling-sources/shared_libs/monitors/snitcher-ip2company.ts +332 -0
- package/dist/bundling-sources/shared_libs/monitors/snitcher-radar.ts +221 -0
- package/dist/bundling-sources/shared_libs/monitors/starter-templates.ts +105 -0
- package/dist/bundling-sources/shared_libs/monitors/tracking-provider-guidance.ts +73 -0
- package/dist/bundling-sources/shared_libs/monitors/tracking-provider-integrations.ts +81 -0
- package/dist/bundling-sources/shared_libs/monitors/tsconfig.json +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-env.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +302 -0
- package/dist/bundling-sources/shared_libs/plays/email-status.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/tool-category-descriptions.ts +2 -2
- package/dist/bundling-sources/shared_libs/runtime-env.ts +6 -0
- package/dist/bundling-sources/shared_libs/security/header-safe-env.ts +28 -0
- package/dist/bundling-sources/shared_libs/security/secret-redaction.ts +267 -0
- package/dist/cli/index.js +25 -14
- package/dist/cli/index.mjs +25 -14
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/index.mjs +3 -3
- package/dist/install-integrity.json +45 -2
- package/package.json +3 -1
- /package/dist/bundling-sources/shared_libs/{integrations/bettercontact-execution-policy.ts → integration-contracts/bettercontact/execution-policy.ts} +0 -0
- /package/dist/bundling-sources/shared_libs/{integrations/theirstack-execution-policy.ts → integration-contracts/theirstack/execution-policy.ts} +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { getUsdPerCustomerCredit } from '@deepline/billing/credit-pricing';
|
|
2
|
+
|
|
3
|
+
export const MONITOR_BILLING_VERIFIED_AT = '2026-06-25';
|
|
4
|
+
// Monitor / defineMonitor upstream markup, deliberately HIGHER than the general
|
|
5
|
+
// tool path (1.4×, DEEPLINE_CREDIT_USD_MARKUP_MULTIPLIER): monitors are a premium,
|
|
6
|
+
// always-on data pipe. Applied to the provider's per-event USD cost. Raised from
|
|
7
|
+
// 3.0× to 5.0× to reprice monitor responses (findings / webhook events) now that
|
|
8
|
+
// upstream-free monitors waive the base create fee and bill purely on responses.
|
|
9
|
+
export const MONITOR_UPSTREAM_COST_MARKUP_MULTIPLIER = 5.0;
|
|
10
|
+
// Flat creation/lifecycle fee in Deepline retail dollars. Providers do not
|
|
11
|
+
// charge to create a monitor, so this is a pure platform fee; render it through
|
|
12
|
+
// current credits rather than baking in a denomination.
|
|
13
|
+
export const MONITOR_BASE_LIFECYCLE_USD = 0.5;
|
|
14
|
+
export const MONITOR_PLAN_ENTITLEMENT_ALLOCATION_DIVISOR = 100;
|
|
15
|
+
|
|
16
|
+
const MONITOR_CREDIT_PRECISION_FACTOR = 100;
|
|
17
|
+
|
|
18
|
+
function roundUpMonitorCredits(value: number): number {
|
|
19
|
+
if (!Number.isFinite(value) || value <= 0) return 0;
|
|
20
|
+
return (
|
|
21
|
+
Math.ceil(Number((value * MONITOR_CREDIT_PRECISION_FACTOR).toFixed(9))) /
|
|
22
|
+
MONITOR_CREDIT_PRECISION_FACTOR
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function deeplineRetailUsdToCredits(amountUsd: number): number {
|
|
27
|
+
if (!Number.isFinite(amountUsd) || amountUsd <= 0) return 0;
|
|
28
|
+
return roundUpMonitorCredits(amountUsd / getUsdPerCustomerCredit());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function planEntitlementCreateCredits(
|
|
32
|
+
minimumMonthlyPlanUsd: number,
|
|
33
|
+
): number {
|
|
34
|
+
return deeplineRetailUsdToCredits(
|
|
35
|
+
minimumMonthlyPlanUsd / MONITOR_PLAN_ENTITLEMENT_ALLOCATION_DIVISOR,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function monitorProviderUsdToRetailCredits(
|
|
40
|
+
providerCostUsd: number,
|
|
41
|
+
markupMultiplier = MONITOR_UPSTREAM_COST_MARKUP_MULTIPLIER,
|
|
42
|
+
): number {
|
|
43
|
+
if (!Number.isFinite(providerCostUsd) || providerCostUsd <= 0) return 0;
|
|
44
|
+
if (!Number.isFinite(markupMultiplier) || markupMultiplier <= 0) {
|
|
45
|
+
throw new Error(`Invalid monitor markup multiplier: ${markupMultiplier}`);
|
|
46
|
+
}
|
|
47
|
+
const rawCredits =
|
|
48
|
+
(providerCostUsd * markupMultiplier) / getUsdPerCustomerCredit();
|
|
49
|
+
return roundUpMonitorCredits(rawCredits);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const MONITOR_BASE_LIFECYCLE_CREDITS = deeplineRetailUsdToCredits(
|
|
53
|
+
MONITOR_BASE_LIFECYCLE_USD,
|
|
54
|
+
);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MonitorBillingPolicy,
|
|
3
|
+
MonitorPayloadPricingEntry,
|
|
4
|
+
MonitorPayloadPricingMode,
|
|
5
|
+
} from '@deepline/monitors/billing/types';
|
|
6
|
+
|
|
7
|
+
export type PublicMonitorPricing = {
|
|
8
|
+
mode: MonitorPayloadPricingMode;
|
|
9
|
+
billing_basis: 'per_accepted_event' | 'recurring';
|
|
10
|
+
deepline_credits: number;
|
|
11
|
+
unit: string;
|
|
12
|
+
charge_timing: string;
|
|
13
|
+
period_days?: number;
|
|
14
|
+
display: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type PublicResolvedMonitorPricing = PublicMonitorPricing & {
|
|
18
|
+
discriminator: string;
|
|
19
|
+
selection: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function formatCredits(credits: number) {
|
|
23
|
+
return `${credits.toLocaleString('en')} Deepline ${
|
|
24
|
+
credits === 1 ? 'credit' : 'credits'
|
|
25
|
+
}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Customer-safe pricing semantics for one payload-priced monitor option. */
|
|
29
|
+
export function publicMonitorPricingEntry(
|
|
30
|
+
entry: MonitorPayloadPricingEntry,
|
|
31
|
+
): PublicMonitorPricing {
|
|
32
|
+
const credits = formatCredits(entry.retailCredits);
|
|
33
|
+
if (entry.mode === 'monitor_subscription') {
|
|
34
|
+
const periodDays = entry.periodDays ?? 30;
|
|
35
|
+
return {
|
|
36
|
+
mode: entry.mode,
|
|
37
|
+
billing_basis: 'recurring',
|
|
38
|
+
deepline_credits: entry.retailCredits,
|
|
39
|
+
unit: entry.unitLabel,
|
|
40
|
+
charge_timing: `on deploy or reactivation and every ${periodDays} days while active`,
|
|
41
|
+
period_days: periodDays,
|
|
42
|
+
display: `${credits} on deploy or reactivation and every ${periodDays} days while active; no additional per-event charge.`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
mode: entry.mode,
|
|
47
|
+
billing_basis: 'per_accepted_event',
|
|
48
|
+
deepline_credits: entry.retailCredits,
|
|
49
|
+
unit: entry.unitLabel,
|
|
50
|
+
charge_timing: 'for each accepted provider event',
|
|
51
|
+
display: `${credits} per accepted ${entry.unitLabel} event.`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Resolve the selected payload price without exposing provider cost inputs. */
|
|
56
|
+
export function resolvePublicMonitorPricing(input: {
|
|
57
|
+
policy: MonitorBillingPolicy | null | undefined;
|
|
58
|
+
payload: Record<string, unknown> | null | undefined;
|
|
59
|
+
}): PublicResolvedMonitorPricing | undefined {
|
|
60
|
+
const pricing = input.policy?.payloadPricing;
|
|
61
|
+
if (!pricing || !input.payload) return undefined;
|
|
62
|
+
const rawSelection = input.payload[pricing.discriminator];
|
|
63
|
+
if (typeof rawSelection !== 'string') return undefined;
|
|
64
|
+
const selection = rawSelection.trim();
|
|
65
|
+
const entry = pricing.entries[selection];
|
|
66
|
+
if (!selection || !entry) return undefined;
|
|
67
|
+
return {
|
|
68
|
+
discriminator: pricing.discriminator,
|
|
69
|
+
selection,
|
|
70
|
+
...publicMonitorPricingEntry(entry),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MonitorBillingPhase,
|
|
3
|
+
MonitorBillingSession,
|
|
4
|
+
} from '@deepline/monitors/billing/types';
|
|
5
|
+
|
|
6
|
+
function requireNonEmpty(value: string, label: string) {
|
|
7
|
+
const trimmed = value.trim();
|
|
8
|
+
if (!trimmed) {
|
|
9
|
+
throw new Error(`Monitor billing session ${label} is required.`);
|
|
10
|
+
}
|
|
11
|
+
return trimmed;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function segment(value: string) {
|
|
15
|
+
return encodeURIComponent(value);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function monitorBillingSessionKey(input: {
|
|
19
|
+
orgId: string;
|
|
20
|
+
monitorKey: string;
|
|
21
|
+
billingReason: MonitorBillingPhase;
|
|
22
|
+
providerEventIdOrPeriod?: string | null;
|
|
23
|
+
}): MonitorBillingSession {
|
|
24
|
+
const orgId = requireNonEmpty(input.orgId, 'orgId');
|
|
25
|
+
const monitorKey = requireNonEmpty(input.monitorKey, 'monitorKey');
|
|
26
|
+
const billingReason = requireNonEmpty(
|
|
27
|
+
input.billingReason,
|
|
28
|
+
'billingReason',
|
|
29
|
+
) as MonitorBillingPhase;
|
|
30
|
+
const providerEventIdOrPeriod =
|
|
31
|
+
input.providerEventIdOrPeriod?.trim() || 'lifecycle';
|
|
32
|
+
return {
|
|
33
|
+
key: [
|
|
34
|
+
'monitor',
|
|
35
|
+
segment(orgId),
|
|
36
|
+
segment(monitorKey),
|
|
37
|
+
segment(billingReason),
|
|
38
|
+
segment(providerEventIdOrPeriod),
|
|
39
|
+
].join(':'),
|
|
40
|
+
orgId,
|
|
41
|
+
monitorKey,
|
|
42
|
+
billingReason,
|
|
43
|
+
providerEventIdOrPeriod,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MonitorBillingPhase,
|
|
3
|
+
MonitorBillingState,
|
|
4
|
+
MonitorBillingPolicy,
|
|
5
|
+
} from '@deepline/monitors/billing/types';
|
|
6
|
+
import type { PublicMonitorCustomerChargeSummary } from '@deepline/monitors/billing/charges';
|
|
7
|
+
|
|
8
|
+
export type StoredMonitorBillingState = {
|
|
9
|
+
state: MonitorBillingState;
|
|
10
|
+
lifecycle_charge_credits: number;
|
|
11
|
+
// How much of the CURRENT lifecycle charge has already been refunded (via
|
|
12
|
+
// radar_failure). Caps cumulative refunds at what the customer actually paid,
|
|
13
|
+
// so multiple distinct failure events can total at most 1x the charge. A fresh
|
|
14
|
+
// create/renewal charge resets this to 0 by omission — `monitorBillingState-
|
|
15
|
+
// ForSuccessfulCharge` rebuilds the state without it.
|
|
16
|
+
lifecycle_refunded_credits?: number;
|
|
17
|
+
usage_charge_pending: boolean;
|
|
18
|
+
reactivation_required: boolean;
|
|
19
|
+
suspension_reason?: string;
|
|
20
|
+
last_billing_error?: string;
|
|
21
|
+
last_charge_session_key?: string;
|
|
22
|
+
last_charged_at?: string;
|
|
23
|
+
next_renewal_at?: string | null;
|
|
24
|
+
provider_next_charge_at?: string | null;
|
|
25
|
+
cancel_before_at?: string | null;
|
|
26
|
+
catalog_snapshot?: Record<string, unknown>;
|
|
27
|
+
commercial_snapshot?: Record<string, unknown>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const SUBSCRIPTION_RENEWAL_GUARD_MS = 24 * 60 * 60 * 1000;
|
|
31
|
+
|
|
32
|
+
export type PublicMonitorBillingSummary = Pick<
|
|
33
|
+
StoredMonitorBillingState,
|
|
34
|
+
| 'state'
|
|
35
|
+
| 'lifecycle_charge_credits'
|
|
36
|
+
| 'usage_charge_pending'
|
|
37
|
+
| 'reactivation_required'
|
|
38
|
+
| 'suspension_reason'
|
|
39
|
+
| 'next_renewal_at'
|
|
40
|
+
> & {
|
|
41
|
+
create_charge?: PublicMonitorCustomerChargeSummary;
|
|
42
|
+
reactivate_charge?: PublicMonitorCustomerChargeSummary;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function readRecord(value: unknown): Record<string, unknown> {
|
|
46
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
47
|
+
return value as Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
if (typeof value === 'string' && value.trim()) {
|
|
50
|
+
try {
|
|
51
|
+
const parsed = JSON.parse(value) as unknown;
|
|
52
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
53
|
+
? (parsed as Record<string, unknown>)
|
|
54
|
+
: {};
|
|
55
|
+
} catch {
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readString(value: unknown) {
|
|
63
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readNumber(value: unknown) {
|
|
67
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function readBoolean(value: unknown) {
|
|
71
|
+
return typeof value === 'boolean' ? value : null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readBillingState(value: unknown): MonitorBillingState {
|
|
75
|
+
const state = readString(value);
|
|
76
|
+
switch (state) {
|
|
77
|
+
case 'active':
|
|
78
|
+
case 'preflight_denied':
|
|
79
|
+
case 'billing_pending':
|
|
80
|
+
case 'billing_suspended':
|
|
81
|
+
case 'upstream_disable_pending':
|
|
82
|
+
case 'upstream_disable_failed':
|
|
83
|
+
case 'disabled':
|
|
84
|
+
return state;
|
|
85
|
+
default:
|
|
86
|
+
return 'billing_pending';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function parseStoredMonitorBillingState(
|
|
91
|
+
value: unknown,
|
|
92
|
+
): StoredMonitorBillingState {
|
|
93
|
+
const record = readRecord(value);
|
|
94
|
+
return {
|
|
95
|
+
state: readBillingState(record.state),
|
|
96
|
+
lifecycle_charge_credits: Math.max(
|
|
97
|
+
0,
|
|
98
|
+
readNumber(record.lifecycle_charge_credits) ?? 0,
|
|
99
|
+
),
|
|
100
|
+
...(readNumber(record.lifecycle_refunded_credits)
|
|
101
|
+
? {
|
|
102
|
+
lifecycle_refunded_credits: Math.max(
|
|
103
|
+
0,
|
|
104
|
+
readNumber(record.lifecycle_refunded_credits)!,
|
|
105
|
+
),
|
|
106
|
+
}
|
|
107
|
+
: {}),
|
|
108
|
+
usage_charge_pending:
|
|
109
|
+
readBoolean(record.usage_charge_pending) ??
|
|
110
|
+
readBillingState(record.state) === 'billing_pending',
|
|
111
|
+
reactivation_required:
|
|
112
|
+
readBoolean(record.reactivation_required) ??
|
|
113
|
+
readBillingState(record.state) !== 'active',
|
|
114
|
+
...(readString(record.suspension_reason)
|
|
115
|
+
? { suspension_reason: readString(record.suspension_reason)! }
|
|
116
|
+
: {}),
|
|
117
|
+
...(readString(record.last_billing_error)
|
|
118
|
+
? { last_billing_error: readString(record.last_billing_error)! }
|
|
119
|
+
: {}),
|
|
120
|
+
...(readString(record.last_charge_session_key)
|
|
121
|
+
? { last_charge_session_key: readString(record.last_charge_session_key)! }
|
|
122
|
+
: {}),
|
|
123
|
+
...(readString(record.last_charged_at)
|
|
124
|
+
? { last_charged_at: readString(record.last_charged_at)! }
|
|
125
|
+
: {}),
|
|
126
|
+
...(record.next_renewal_at === null
|
|
127
|
+
? { next_renewal_at: null }
|
|
128
|
+
: readString(record.next_renewal_at)
|
|
129
|
+
? { next_renewal_at: readString(record.next_renewal_at)! }
|
|
130
|
+
: {}),
|
|
131
|
+
...(record.provider_next_charge_at === null
|
|
132
|
+
? { provider_next_charge_at: null }
|
|
133
|
+
: readString(record.provider_next_charge_at)
|
|
134
|
+
? {
|
|
135
|
+
provider_next_charge_at: readString(
|
|
136
|
+
record.provider_next_charge_at,
|
|
137
|
+
)!,
|
|
138
|
+
}
|
|
139
|
+
: {}),
|
|
140
|
+
...(record.cancel_before_at === null
|
|
141
|
+
? { cancel_before_at: null }
|
|
142
|
+
: readString(record.cancel_before_at)
|
|
143
|
+
? { cancel_before_at: readString(record.cancel_before_at)! }
|
|
144
|
+
: {}),
|
|
145
|
+
...(record.catalog_snapshot &&
|
|
146
|
+
typeof record.catalog_snapshot === 'object' &&
|
|
147
|
+
!Array.isArray(record.catalog_snapshot)
|
|
148
|
+
? { catalog_snapshot: record.catalog_snapshot as Record<string, unknown> }
|
|
149
|
+
: {}),
|
|
150
|
+
...(record.commercial_snapshot &&
|
|
151
|
+
typeof record.commercial_snapshot === 'object' &&
|
|
152
|
+
!Array.isArray(record.commercial_snapshot)
|
|
153
|
+
? {
|
|
154
|
+
commercial_snapshot: record.commercial_snapshot as Record<
|
|
155
|
+
string,
|
|
156
|
+
unknown
|
|
157
|
+
>,
|
|
158
|
+
}
|
|
159
|
+
: {}),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function publicMonitorBillingSummary(
|
|
164
|
+
state: StoredMonitorBillingState,
|
|
165
|
+
charges?: {
|
|
166
|
+
create_charge?: PublicMonitorCustomerChargeSummary;
|
|
167
|
+
reactivate_charge?: PublicMonitorCustomerChargeSummary;
|
|
168
|
+
},
|
|
169
|
+
): PublicMonitorBillingSummary {
|
|
170
|
+
return {
|
|
171
|
+
state: state.state,
|
|
172
|
+
lifecycle_charge_credits: state.lifecycle_charge_credits,
|
|
173
|
+
usage_charge_pending: state.usage_charge_pending,
|
|
174
|
+
reactivation_required: state.reactivation_required,
|
|
175
|
+
...(charges?.create_charge ? { create_charge: charges.create_charge } : {}),
|
|
176
|
+
...(charges?.reactivate_charge
|
|
177
|
+
? { reactivate_charge: charges.reactivate_charge }
|
|
178
|
+
: {}),
|
|
179
|
+
...(state.suspension_reason
|
|
180
|
+
? { suspension_reason: state.suspension_reason }
|
|
181
|
+
: {}),
|
|
182
|
+
...(state.next_renewal_at !== undefined
|
|
183
|
+
? { next_renewal_at: state.next_renewal_at }
|
|
184
|
+
: {}),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function providerRenewalFields(
|
|
189
|
+
providerNextChargeAt: string | null | undefined,
|
|
190
|
+
): Pick<
|
|
191
|
+
StoredMonitorBillingState,
|
|
192
|
+
'provider_next_charge_at' | 'cancel_before_at'
|
|
193
|
+
> {
|
|
194
|
+
if (providerNextChargeAt === undefined) return {};
|
|
195
|
+
if (providerNextChargeAt === null) {
|
|
196
|
+
return {
|
|
197
|
+
provider_next_charge_at: null,
|
|
198
|
+
cancel_before_at: null,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
const parsed = Date.parse(providerNextChargeAt);
|
|
202
|
+
if (!Number.isFinite(parsed)) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`Provider next charge timestamp is invalid: ${providerNextChargeAt}.`,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
provider_next_charge_at: new Date(parsed).toISOString(),
|
|
209
|
+
cancel_before_at: new Date(
|
|
210
|
+
parsed - SUBSCRIPTION_RENEWAL_GUARD_MS,
|
|
211
|
+
).toISOString(),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function monitorBillingStateWithProviderRenewal(input: {
|
|
216
|
+
previous: StoredMonitorBillingState;
|
|
217
|
+
providerNextChargeAt: string | null;
|
|
218
|
+
}): StoredMonitorBillingState {
|
|
219
|
+
return {
|
|
220
|
+
...input.previous,
|
|
221
|
+
...providerRenewalFields(input.providerNextChargeAt),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function defaultMonitorBillingState(): StoredMonitorBillingState {
|
|
226
|
+
return {
|
|
227
|
+
state: 'billing_pending',
|
|
228
|
+
lifecycle_charge_credits: 0,
|
|
229
|
+
usage_charge_pending: false,
|
|
230
|
+
reactivation_required: false,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function monitorBillingStateForSuccessfulCharge(input: {
|
|
235
|
+
policy: MonitorBillingPolicy;
|
|
236
|
+
phase: MonitorBillingPhase;
|
|
237
|
+
chargedCredits: number;
|
|
238
|
+
pendingProviderCostKeys: readonly string[];
|
|
239
|
+
sessionKey?: string;
|
|
240
|
+
chargedAt?: Date;
|
|
241
|
+
catalogSnapshot?: Record<string, unknown>;
|
|
242
|
+
commercialSnapshot?: Record<string, unknown>;
|
|
243
|
+
providerNextChargeAt?: string | null;
|
|
244
|
+
}): StoredMonitorBillingState {
|
|
245
|
+
return {
|
|
246
|
+
state:
|
|
247
|
+
input.pendingProviderCostKeys.length > 0 ? 'billing_pending' : 'active',
|
|
248
|
+
lifecycle_charge_credits: input.chargedCredits,
|
|
249
|
+
usage_charge_pending: input.pendingProviderCostKeys.length > 0,
|
|
250
|
+
reactivation_required: false,
|
|
251
|
+
...(input.sessionKey ? { last_charge_session_key: input.sessionKey } : {}),
|
|
252
|
+
last_charged_at: (input.chargedAt ?? new Date()).toISOString(),
|
|
253
|
+
next_renewal_at: nextRenewalAtForPolicy(input.policy, input.chargedAt),
|
|
254
|
+
...providerRenewalFields(input.providerNextChargeAt),
|
|
255
|
+
...(input.catalogSnapshot
|
|
256
|
+
? { catalog_snapshot: input.catalogSnapshot }
|
|
257
|
+
: {}),
|
|
258
|
+
...(input.commercialSnapshot
|
|
259
|
+
? { commercial_snapshot: input.commercialSnapshot }
|
|
260
|
+
: {}),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function monitorBillingStateForWrapperEntitlement(input: {
|
|
265
|
+
commercialSnapshot: Record<string, unknown>;
|
|
266
|
+
}): StoredMonitorBillingState {
|
|
267
|
+
return {
|
|
268
|
+
state: 'active',
|
|
269
|
+
lifecycle_charge_credits: 0,
|
|
270
|
+
usage_charge_pending: false,
|
|
271
|
+
reactivation_required: false,
|
|
272
|
+
commercial_snapshot: input.commercialSnapshot,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function monitorBillingStateForDisabled(input: {
|
|
277
|
+
previous?: StoredMonitorBillingState | null;
|
|
278
|
+
reason: string;
|
|
279
|
+
}): StoredMonitorBillingState {
|
|
280
|
+
return {
|
|
281
|
+
...(input.previous ?? defaultMonitorBillingState()),
|
|
282
|
+
state: 'disabled',
|
|
283
|
+
usage_charge_pending: false,
|
|
284
|
+
reactivation_required: true,
|
|
285
|
+
suspension_reason: input.reason,
|
|
286
|
+
// THE PAID-THROUGH ANCHOR SURVIVES A DISABLE.
|
|
287
|
+
//
|
|
288
|
+
// These three used to be nulled here, and that is what made a suspended
|
|
289
|
+
// monitor cost twice. The customer bought a month of this slot; turning the
|
|
290
|
+
// monitor off - by policy or by their own hand - does not give that month
|
|
291
|
+
// back, and TamRadar has no activate endpoint, so coming back is a CREATE
|
|
292
|
+
// that would open a second charge for coverage already paid for.
|
|
293
|
+
//
|
|
294
|
+
// Erasing the anchor here also meant a correct check on the way back up
|
|
295
|
+
// would have had nothing to read. The record of what was paid for, and
|
|
296
|
+
// until when, is the thing that has to outlive the disable.
|
|
297
|
+
//
|
|
298
|
+
// Renewals do NOT keep running off it: `listRenewalCandidates` ranges
|
|
299
|
+
// `by_org_status_next_renewal` on `status: 'active'`, and a disabled
|
|
300
|
+
// monitor is not active. The anchor is a receipt, not a schedule.
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Whether a subscription charge is admitted, or already paid for.
|
|
306
|
+
*
|
|
307
|
+
* THE SLOT-MONTH INVARIANT: an organization pays once per (monitor key,
|
|
308
|
+
* coverage period). Inside a period it has already bought, re-creating that
|
|
309
|
+
* monitor is free - however it came to be gone. Policy suspended it and a
|
|
310
|
+
* top-up revived it; an operator ran the revive verb; the customer deleted it
|
|
311
|
+
* on Tuesday and wanted it back on Wednesday. All the same slot, all the same
|
|
312
|
+
* month, one charge.
|
|
313
|
+
*
|
|
314
|
+
* Deliberately blind to WHY the monitor went away. Charging a customer twice
|
|
315
|
+
* for one slot-month is the same wrong answer whether Deepline or the customer
|
|
316
|
+
* caused the gap, and a rule that had to know the cause would need the cause to
|
|
317
|
+
* be recorded correctly on every path that can delete a monitor.
|
|
318
|
+
*
|
|
319
|
+
* THE ONE GUARD: the incoming charge may not exceed what was actually paid.
|
|
320
|
+
* Without it, deleting a cheap monitor and re-creating an expensive one under
|
|
321
|
+
* the same key inside the period would take the expensive tier for the cheap
|
|
322
|
+
* price. An upgrade therefore pays in full and opens a fresh period - the
|
|
323
|
+
* conservative answer, because the alternative is pro-rating, which is a
|
|
324
|
+
* pricing rule this system does not have and should not grow by accident.
|
|
325
|
+
*/
|
|
326
|
+
export function monitorPaidThroughAdmission(input: {
|
|
327
|
+
previous?: StoredMonitorBillingState | null;
|
|
328
|
+
/** What this lifecycle phase would charge if nothing were already paid. */
|
|
329
|
+
quotedCredits: number;
|
|
330
|
+
now?: Date;
|
|
331
|
+
}): {
|
|
332
|
+
/** True when the customer must pay: no open period covers this charge. */
|
|
333
|
+
chargeAdmitted: boolean;
|
|
334
|
+
/** The period the surviving anchor covers, when one does. */
|
|
335
|
+
paidThrough: string | null;
|
|
336
|
+
/** The provider's own period end, when the provider bills recurringly. */
|
|
337
|
+
providerPaidThrough: string | null;
|
|
338
|
+
reason:
|
|
339
|
+
| 'no_previous_period'
|
|
340
|
+
| 'period_elapsed'
|
|
341
|
+
| 'charge_exceeds_paid'
|
|
342
|
+
| 'inside_paid_period';
|
|
343
|
+
} {
|
|
344
|
+
const previous = input.previous;
|
|
345
|
+
const now = input.now ?? new Date();
|
|
346
|
+
// TamRadar subscriptions have a provider-owned period. `next_renewal_at`
|
|
347
|
+
// deliberately remains null for those mixed policies so the generic renewal
|
|
348
|
+
// scheduler cannot also claim the provider's subscription renewal. Its
|
|
349
|
+
// `provider_next_charge_at` is nevertheless a durable paid-through receipt
|
|
350
|
+
// for admission after a disable/recreate.
|
|
351
|
+
const paidThrough = previous?.next_renewal_at ?? null;
|
|
352
|
+
const providerPaidThrough = previous?.provider_next_charge_at ?? null;
|
|
353
|
+
const base = { paidThrough, providerPaidThrough };
|
|
354
|
+
const effectivePaidThrough = paidThrough ?? providerPaidThrough;
|
|
355
|
+
|
|
356
|
+
if (input.quotedCredits <= 0) {
|
|
357
|
+
// Nothing to admit. Free phases are free for their own reasons.
|
|
358
|
+
return { ...base, chargeAdmitted: false, reason: 'inside_paid_period' };
|
|
359
|
+
}
|
|
360
|
+
if (!effectivePaidThrough) {
|
|
361
|
+
return { ...base, chargeAdmitted: true, reason: 'no_previous_period' };
|
|
362
|
+
}
|
|
363
|
+
const paidThroughMs = Date.parse(effectivePaidThrough);
|
|
364
|
+
if (!Number.isFinite(paidThroughMs) || now.getTime() >= paidThroughMs) {
|
|
365
|
+
// An unparseable anchor charges rather than granting free coverage off a
|
|
366
|
+
// value nobody can read.
|
|
367
|
+
return { ...base, chargeAdmitted: true, reason: 'period_elapsed' };
|
|
368
|
+
}
|
|
369
|
+
const alreadyPaid = previous?.lifecycle_charge_credits ?? 0;
|
|
370
|
+
if (input.quotedCredits > alreadyPaid) {
|
|
371
|
+
return { ...base, chargeAdmitted: true, reason: 'charge_exceeds_paid' };
|
|
372
|
+
}
|
|
373
|
+
return { ...base, chargeAdmitted: false, reason: 'inside_paid_period' };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export function monitorBillingStateForSuspension(input: {
|
|
377
|
+
previous?: StoredMonitorBillingState | null;
|
|
378
|
+
reason: string;
|
|
379
|
+
error?: string;
|
|
380
|
+
disablePending?: boolean;
|
|
381
|
+
}): StoredMonitorBillingState {
|
|
382
|
+
return {
|
|
383
|
+
...(input.previous ?? defaultMonitorBillingState()),
|
|
384
|
+
state: input.disablePending
|
|
385
|
+
? 'upstream_disable_pending'
|
|
386
|
+
: 'billing_suspended',
|
|
387
|
+
usage_charge_pending: false,
|
|
388
|
+
reactivation_required: true,
|
|
389
|
+
suspension_reason: input.reason,
|
|
390
|
+
...(input.error ? { last_billing_error: input.error } : {}),
|
|
391
|
+
next_renewal_at: null,
|
|
392
|
+
provider_next_charge_at: null,
|
|
393
|
+
cancel_before_at: null,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function monitorBillingStateForUpstreamDisableFailed(input: {
|
|
398
|
+
previous?: StoredMonitorBillingState | null;
|
|
399
|
+
error: string;
|
|
400
|
+
}): StoredMonitorBillingState {
|
|
401
|
+
return {
|
|
402
|
+
...(input.previous ?? defaultMonitorBillingState()),
|
|
403
|
+
state: 'upstream_disable_failed',
|
|
404
|
+
usage_charge_pending: false,
|
|
405
|
+
reactivation_required: true,
|
|
406
|
+
suspension_reason: 'billing_failure_upstream_disable_failed',
|
|
407
|
+
last_billing_error: input.error,
|
|
408
|
+
next_renewal_at: null,
|
|
409
|
+
provider_next_charge_at: null,
|
|
410
|
+
cancel_before_at: null,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Whether this policy bills in periods at all.
|
|
416
|
+
*
|
|
417
|
+
* Only a policy that opens a renewal period can leave a paid-through anchor
|
|
418
|
+
* behind, so only one of those can make the slot-month rule matter. An
|
|
419
|
+
* update-priced monitor charges per change and has nothing to reuse, which
|
|
420
|
+
* means the charge path can skip reading its previous state entirely - and
|
|
421
|
+
* that read is on a batch create path that runs once per member.
|
|
422
|
+
*/
|
|
423
|
+
export function policyOpensRenewalPeriod(
|
|
424
|
+
policy: MonitorBillingPolicy,
|
|
425
|
+
): boolean {
|
|
426
|
+
return nextRenewalAtForPolicy(policy) !== null;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function nextRenewalAtForPolicy(
|
|
430
|
+
policy: MonitorBillingPolicy,
|
|
431
|
+
chargedAt?: Date,
|
|
432
|
+
): string | null {
|
|
433
|
+
const recurring = policy.providerChargeComponents.find(
|
|
434
|
+
(component) => component.kind === 'provider_recurring_period',
|
|
435
|
+
);
|
|
436
|
+
if (recurring?.kind === 'provider_recurring_period') {
|
|
437
|
+
const base = chargedAt ?? new Date();
|
|
438
|
+
return new Date(
|
|
439
|
+
base.getTime() + recurring.periodDays * 24 * 60 * 60 * 1000,
|
|
440
|
+
).toISOString();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const hasMonthlyPlanRenewal = policy.providerChargeComponents.some(
|
|
444
|
+
(component) =>
|
|
445
|
+
component.kind === 'provider_entitlement_cost' &&
|
|
446
|
+
component.phase === 'periodic_renewal',
|
|
447
|
+
);
|
|
448
|
+
if (!hasMonthlyPlanRenewal) {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const base = chargedAt ?? new Date();
|
|
453
|
+
return new Date(base.getTime() + 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
454
|
+
}
|