@polar-sh/sdk 1.0.2 → 1.0.3-alpha.3
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/2026-04/index.cjs +4 -4
- package/dist/2026-04/index.d.cts +2 -2
- package/dist/2026-04/index.d.mts +2 -2
- package/dist/2026-04/index.mjs +3 -3
- package/dist/2026-04/services/benefit_grants.cjs +1 -1
- package/dist/2026-04/services/benefit_grants.d.cts +1 -1
- package/dist/2026-04/services/benefit_grants.d.mts +1 -1
- package/dist/2026-04/services/benefit_grants.mjs +1 -1
- package/dist/2026-04/services/benefits.cjs +1 -1
- package/dist/2026-04/services/benefits.d.cts +1 -1
- package/dist/2026-04/services/benefits.d.mts +1 -1
- package/dist/2026-04/services/benefits.mjs +1 -1
- package/dist/2026-04/services/checkout_links.cjs +1 -1
- package/dist/2026-04/services/checkout_links.d.cts +1 -1
- package/dist/2026-04/services/checkout_links.d.mts +1 -1
- package/dist/2026-04/services/checkout_links.mjs +1 -1
- package/dist/2026-04/services/checkouts.cjs +1 -1
- package/dist/2026-04/services/checkouts.d.cts +1 -1
- package/dist/2026-04/services/checkouts.d.mts +1 -1
- package/dist/2026-04/services/checkouts.mjs +1 -1
- package/dist/2026-04/services/custom_fields.cjs +1 -1
- package/dist/2026-04/services/custom_fields.d.cts +1 -1
- package/dist/2026-04/services/custom_fields.d.mts +1 -1
- package/dist/2026-04/services/custom_fields.mjs +1 -1
- package/dist/2026-04/services/customer_meters.cjs +1 -1
- package/dist/2026-04/services/customer_meters.d.cts +1 -1
- package/dist/2026-04/services/customer_meters.d.mts +1 -1
- package/dist/2026-04/services/customer_meters.mjs +1 -1
- package/dist/2026-04/services/customer_portal/benefit_grants.cjs +1 -1
- package/dist/2026-04/services/customer_portal/benefit_grants.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/benefit_grants.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/benefit_grants.mjs +1 -1
- package/dist/2026-04/services/customer_portal/customer_meters.cjs +1 -1
- package/dist/2026-04/services/customer_portal/customer_meters.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/customer_meters.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/customer_meters.mjs +1 -1
- package/dist/2026-04/services/customer_portal/customer_session.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/customer_session.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/customers.cjs +1 -1
- package/dist/2026-04/services/customer_portal/customers.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/customers.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/customers.mjs +1 -1
- package/dist/2026-04/services/customer_portal/downloadables.cjs +1 -1
- package/dist/2026-04/services/customer_portal/downloadables.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/downloadables.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/downloadables.mjs +1 -1
- package/dist/2026-04/services/customer_portal/index.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/index.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/license_keys.cjs +1 -1
- package/dist/2026-04/services/customer_portal/license_keys.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/license_keys.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/license_keys.mjs +1 -1
- package/dist/2026-04/services/customer_portal/members.cjs +1 -1
- package/dist/2026-04/services/customer_portal/members.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/members.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/members.mjs +1 -1
- package/dist/2026-04/services/customer_portal/orders.cjs +1 -1
- package/dist/2026-04/services/customer_portal/orders.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/orders.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/orders.mjs +1 -1
- package/dist/2026-04/services/customer_portal/organizations.cjs +1 -1
- package/dist/2026-04/services/customer_portal/organizations.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/organizations.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/organizations.mjs +1 -1
- package/dist/2026-04/services/customer_portal/seats.cjs +1 -1
- package/dist/2026-04/services/customer_portal/seats.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/seats.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/seats.mjs +1 -1
- package/dist/2026-04/services/customer_portal/subscriptions.cjs +1 -1
- package/dist/2026-04/services/customer_portal/subscriptions.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/subscriptions.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/subscriptions.mjs +1 -1
- package/dist/2026-04/services/customer_portal/wallets.cjs +1 -1
- package/dist/2026-04/services/customer_portal/wallets.d.cts +1 -1
- package/dist/2026-04/services/customer_portal/wallets.d.mts +1 -1
- package/dist/2026-04/services/customer_portal/wallets.mjs +1 -1
- package/dist/2026-04/services/customer_seats.cjs +1 -1
- package/dist/2026-04/services/customer_seats.d.cts +1 -1
- package/dist/2026-04/services/customer_seats.d.mts +1 -1
- package/dist/2026-04/services/customer_seats.mjs +1 -1
- package/dist/2026-04/services/customer_sessions.cjs +1 -1
- package/dist/2026-04/services/customer_sessions.d.cts +1 -1
- package/dist/2026-04/services/customer_sessions.d.mts +1 -1
- package/dist/2026-04/services/customer_sessions.mjs +1 -1
- package/dist/2026-04/services/customers/index.cjs +1 -1
- package/dist/2026-04/services/customers/index.d.cts +1 -1
- package/dist/2026-04/services/customers/index.d.mts +1 -1
- package/dist/2026-04/services/customers/index.mjs +1 -1
- package/dist/2026-04/services/customers/members.cjs +1 -1
- package/dist/2026-04/services/customers/members.d.cts +1 -1
- package/dist/2026-04/services/customers/members.d.mts +1 -1
- package/dist/2026-04/services/customers/members.mjs +1 -1
- package/dist/2026-04/services/discounts.cjs +1 -1
- package/dist/2026-04/services/discounts.d.cts +1 -1
- package/dist/2026-04/services/discounts.d.mts +1 -1
- package/dist/2026-04/services/discounts.mjs +1 -1
- package/dist/2026-04/services/disputes.cjs +1 -1
- package/dist/2026-04/services/disputes.d.cts +1 -1
- package/dist/2026-04/services/disputes.d.mts +1 -1
- package/dist/2026-04/services/disputes.mjs +1 -1
- package/dist/2026-04/services/event_types.cjs +1 -1
- package/dist/2026-04/services/event_types.d.cts +1 -1
- package/dist/2026-04/services/event_types.d.mts +1 -1
- package/dist/2026-04/services/event_types.mjs +1 -1
- package/dist/2026-04/services/events.cjs +1 -1
- package/dist/2026-04/services/events.d.cts +1 -1
- package/dist/2026-04/services/events.d.mts +1 -1
- package/dist/2026-04/services/events.mjs +1 -1
- package/dist/2026-04/services/files.cjs +1 -1
- package/dist/2026-04/services/files.d.cts +1 -1
- package/dist/2026-04/services/files.d.mts +1 -1
- package/dist/2026-04/services/files.mjs +1 -1
- package/dist/2026-04/services/license_keys.cjs +1 -1
- package/dist/2026-04/services/license_keys.d.cts +1 -1
- package/dist/2026-04/services/license_keys.d.mts +1 -1
- package/dist/2026-04/services/license_keys.mjs +1 -1
- package/dist/2026-04/services/meters.cjs +1 -1
- package/dist/2026-04/services/meters.d.cts +1 -1
- package/dist/2026-04/services/meters.d.mts +1 -1
- package/dist/2026-04/services/meters.mjs +1 -1
- package/dist/2026-04/services/metrics.cjs +1 -1
- package/dist/2026-04/services/metrics.d.cts +1 -1
- package/dist/2026-04/services/metrics.d.mts +1 -1
- package/dist/2026-04/services/metrics.mjs +1 -1
- package/dist/2026-04/services/oauth2/clients/index.d.cts +1 -1
- package/dist/2026-04/services/oauth2/clients/index.d.mts +1 -1
- package/dist/2026-04/services/oauth2/clients/oauth2.cjs +1 -1
- package/dist/2026-04/services/oauth2/clients/oauth2.d.cts +1 -1
- package/dist/2026-04/services/oauth2/clients/oauth2.d.mts +1 -1
- package/dist/2026-04/services/oauth2/clients/oauth2.mjs +1 -1
- package/dist/2026-04/services/oauth2/index.d.cts +1 -1
- package/dist/2026-04/services/oauth2/index.d.mts +1 -1
- package/dist/2026-04/services/orders.cjs +1 -1
- package/dist/2026-04/services/orders.d.cts +1 -1
- package/dist/2026-04/services/orders.d.mts +1 -1
- package/dist/2026-04/services/orders.mjs +1 -1
- package/dist/2026-04/services/organizations.cjs +1 -1
- package/dist/2026-04/services/organizations.d.cts +1 -1
- package/dist/2026-04/services/organizations.d.mts +1 -1
- package/dist/2026-04/services/organizations.mjs +1 -1
- package/dist/2026-04/services/payments.cjs +1 -1
- package/dist/2026-04/services/payments.d.cts +1 -1
- package/dist/2026-04/services/payments.d.mts +1 -1
- package/dist/2026-04/services/payments.mjs +1 -1
- package/dist/2026-04/services/products.cjs +1 -1
- package/dist/2026-04/services/products.d.cts +1 -1
- package/dist/2026-04/services/products.d.mts +1 -1
- package/dist/2026-04/services/products.mjs +1 -1
- package/dist/2026-04/services/refunds.cjs +1 -1
- package/dist/2026-04/services/refunds.d.cts +1 -1
- package/dist/2026-04/services/refunds.d.mts +1 -1
- package/dist/2026-04/services/refunds.mjs +1 -1
- package/dist/2026-04/services/subscriptions.cjs +1 -1
- package/dist/2026-04/services/subscriptions.d.cts +1 -1
- package/dist/2026-04/services/subscriptions.d.mts +1 -1
- package/dist/2026-04/services/subscriptions.mjs +1 -1
- package/dist/2026-04/services/webhooks.cjs +1 -1
- package/dist/2026-04/services/webhooks.d.cts +1 -1
- package/dist/2026-04/services/webhooks.d.mts +1 -1
- package/dist/2026-04/services/webhooks.mjs +1 -1
- package/dist/2026-10/index.cjs +4 -4
- package/dist/2026-10/index.cjs.map +1 -1
- package/dist/2026-10/index.d.cts +2 -2
- package/dist/2026-10/index.d.mts +2 -2
- package/dist/2026-10/index.mjs +3 -3
- package/dist/2026-10/index.mjs.map +1 -1
- package/dist/2026-10/services/benefit_grants.cjs +1 -1
- package/dist/2026-10/services/benefit_grants.d.cts +1 -1
- package/dist/2026-10/services/benefit_grants.d.mts +1 -1
- package/dist/2026-10/services/benefit_grants.mjs +1 -1
- package/dist/2026-10/services/benefits.cjs +1 -1
- package/dist/2026-10/services/benefits.d.cts +1 -1
- package/dist/2026-10/services/benefits.d.mts +1 -1
- package/dist/2026-10/services/benefits.mjs +1 -1
- package/dist/2026-10/services/checkout_links.cjs +1 -1
- package/dist/2026-10/services/checkout_links.d.cts +1 -1
- package/dist/2026-10/services/checkout_links.d.mts +1 -1
- package/dist/2026-10/services/checkout_links.mjs +1 -1
- package/dist/2026-10/services/checkouts.cjs +1 -1
- package/dist/2026-10/services/checkouts.d.cts +1 -1
- package/dist/2026-10/services/checkouts.d.mts +1 -1
- package/dist/2026-10/services/checkouts.mjs +1 -1
- package/dist/2026-10/services/custom_fields.cjs +1 -1
- package/dist/2026-10/services/custom_fields.d.cts +1 -1
- package/dist/2026-10/services/custom_fields.d.mts +1 -1
- package/dist/2026-10/services/custom_fields.mjs +1 -1
- package/dist/2026-10/services/customer_meters.cjs +1 -1
- package/dist/2026-10/services/customer_meters.d.cts +1 -1
- package/dist/2026-10/services/customer_meters.d.mts +1 -1
- package/dist/2026-10/services/customer_meters.mjs +1 -1
- package/dist/2026-10/services/customer_portal/benefit_grants.cjs +1 -1
- package/dist/2026-10/services/customer_portal/benefit_grants.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/benefit_grants.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/benefit_grants.mjs +1 -1
- package/dist/2026-10/services/customer_portal/customer_meters.cjs +1 -1
- package/dist/2026-10/services/customer_portal/customer_meters.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/customer_meters.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/customer_meters.mjs +1 -1
- package/dist/2026-10/services/customer_portal/customer_session.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/customer_session.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/customers.cjs +1 -1
- package/dist/2026-10/services/customer_portal/customers.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/customers.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/customers.mjs +1 -1
- package/dist/2026-10/services/customer_portal/downloadables.cjs +1 -1
- package/dist/2026-10/services/customer_portal/downloadables.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/downloadables.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/downloadables.mjs +1 -1
- package/dist/2026-10/services/customer_portal/index.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/index.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/license_keys.cjs +1 -1
- package/dist/2026-10/services/customer_portal/license_keys.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/license_keys.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/license_keys.mjs +1 -1
- package/dist/2026-10/services/customer_portal/members.cjs +1 -1
- package/dist/2026-10/services/customer_portal/members.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/members.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/members.mjs +1 -1
- package/dist/2026-10/services/customer_portal/orders.cjs +1 -1
- package/dist/2026-10/services/customer_portal/orders.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/orders.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/orders.mjs +1 -1
- package/dist/2026-10/services/customer_portal/organizations.cjs +1 -1
- package/dist/2026-10/services/customer_portal/organizations.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/organizations.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/organizations.mjs +1 -1
- package/dist/2026-10/services/customer_portal/seats.cjs +1 -1
- package/dist/2026-10/services/customer_portal/seats.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/seats.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/seats.mjs +1 -1
- package/dist/2026-10/services/customer_portal/subscriptions.cjs +1 -1
- package/dist/2026-10/services/customer_portal/subscriptions.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/subscriptions.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/subscriptions.mjs +1 -1
- package/dist/2026-10/services/customer_portal/wallets.cjs +1 -1
- package/dist/2026-10/services/customer_portal/wallets.d.cts +1 -1
- package/dist/2026-10/services/customer_portal/wallets.d.mts +1 -1
- package/dist/2026-10/services/customer_portal/wallets.mjs +1 -1
- package/dist/2026-10/services/customer_seats.cjs +1 -1
- package/dist/2026-10/services/customer_seats.d.cts +1 -1
- package/dist/2026-10/services/customer_seats.d.mts +1 -1
- package/dist/2026-10/services/customer_seats.mjs +1 -1
- package/dist/2026-10/services/customer_sessions.cjs +1 -1
- package/dist/2026-10/services/customer_sessions.d.cts +1 -1
- package/dist/2026-10/services/customer_sessions.d.mts +1 -1
- package/dist/2026-10/services/customer_sessions.mjs +1 -1
- package/dist/2026-10/services/customers/index.cjs +1 -1
- package/dist/2026-10/services/customers/index.d.cts +1 -1
- package/dist/2026-10/services/customers/index.d.mts +1 -1
- package/dist/2026-10/services/customers/index.mjs +1 -1
- package/dist/2026-10/services/customers/members.cjs +1 -1
- package/dist/2026-10/services/customers/members.d.cts +1 -1
- package/dist/2026-10/services/customers/members.d.mts +1 -1
- package/dist/2026-10/services/customers/members.mjs +1 -1
- package/dist/2026-10/services/discounts.cjs +1 -1
- package/dist/2026-10/services/discounts.d.cts +1 -1
- package/dist/2026-10/services/discounts.d.mts +1 -1
- package/dist/2026-10/services/discounts.mjs +1 -1
- package/dist/2026-10/services/disputes.cjs +1 -1
- package/dist/2026-10/services/disputes.d.cts +1 -1
- package/dist/2026-10/services/disputes.d.mts +1 -1
- package/dist/2026-10/services/disputes.mjs +1 -1
- package/dist/2026-10/services/event_types.cjs +1 -1
- package/dist/2026-10/services/event_types.d.cts +1 -1
- package/dist/2026-10/services/event_types.d.mts +1 -1
- package/dist/2026-10/services/event_types.mjs +1 -1
- package/dist/2026-10/services/events.cjs +1 -1
- package/dist/2026-10/services/events.d.cts +1 -1
- package/dist/2026-10/services/events.d.mts +1 -1
- package/dist/2026-10/services/events.mjs +1 -1
- package/dist/2026-10/services/files.cjs +1 -1
- package/dist/2026-10/services/files.d.cts +1 -1
- package/dist/2026-10/services/files.d.mts +1 -1
- package/dist/2026-10/services/files.mjs +1 -1
- package/dist/2026-10/services/license_keys.cjs +1 -1
- package/dist/2026-10/services/license_keys.d.cts +1 -1
- package/dist/2026-10/services/license_keys.d.mts +1 -1
- package/dist/2026-10/services/license_keys.mjs +1 -1
- package/dist/2026-10/services/meters.cjs +1 -1
- package/dist/2026-10/services/meters.d.cts +1 -1
- package/dist/2026-10/services/meters.d.mts +1 -1
- package/dist/2026-10/services/meters.mjs +1 -1
- package/dist/2026-10/services/metrics.cjs +1 -1
- package/dist/2026-10/services/metrics.d.cts +1 -1
- package/dist/2026-10/services/metrics.d.mts +1 -1
- package/dist/2026-10/services/metrics.mjs +1 -1
- package/dist/2026-10/services/oauth2/clients/index.d.cts +1 -1
- package/dist/2026-10/services/oauth2/clients/index.d.mts +1 -1
- package/dist/2026-10/services/oauth2/clients/oauth2.cjs +1 -1
- package/dist/2026-10/services/oauth2/clients/oauth2.d.cts +1 -1
- package/dist/2026-10/services/oauth2/clients/oauth2.d.mts +1 -1
- package/dist/2026-10/services/oauth2/clients/oauth2.mjs +1 -1
- package/dist/2026-10/services/oauth2/index.d.cts +1 -1
- package/dist/2026-10/services/oauth2/index.d.mts +1 -1
- package/dist/2026-10/services/orders.cjs +1 -1
- package/dist/2026-10/services/orders.d.cts +1 -1
- package/dist/2026-10/services/orders.d.mts +1 -1
- package/dist/2026-10/services/orders.mjs +1 -1
- package/dist/2026-10/services/organizations.cjs +1 -1
- package/dist/2026-10/services/organizations.d.cts +1 -1
- package/dist/2026-10/services/organizations.d.mts +1 -1
- package/dist/2026-10/services/organizations.mjs +1 -1
- package/dist/2026-10/services/payments.cjs +1 -1
- package/dist/2026-10/services/payments.d.cts +1 -1
- package/dist/2026-10/services/payments.d.mts +1 -1
- package/dist/2026-10/services/payments.mjs +1 -1
- package/dist/2026-10/services/products.cjs +1 -1
- package/dist/2026-10/services/products.d.cts +1 -1
- package/dist/2026-10/services/products.d.mts +1 -1
- package/dist/2026-10/services/products.mjs +1 -1
- package/dist/2026-10/services/refunds.cjs +1 -1
- package/dist/2026-10/services/refunds.d.cts +1 -1
- package/dist/2026-10/services/refunds.d.mts +1 -1
- package/dist/2026-10/services/refunds.mjs +1 -1
- package/dist/2026-10/services/subscriptions.cjs +1 -1
- package/dist/2026-10/services/subscriptions.d.cts +1 -1
- package/dist/2026-10/services/subscriptions.d.mts +1 -1
- package/dist/2026-10/services/subscriptions.mjs +1 -1
- package/dist/2026-10/services/webhooks.cjs +1 -1
- package/dist/2026-10/services/webhooks.d.cts +1 -1
- package/dist/2026-10/services/webhooks.d.mts +1 -1
- package/dist/2026-10/services/webhooks.mjs +1 -1
- package/dist/2027-01/index.cjs +4 -4
- package/dist/2027-01/index.cjs.map +1 -1
- package/dist/2027-01/index.d.cts +2 -2
- package/dist/2027-01/index.d.mts +2 -2
- package/dist/2027-01/index.mjs +3 -3
- package/dist/2027-01/index.mjs.map +1 -1
- package/dist/2027-01/services/benefit_grants.cjs +1 -1
- package/dist/2027-01/services/benefit_grants.d.cts +1 -1
- package/dist/2027-01/services/benefit_grants.d.mts +1 -1
- package/dist/2027-01/services/benefit_grants.mjs +1 -1
- package/dist/2027-01/services/benefits.cjs +2 -2
- package/dist/2027-01/services/benefits.cjs.map +1 -1
- package/dist/2027-01/services/benefits.d.cts +1 -1
- package/dist/2027-01/services/benefits.d.mts +1 -1
- package/dist/2027-01/services/benefits.mjs +2 -2
- package/dist/2027-01/services/benefits.mjs.map +1 -1
- package/dist/2027-01/services/checkout_links.cjs +1 -1
- package/dist/2027-01/services/checkout_links.d.cts +1 -1
- package/dist/2027-01/services/checkout_links.d.mts +1 -1
- package/dist/2027-01/services/checkout_links.mjs +1 -1
- package/dist/2027-01/services/checkouts.cjs +1 -1
- package/dist/2027-01/services/checkouts.d.cts +1 -1
- package/dist/2027-01/services/checkouts.d.mts +1 -1
- package/dist/2027-01/services/checkouts.mjs +1 -1
- package/dist/2027-01/services/custom_fields.cjs +1 -1
- package/dist/2027-01/services/custom_fields.d.cts +1 -1
- package/dist/2027-01/services/custom_fields.d.mts +1 -1
- package/dist/2027-01/services/custom_fields.mjs +1 -1
- package/dist/2027-01/services/customer_meters.cjs +32 -1
- package/dist/2027-01/services/customer_meters.cjs.map +1 -1
- package/dist/2027-01/services/customer_meters.d.cts +4 -2
- package/dist/2027-01/services/customer_meters.d.cts.map +1 -1
- package/dist/2027-01/services/customer_meters.d.mts +4 -2
- package/dist/2027-01/services/customer_meters.d.mts.map +1 -1
- package/dist/2027-01/services/customer_meters.mjs +32 -2
- package/dist/2027-01/services/customer_meters.mjs.map +1 -1
- package/dist/2027-01/services/customer_portal/benefit_grants.cjs +1 -1
- package/dist/2027-01/services/customer_portal/benefit_grants.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/benefit_grants.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/benefit_grants.mjs +1 -1
- package/dist/2027-01/services/customer_portal/customer_meters.cjs +1 -1
- package/dist/2027-01/services/customer_portal/customer_meters.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/customer_meters.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/customer_meters.mjs +1 -1
- package/dist/2027-01/services/customer_portal/customer_session.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/customer_session.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/customers.cjs +1 -1
- package/dist/2027-01/services/customer_portal/customers.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/customers.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/customers.mjs +1 -1
- package/dist/2027-01/services/customer_portal/downloadables.cjs +1 -1
- package/dist/2027-01/services/customer_portal/downloadables.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/downloadables.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/downloadables.mjs +1 -1
- package/dist/2027-01/services/customer_portal/index.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/index.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/license_keys.cjs +1 -1
- package/dist/2027-01/services/customer_portal/license_keys.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/license_keys.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/license_keys.mjs +1 -1
- package/dist/2027-01/services/customer_portal/members.cjs +1 -1
- package/dist/2027-01/services/customer_portal/members.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/members.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/members.mjs +1 -1
- package/dist/2027-01/services/customer_portal/orders.cjs +1 -1
- package/dist/2027-01/services/customer_portal/orders.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/orders.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/orders.mjs +1 -1
- package/dist/2027-01/services/customer_portal/organizations.cjs +1 -1
- package/dist/2027-01/services/customer_portal/organizations.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/organizations.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/organizations.mjs +1 -1
- package/dist/2027-01/services/customer_portal/seats.cjs +1 -1
- package/dist/2027-01/services/customer_portal/seats.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/seats.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/seats.mjs +1 -1
- package/dist/2027-01/services/customer_portal/subscriptions.cjs +1 -1
- package/dist/2027-01/services/customer_portal/subscriptions.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/subscriptions.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/subscriptions.mjs +1 -1
- package/dist/2027-01/services/customer_portal/wallets.cjs +1 -1
- package/dist/2027-01/services/customer_portal/wallets.d.cts +1 -1
- package/dist/2027-01/services/customer_portal/wallets.d.mts +1 -1
- package/dist/2027-01/services/customer_portal/wallets.mjs +1 -1
- package/dist/2027-01/services/customer_seats.cjs +11 -1
- package/dist/2027-01/services/customer_seats.cjs.map +1 -1
- package/dist/2027-01/services/customer_seats.d.cts +1 -1
- package/dist/2027-01/services/customer_seats.d.cts.map +1 -1
- package/dist/2027-01/services/customer_seats.d.mts +1 -1
- package/dist/2027-01/services/customer_seats.d.mts.map +1 -1
- package/dist/2027-01/services/customer_seats.mjs +11 -1
- package/dist/2027-01/services/customer_seats.mjs.map +1 -1
- package/dist/2027-01/services/customer_sessions.cjs +1 -1
- package/dist/2027-01/services/customer_sessions.d.cts +1 -1
- package/dist/2027-01/services/customer_sessions.d.mts +1 -1
- package/dist/2027-01/services/customer_sessions.mjs +1 -1
- package/dist/2027-01/services/customers/index.cjs +3 -1
- package/dist/2027-01/services/customers/index.cjs.map +1 -1
- package/dist/2027-01/services/customers/index.d.cts +1 -1
- package/dist/2027-01/services/customers/index.d.cts.map +1 -1
- package/dist/2027-01/services/customers/index.d.mts +1 -1
- package/dist/2027-01/services/customers/index.d.mts.map +1 -1
- package/dist/2027-01/services/customers/index.mjs +3 -1
- package/dist/2027-01/services/customers/index.mjs.map +1 -1
- package/dist/2027-01/services/customers/members.cjs +1 -1
- package/dist/2027-01/services/customers/members.d.cts +1 -1
- package/dist/2027-01/services/customers/members.d.mts +1 -1
- package/dist/2027-01/services/customers/members.mjs +1 -1
- package/dist/2027-01/services/discounts.cjs +2 -1
- package/dist/2027-01/services/discounts.cjs.map +1 -1
- package/dist/2027-01/services/discounts.d.cts +5 -1
- package/dist/2027-01/services/discounts.d.cts.map +1 -1
- package/dist/2027-01/services/discounts.d.mts +5 -1
- package/dist/2027-01/services/discounts.d.mts.map +1 -1
- package/dist/2027-01/services/discounts.mjs +2 -1
- package/dist/2027-01/services/discounts.mjs.map +1 -1
- package/dist/2027-01/services/disputes.cjs +1 -1
- package/dist/2027-01/services/disputes.d.cts +1 -1
- package/dist/2027-01/services/disputes.d.mts +1 -1
- package/dist/2027-01/services/disputes.mjs +1 -1
- package/dist/2027-01/services/event_types.cjs +1 -1
- package/dist/2027-01/services/event_types.d.cts +1 -1
- package/dist/2027-01/services/event_types.d.mts +1 -1
- package/dist/2027-01/services/event_types.mjs +1 -1
- package/dist/2027-01/services/events.cjs +1 -1
- package/dist/2027-01/services/events.d.cts +1 -1
- package/dist/2027-01/services/events.d.mts +1 -1
- package/dist/2027-01/services/events.mjs +1 -1
- package/dist/2027-01/services/files.cjs +1 -1
- package/dist/2027-01/services/files.d.cts +1 -1
- package/dist/2027-01/services/files.d.mts +1 -1
- package/dist/2027-01/services/files.mjs +1 -1
- package/dist/2027-01/services/license_keys.cjs +1 -1
- package/dist/2027-01/services/license_keys.d.cts +1 -1
- package/dist/2027-01/services/license_keys.d.mts +1 -1
- package/dist/2027-01/services/license_keys.mjs +1 -1
- package/dist/2027-01/services/meters.cjs +1 -1
- package/dist/2027-01/services/meters.d.cts +1 -1
- package/dist/2027-01/services/meters.d.mts +1 -1
- package/dist/2027-01/services/meters.mjs +1 -1
- package/dist/2027-01/services/metrics.cjs +1 -1
- package/dist/2027-01/services/metrics.d.cts +1 -1
- package/dist/2027-01/services/metrics.d.mts +1 -1
- package/dist/2027-01/services/metrics.mjs +1 -1
- package/dist/2027-01/services/oauth2/clients/index.d.cts +1 -1
- package/dist/2027-01/services/oauth2/clients/index.d.mts +1 -1
- package/dist/2027-01/services/oauth2/clients/oauth2.cjs +1 -1
- package/dist/2027-01/services/oauth2/clients/oauth2.d.cts +1 -1
- package/dist/2027-01/services/oauth2/clients/oauth2.d.mts +1 -1
- package/dist/2027-01/services/oauth2/clients/oauth2.mjs +1 -1
- package/dist/2027-01/services/oauth2/index.d.cts +1 -1
- package/dist/2027-01/services/oauth2/index.d.mts +1 -1
- package/dist/2027-01/services/orders.cjs +1 -1
- package/dist/2027-01/services/orders.d.cts +1 -1
- package/dist/2027-01/services/orders.d.mts +1 -1
- package/dist/2027-01/services/orders.mjs +1 -1
- package/dist/2027-01/services/organizations.cjs +1 -1
- package/dist/2027-01/services/organizations.d.cts +1 -1
- package/dist/2027-01/services/organizations.d.mts +1 -1
- package/dist/2027-01/services/organizations.mjs +1 -1
- package/dist/2027-01/services/payments.cjs +1 -1
- package/dist/2027-01/services/payments.d.cts +1 -1
- package/dist/2027-01/services/payments.d.mts +1 -1
- package/dist/2027-01/services/payments.mjs +1 -1
- package/dist/2027-01/services/products.cjs +1 -1
- package/dist/2027-01/services/products.d.cts +1 -1
- package/dist/2027-01/services/products.d.mts +1 -1
- package/dist/2027-01/services/products.mjs +1 -1
- package/dist/2027-01/services/refunds.cjs +1 -1
- package/dist/2027-01/services/refunds.d.cts +1 -1
- package/dist/2027-01/services/refunds.d.mts +1 -1
- package/dist/2027-01/services/refunds.mjs +1 -1
- package/dist/2027-01/services/subscriptions.cjs +1 -1
- package/dist/2027-01/services/subscriptions.d.cts +1 -1
- package/dist/2027-01/services/subscriptions.d.mts +1 -1
- package/dist/2027-01/services/subscriptions.mjs +1 -1
- package/dist/2027-01/services/webhooks.cjs +1 -1
- package/dist/2027-01/services/webhooks.d.cts +1 -1
- package/dist/2027-01/services/webhooks.d.mts +1 -1
- package/dist/2027-01/services/webhooks.mjs +1 -1
- package/dist/{base-elZHdyac.cjs → base-DHy1aJ70.cjs} +5 -2
- package/dist/{base-Cir88A-B.mjs.map → base-DHy1aJ70.cjs.map} +1 -1
- package/dist/{base-Cir88A-B.mjs → base-jGd8URjq.mjs} +5 -2
- package/dist/{base-elZHdyac.cjs.map → base-jGd8URjq.mjs.map} +1 -1
- package/dist/{errors-BNX4S5mh.cjs → errors-B-sbvXah.cjs} +23 -3
- package/dist/errors-B-sbvXah.cjs.map +1 -0
- package/dist/{errors-CU3SpW0J.mjs → errors-B3PVjuYS.mjs} +2 -2
- package/dist/{errors-CU3SpW0J.mjs.map → errors-B3PVjuYS.mjs.map} +1 -1
- package/dist/{errors-CpcxXUUY.cjs → errors-BHJtI-Az.cjs} +3 -3
- package/dist/{errors-CpcxXUUY.cjs.map → errors-BHJtI-Az.cjs.map} +1 -1
- package/dist/{errors-D48S0nru.cjs → errors-CzBzBuG7.cjs} +3 -3
- package/dist/{errors-D48S0nru.cjs.map → errors-CzBzBuG7.cjs.map} +1 -1
- package/dist/{errors-DeSE0QDX.mjs → errors-TTexeU-I.mjs} +17 -3
- package/dist/errors-TTexeU-I.mjs.map +1 -0
- package/dist/{errors-CmQk48Hf.mjs → errors-v8-ROkLl.mjs} +2 -2
- package/dist/{errors-CmQk48Hf.mjs.map → errors-v8-ROkLl.mjs.map} +1 -1
- package/dist/{index-DW6QhN4v.d.mts → index-4QvmyIfk.d.mts} +14 -3
- package/dist/index-4QvmyIfk.d.mts.map +1 -0
- package/dist/{index-fmn-6v-G.d.cts → index-BsjbzbzQ.d.cts} +2 -2
- package/dist/index-BsjbzbzQ.d.cts.map +1 -0
- package/dist/{index-NMXOM5dy.d.cts → index-CdOi2TtF.d.cts} +2 -2
- package/dist/{index-NMXOM5dy.d.cts.map → index-CdOi2TtF.d.cts.map} +1 -1
- package/dist/{index-BAjXT-W8.d.cts → index-ClJvTviw.d.cts} +14 -3
- package/dist/index-ClJvTviw.d.cts.map +1 -0
- package/dist/{index-CZh-OhWh.d.mts → index-Dsoc8aUZ.d.mts} +2 -2
- package/dist/index-Dsoc8aUZ.d.mts.map +1 -0
- package/dist/{index-CgqeU9bV.d.mts → index-aTh5rKU2.d.mts} +2 -2
- package/dist/{index-CgqeU9bV.d.mts.map → index-aTh5rKU2.d.mts.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -2
- package/dist/{models-MQGPXSzo.d.cts → models-DEZDKjvs.d.cts} +2 -2
- package/dist/{models-MQGPXSzo.d.cts.map → models-DEZDKjvs.d.cts.map} +1 -1
- package/dist/{models-MQGPXSzo.d.mts → models-DEZDKjvs.d.mts} +2 -2
- package/dist/{models-MQGPXSzo.d.mts.map → models-DEZDKjvs.d.mts.map} +1 -1
- package/dist/models-DqFvgD36.d.cts +22224 -0
- package/dist/models-DqFvgD36.d.cts.map +1 -0
- package/dist/models-DqFvgD36.d.mts +22224 -0
- package/dist/models-DqFvgD36.d.mts.map +1 -0
- package/dist/{models-P8aaHkKR.d.cts → models-ZHvf-GL5.d.cts} +12 -12
- package/dist/{models-P8aaHkKR.d.cts.map → models-ZHvf-GL5.d.cts.map} +1 -1
- package/dist/{models-P8aaHkKR.d.mts → models-ZHvf-GL5.d.mts} +12 -12
- package/dist/{models-P8aaHkKR.d.mts.map → models-ZHvf-GL5.d.mts.map} +1 -1
- package/dist/outpost/index.cjs +587 -0
- package/dist/outpost/index.cjs.map +1 -0
- package/dist/outpost/index.d.cts +114 -0
- package/dist/outpost/index.d.cts.map +1 -0
- package/dist/outpost/index.d.mts +114 -0
- package/dist/outpost/index.d.mts.map +1 -0
- package/dist/outpost/index.mjs +581 -0
- package/dist/outpost/index.mjs.map +1 -0
- package/dist/rolldown-runtime-C0BPl7ul.cjs +43 -0
- package/dist/{webhooks-DHKM6i1k.mjs → webhooks-DZ_9hr_d.mjs} +2 -2
- package/dist/{webhooks-DHKM6i1k.mjs.map → webhooks-DZ_9hr_d.mjs.map} +1 -1
- package/dist/{webhooks-Da8LWSE5.cjs → webhooks-iwHkVmKt.cjs} +2 -2
- package/dist/{webhooks-Da8LWSE5.cjs.map → webhooks-iwHkVmKt.cjs.map} +1 -1
- package/package.json +31 -5
- package/dist/errors-BNX4S5mh.cjs.map +0 -1
- package/dist/errors-DeSE0QDX.mjs.map +0 -1
- package/dist/index-BAjXT-W8.d.cts.map +0 -1
- package/dist/index-CZh-OhWh.d.mts.map +0 -1
- package/dist/index-DW6QhN4v.d.mts.map +0 -1
- package/dist/index-fmn-6v-G.d.cts.map +0 -1
- package/dist/models-D1RXR1_Z.d.cts +0 -22148
- package/dist/models-D1RXR1_Z.d.cts.map +0 -1
- package/dist/models-D1RXR1_Z.d.mts +0 -22148
- package/dist/models-D1RXR1_Z.d.mts.map +0 -1
- package/dist/rolldown-runtime-ak5kbic3.cjs +0 -18
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
import { r as PolarError } from "../base-jGd8URjq.mjs";
|
|
2
|
+
import { Redis } from "ioredis";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { once } from "node:events";
|
|
5
|
+
import WebSocket from "ws";
|
|
6
|
+
//#region src/outpost/event.ts
|
|
7
|
+
const customerKey = (input) => {
|
|
8
|
+
if (input.customer_id === void 0 === (input.external_customer_id === void 0)) throw new TypeError("Provide exactly one customer_id or external_customer_id");
|
|
9
|
+
const identifier = input.customer_id ?? input.external_customer_id;
|
|
10
|
+
return `${input.customer_id !== void 0 ? "customer" : "external"}:${identifier}`;
|
|
11
|
+
};
|
|
12
|
+
const parseEvent = (input) => {
|
|
13
|
+
if (input.timestamp !== void 0 && !/(Z|[+-]\d{2}:?\d{2})$/i.test(input.timestamp)) throw new TypeError("Event timestamps must include a timezone");
|
|
14
|
+
const timestamp = input.timestamp === void 0 ? Date.now() : Date.parse(input.timestamp);
|
|
15
|
+
const fraction = input.timestamp?.match(/\.(\d+)/)?.[1] ?? "";
|
|
16
|
+
const microseconds = timestamp * 1e3 + Number(fraction.padEnd(6, "0").slice(3, 6));
|
|
17
|
+
if (!Number.isSafeInteger(microseconds)) throw new TypeError("Invalid event timestamp");
|
|
18
|
+
return {
|
|
19
|
+
name: input.name,
|
|
20
|
+
customer: customerKey(input),
|
|
21
|
+
timestamp: microseconds,
|
|
22
|
+
metadata: input.metadata ?? {}
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const getProperty = (event, property) => {
|
|
26
|
+
if (property === "name") return event.name;
|
|
27
|
+
if (property === "source") return "user";
|
|
28
|
+
if (property === "timestamp") return Math.trunc(event.timestamp / 1e6);
|
|
29
|
+
let value = event.metadata;
|
|
30
|
+
for (const part of property.replace(/^metadata\./, "").split(".")) {
|
|
31
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || !Object.hasOwn(value, part)) return void 0;
|
|
32
|
+
value = value[part];
|
|
33
|
+
}
|
|
34
|
+
return value;
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/outpost/reducer.ts
|
|
38
|
+
const emptyStatistics = () => ({
|
|
39
|
+
count: 0,
|
|
40
|
+
sum: 0,
|
|
41
|
+
min: null,
|
|
42
|
+
max: null
|
|
43
|
+
});
|
|
44
|
+
const addStatistics = (target, other) => {
|
|
45
|
+
const total = target.sum + other.sum;
|
|
46
|
+
if (!Number.isFinite(total)) throw new TypeError("Aggregation values must be finite");
|
|
47
|
+
target.count += other.count;
|
|
48
|
+
target.sum = total;
|
|
49
|
+
if (other.min !== null) target.min = target.min === null ? other.min : Math.min(target.min, other.min);
|
|
50
|
+
if (other.max !== null) target.max = target.max === null ? other.max : Math.max(target.max, other.max);
|
|
51
|
+
};
|
|
52
|
+
const quantity = (statistics, func) => {
|
|
53
|
+
switch (func) {
|
|
54
|
+
case "count": return statistics.count;
|
|
55
|
+
case "sum": return statistics.sum;
|
|
56
|
+
case "min": return statistics.min ?? 0;
|
|
57
|
+
case "max": return statistics.max ?? 0;
|
|
58
|
+
case "avg": return statistics.count ? statistics.sum / statistics.count : 0;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const BUCKET_SIZE = 3e8;
|
|
62
|
+
const getBucketStart = (timestamp) => {
|
|
63
|
+
if (!Number.isSafeInteger(timestamp)) throw new TypeError("Invalid bucket timestamp");
|
|
64
|
+
return Math.floor(timestamp / BUCKET_SIZE) * BUCKET_SIZE;
|
|
65
|
+
};
|
|
66
|
+
const getMatcher = (filter) => {
|
|
67
|
+
if (!("property" in filter)) {
|
|
68
|
+
const children = filter.clauses.map(getMatcher);
|
|
69
|
+
return filter.conjunction === "and" ? (event) => children.every((match) => match(event)) : (event) => children.some((match) => match(event));
|
|
70
|
+
}
|
|
71
|
+
return (event) => {
|
|
72
|
+
const actual = getProperty(event, filter.property);
|
|
73
|
+
if (actual === void 0 || actual === null) return false;
|
|
74
|
+
if (filter.operator === "eq" || filter.operator === "ne") {
|
|
75
|
+
const actualValue = typeof actual === "boolean" ? Number(actual) : actual;
|
|
76
|
+
const expectedValue = typeof filter.value === "boolean" ? Number(filter.value) : filter.value;
|
|
77
|
+
return filter.operator === "eq" ? actualValue === expectedValue : actualValue !== expectedValue;
|
|
78
|
+
}
|
|
79
|
+
if (typeof actual !== typeof filter.value) return false;
|
|
80
|
+
if (typeof actual !== "number" && typeof actual !== "string" || typeof filter.value !== "number" && typeof filter.value !== "string") return false;
|
|
81
|
+
switch (filter.operator) {
|
|
82
|
+
case "gt": return actual > filter.value;
|
|
83
|
+
case "gte": return actual >= filter.value;
|
|
84
|
+
case "lt": return actual < filter.value;
|
|
85
|
+
case "lte": return actual <= filter.value;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
const reduce = (reducers, events, oldestBucketStart) => {
|
|
90
|
+
const updates = /* @__PURE__ */ new Map();
|
|
91
|
+
const matchers = reducers.map((reducer) => [reducer, getMatcher(reducer.filter)]);
|
|
92
|
+
for (const event of events) {
|
|
93
|
+
const bucketStart = getBucketStart(event.timestamp);
|
|
94
|
+
if (bucketStart < oldestBucketStart) continue;
|
|
95
|
+
for (const [reducer, match] of matchers) {
|
|
96
|
+
if (!match(event)) continue;
|
|
97
|
+
const { aggregation } = reducer;
|
|
98
|
+
const value = aggregation.func === "count" ? 1 : getProperty(event, aggregation.property);
|
|
99
|
+
if (typeof value !== "number") continue;
|
|
100
|
+
if (!Number.isFinite(value)) throw new TypeError("Aggregation values must be finite");
|
|
101
|
+
const key = JSON.stringify([
|
|
102
|
+
event.customer,
|
|
103
|
+
reducer.id,
|
|
104
|
+
bucketStart
|
|
105
|
+
]);
|
|
106
|
+
let update = updates.get(key);
|
|
107
|
+
if (!update) {
|
|
108
|
+
update = {
|
|
109
|
+
customer: event.customer,
|
|
110
|
+
reducer: reducer.id,
|
|
111
|
+
bucketStart,
|
|
112
|
+
statistics: emptyStatistics()
|
|
113
|
+
};
|
|
114
|
+
updates.set(key, update);
|
|
115
|
+
}
|
|
116
|
+
addStatistics(update.statistics, aggregation.func === "count" ? {
|
|
117
|
+
count: 1,
|
|
118
|
+
sum: 0,
|
|
119
|
+
min: null,
|
|
120
|
+
max: null
|
|
121
|
+
} : {
|
|
122
|
+
count: 1,
|
|
123
|
+
sum: value,
|
|
124
|
+
min: value,
|
|
125
|
+
max: value
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return [...updates.values()];
|
|
130
|
+
};
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/outpost/scripts.ts
|
|
133
|
+
const READ_STATE_SCRIPT = `
|
|
134
|
+
if not redis.call('HGET', KEYS[1], 'epoch') then
|
|
135
|
+
redis.call('HSET', KEYS[1], 'epoch', ARGV[1], 'revision', '0')
|
|
136
|
+
end
|
|
137
|
+
return redis.call('HGETALL', KEYS[1])
|
|
138
|
+
`;
|
|
139
|
+
const WRITE_BUCKET_SCRIPT = `
|
|
140
|
+
local cutoff = redis.call('HGET', KEYS[1], 'cold_until')
|
|
141
|
+
if cutoff and tonumber(ARGV[4]) < tonumber(cutoff) then
|
|
142
|
+
return redis.call('HGETALL', KEYS[1])
|
|
143
|
+
end
|
|
144
|
+
local prefix = 'bucket:' .. ARGV[4] .. ':'
|
|
145
|
+
local total = tonumber(redis.call('HGET', KEYS[1], prefix .. 'sum') or '0') + tonumber(ARGV[6])
|
|
146
|
+
assert(total == total and math.abs(total) ~= math.huge, 'Aggregation values must be finite')
|
|
147
|
+
redis.call('HINCRBYFLOAT', KEYS[1], prefix .. 'sum', ARGV[6])
|
|
148
|
+
redis.call('HINCRBY', KEYS[1], prefix .. 'count', ARGV[5])
|
|
149
|
+
for i, func in ipairs({'min', 'max'}) do
|
|
150
|
+
local raw = ARGV[6 + i]
|
|
151
|
+
if raw ~= '' then
|
|
152
|
+
local value = tonumber(raw)
|
|
153
|
+
assert(value and value == value and math.abs(value) ~= math.huge, 'Invalid value')
|
|
154
|
+
local stored = redis.call('HGET', KEYS[1], prefix .. func)
|
|
155
|
+
local previous = stored and tonumber(stored)
|
|
156
|
+
if not previous or (func == 'min' and value < previous)
|
|
157
|
+
or (func == 'max' and value > previous) then
|
|
158
|
+
redis.call('HSET', KEYS[1], prefix .. func, raw)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
if not redis.call('HGET', KEYS[1], 'epoch') then
|
|
164
|
+
redis.call('HSET', KEYS[1], 'epoch', ARGV[3], 'revision', '0')
|
|
165
|
+
end
|
|
166
|
+
redis.call('HINCRBY', KEYS[1], 'revision', 1)
|
|
167
|
+
local state = redis.call('HGETALL', KEYS[1])
|
|
168
|
+
redis.call('PUBLISH', ARGV[2], cjson.encode({customer = ARGV[1], state = state}))
|
|
169
|
+
return state
|
|
170
|
+
`;
|
|
171
|
+
const APPLY_SNAPSHOT_SCRIPT = `
|
|
172
|
+
local cutoff = tonumber(ARGV[4])
|
|
173
|
+
local current = redis.call('HGET', KEYS[1], 'cold_until')
|
|
174
|
+
if current and tonumber(current) > cutoff then
|
|
175
|
+
return redis.call('HGETALL', KEYS[1])
|
|
176
|
+
end
|
|
177
|
+
local version = tonumber(ARGV[5])
|
|
178
|
+
local previous = redis.call('HGET', KEYS[1], 'snapshot_version')
|
|
179
|
+
if not previous or version >= tonumber(previous) then
|
|
180
|
+
for _, field in ipairs(redis.call('HKEYS', KEYS[1])) do
|
|
181
|
+
if string.sub(field, 1, 5) == 'cold:'
|
|
182
|
+
or (string.sub(field, 1, 7) == 'bucket:'
|
|
183
|
+
and tonumber(string.match(field, '^bucket:(%-?%d+):')) < cutoff) then
|
|
184
|
+
redis.call('HDEL', KEYS[1], field)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
redis.call('HSET', KEYS[1], 'cold_until', ARGV[4], 'snapshot_version', ARGV[5],
|
|
188
|
+
'cold:count', ARGV[8], 'cold:sum', ARGV[9])
|
|
189
|
+
if ARGV[10] ~= '' then redis.call('HSET', KEYS[1], 'cold:min', ARGV[10]) end
|
|
190
|
+
if ARGV[11] ~= '' then redis.call('HSET', KEYS[1], 'cold:max', ARGV[11]) end
|
|
191
|
+
end
|
|
192
|
+
local credit_version = redis.call('HGET', KEYS[1], 'credited_version:' .. ARGV[6])
|
|
193
|
+
if not credit_version or version >= tonumber(credit_version) then
|
|
194
|
+
redis.call('HSET', KEYS[1], 'credited:' .. ARGV[6], ARGV[7],
|
|
195
|
+
'credited_version:' .. ARGV[6], ARGV[5])
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
if not redis.call('HGET', KEYS[1], 'epoch') then
|
|
199
|
+
redis.call('HSET', KEYS[1], 'epoch', ARGV[3], 'revision', '0')
|
|
200
|
+
end
|
|
201
|
+
redis.call('HINCRBY', KEYS[1], 'revision', 1)
|
|
202
|
+
local state = redis.call('HGETALL', KEYS[1])
|
|
203
|
+
redis.call('PUBLISH', ARGV[2], cjson.encode({customer = ARGV[1], state = state}))
|
|
204
|
+
return state
|
|
205
|
+
`;
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/outpost/client.ts
|
|
208
|
+
const CACHE_SIZE = 1024;
|
|
209
|
+
const SYNC_INTERVAL = 3e4;
|
|
210
|
+
const TIMEOUT = 5e3;
|
|
211
|
+
const generateEpoch = () => randomUUID().replaceAll("-", "");
|
|
212
|
+
const number = (value) => {
|
|
213
|
+
const parsed = JSON.parse(value);
|
|
214
|
+
if (!Number.isFinite(parsed)) throw new TypeError("Stored aggregation values must be finite numbers");
|
|
215
|
+
return parsed;
|
|
216
|
+
};
|
|
217
|
+
const signal = () => {
|
|
218
|
+
let resolve;
|
|
219
|
+
return {
|
|
220
|
+
promise: new Promise((done) => {
|
|
221
|
+
resolve = done;
|
|
222
|
+
}),
|
|
223
|
+
resolve
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
const withTimeout = async (promise) => {
|
|
227
|
+
let timer;
|
|
228
|
+
try {
|
|
229
|
+
return await Promise.race([promise, new Promise((_, reject) => {
|
|
230
|
+
timer = setTimeout(() => reject(new PolarError("Outpost operation timed out")), TIMEOUT);
|
|
231
|
+
})]);
|
|
232
|
+
} finally {
|
|
233
|
+
clearTimeout(timer);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const locked = (local, action) => {
|
|
237
|
+
const result = local.pending.then(action);
|
|
238
|
+
local.pending = result.catch(() => {});
|
|
239
|
+
return result;
|
|
240
|
+
};
|
|
241
|
+
const decodeState = (value) => {
|
|
242
|
+
const fields = {};
|
|
243
|
+
for (let i = 0; i < value.length; i += 2) fields[value[i]] = value[i + 1];
|
|
244
|
+
const state = {
|
|
245
|
+
epoch: fields.epoch,
|
|
246
|
+
revision: BigInt(fields.revision),
|
|
247
|
+
coldUntil: fields.cold_until === void 0 ? void 0 : number(fields.cold_until),
|
|
248
|
+
cold: emptyStatistics(),
|
|
249
|
+
buckets: /* @__PURE__ */ new Map(),
|
|
250
|
+
credited: {}
|
|
251
|
+
};
|
|
252
|
+
for (const [field, value] of Object.entries(fields)) if (field.startsWith("cold:")) {
|
|
253
|
+
const statistic = field.slice(5);
|
|
254
|
+
state.cold[statistic] = number(value);
|
|
255
|
+
} else if (field.startsWith("bucket:")) {
|
|
256
|
+
const [, timestamp, statistic] = field.split(":");
|
|
257
|
+
const start = Number(timestamp);
|
|
258
|
+
const bucket = state.buckets.get(start) ?? emptyStatistics();
|
|
259
|
+
bucket[statistic] = number(value);
|
|
260
|
+
state.buckets.set(start, bucket);
|
|
261
|
+
} else if (field.startsWith("credited:")) state.credited[field.slice(9)] = number(value);
|
|
262
|
+
return state;
|
|
263
|
+
};
|
|
264
|
+
const customerArguments = (customer) => {
|
|
265
|
+
const separator = customer.indexOf(":");
|
|
266
|
+
return { [customer.startsWith("customer:") ? "customer_id" : "external_customer_id"]: customer.slice(separator + 1) };
|
|
267
|
+
};
|
|
268
|
+
var Outpost = class {
|
|
269
|
+
redisUrl;
|
|
270
|
+
options;
|
|
271
|
+
redis;
|
|
272
|
+
subscriber;
|
|
273
|
+
websocket;
|
|
274
|
+
reducers = [];
|
|
275
|
+
cache = /* @__PURE__ */ new Map();
|
|
276
|
+
actors = /* @__PURE__ */ new Set();
|
|
277
|
+
configured = signal();
|
|
278
|
+
synced = signal();
|
|
279
|
+
isSynced = false;
|
|
280
|
+
generation = 0;
|
|
281
|
+
channel = "";
|
|
282
|
+
refreshTimer;
|
|
283
|
+
failure;
|
|
284
|
+
messages = Promise.resolve();
|
|
285
|
+
synchronizing;
|
|
286
|
+
constructor(redisUrl, options) {
|
|
287
|
+
this.redisUrl = redisUrl;
|
|
288
|
+
this.options = options;
|
|
289
|
+
}
|
|
290
|
+
async start() {
|
|
291
|
+
if (this.redis) throw new PolarError("Outpost is already connected");
|
|
292
|
+
this.failure = void 0;
|
|
293
|
+
this.configured = signal();
|
|
294
|
+
this.synced = signal();
|
|
295
|
+
this.messages = Promise.resolve();
|
|
296
|
+
try {
|
|
297
|
+
this.redis = new Redis(this.redisUrl, {
|
|
298
|
+
lazyConnect: true,
|
|
299
|
+
enableOfflineQueue: false,
|
|
300
|
+
autoResendUnfulfilledCommands: false,
|
|
301
|
+
maxRetriesPerRequest: 0,
|
|
302
|
+
commandTimeout: TIMEOUT
|
|
303
|
+
});
|
|
304
|
+
this.redis.defineCommand("outpostReadState", {
|
|
305
|
+
numberOfKeys: 1,
|
|
306
|
+
lua: READ_STATE_SCRIPT
|
|
307
|
+
});
|
|
308
|
+
this.redis.defineCommand("outpostWriteBucket", {
|
|
309
|
+
numberOfKeys: 1,
|
|
310
|
+
lua: WRITE_BUCKET_SCRIPT
|
|
311
|
+
});
|
|
312
|
+
this.redis.defineCommand("outpostApplySnapshot", {
|
|
313
|
+
numberOfKeys: 1,
|
|
314
|
+
lua: APPLY_SNAPSHOT_SCRIPT
|
|
315
|
+
});
|
|
316
|
+
const redis = this.redis;
|
|
317
|
+
this.redis.on("error", () => {});
|
|
318
|
+
this.redis.on("close", () => {
|
|
319
|
+
if (this.redis === redis) this.invalidate();
|
|
320
|
+
});
|
|
321
|
+
this.redis.on("ready", () => {
|
|
322
|
+
if (this.subscriber?.status === "ready") this.synchronize();
|
|
323
|
+
});
|
|
324
|
+
await withTimeout(this.redis.connect());
|
|
325
|
+
await this.redis.ping();
|
|
326
|
+
this.channel = `outpost:${this.redis.options.db}:updates`;
|
|
327
|
+
this.subscriber = this.redis.duplicate({
|
|
328
|
+
lazyConnect: true,
|
|
329
|
+
autoResubscribe: false
|
|
330
|
+
});
|
|
331
|
+
const subscriber = this.subscriber;
|
|
332
|
+
this.subscriber.on("error", () => {});
|
|
333
|
+
this.subscriber.on("close", () => {
|
|
334
|
+
if (this.subscriber === subscriber) this.invalidate();
|
|
335
|
+
});
|
|
336
|
+
this.subscriber.on("ready", () => {
|
|
337
|
+
this.synchronize();
|
|
338
|
+
});
|
|
339
|
+
this.subscriber.on("message", (_channel, data) => {
|
|
340
|
+
this.receiveRedis(data).catch((error) => this.fail(error));
|
|
341
|
+
});
|
|
342
|
+
await withTimeout(this.subscriber.connect());
|
|
343
|
+
await withTimeout(this.synced.promise);
|
|
344
|
+
const [url, request] = this.options.client.buildRequest("GET", "/v1/outpost/");
|
|
345
|
+
const endpoint = new URL(url);
|
|
346
|
+
endpoint.protocol = endpoint.protocol === "https:" ? "wss:" : "ws:";
|
|
347
|
+
this.websocket = new WebSocket(endpoint, {
|
|
348
|
+
headers: Object.fromEntries(new Headers(request.headers)),
|
|
349
|
+
handshakeTimeout: TIMEOUT
|
|
350
|
+
});
|
|
351
|
+
this.websocket.on("error", (error) => this.fail(error));
|
|
352
|
+
this.websocket.on("close", () => {
|
|
353
|
+
if (this.websocket) this.fail(new PolarError("Outpost WebSocket disconnected"));
|
|
354
|
+
});
|
|
355
|
+
this.websocket.on("message", (data) => {
|
|
356
|
+
this.messages = this.messages.then(() => this.receiveWebsocket(data.toString())).catch((error) => this.fail(error));
|
|
357
|
+
});
|
|
358
|
+
await withTimeout(once(this.websocket, "open"));
|
|
359
|
+
await this.send({ type: "configuration" });
|
|
360
|
+
this.refreshTimer = setInterval(() => {
|
|
361
|
+
if (this.synchronizing) return;
|
|
362
|
+
this.synchronize();
|
|
363
|
+
}, SYNC_INTERVAL);
|
|
364
|
+
this.requireRedis();
|
|
365
|
+
return this;
|
|
366
|
+
} catch (error) {
|
|
367
|
+
await this.close();
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
async close() {
|
|
372
|
+
clearInterval(this.refreshTimer);
|
|
373
|
+
const websocket = this.websocket;
|
|
374
|
+
const connections = [this.subscriber, this.redis].filter((redis) => redis !== void 0);
|
|
375
|
+
this.websocket = void 0;
|
|
376
|
+
this.subscriber = void 0;
|
|
377
|
+
this.redis = void 0;
|
|
378
|
+
const closed = connections.map((redis) => {
|
|
379
|
+
const ended = redis.status === "end" ? Promise.resolve() : once(redis, "end");
|
|
380
|
+
redis.disconnect();
|
|
381
|
+
return ended;
|
|
382
|
+
});
|
|
383
|
+
if (websocket && websocket.readyState !== WebSocket.CLOSED) {
|
|
384
|
+
closed.push(once(websocket, "close"));
|
|
385
|
+
websocket.terminate();
|
|
386
|
+
}
|
|
387
|
+
this.invalidate();
|
|
388
|
+
this.synced.resolve();
|
|
389
|
+
this.configured.resolve();
|
|
390
|
+
await Promise.allSettled([
|
|
391
|
+
...closed,
|
|
392
|
+
this.messages,
|
|
393
|
+
this.synchronizing
|
|
394
|
+
]);
|
|
395
|
+
this.cache.clear();
|
|
396
|
+
this.actors.clear();
|
|
397
|
+
this.reducers = [];
|
|
398
|
+
}
|
|
399
|
+
async [Symbol.asyncDispose]() {
|
|
400
|
+
await this.close();
|
|
401
|
+
}
|
|
402
|
+
async ingest({ events }) {
|
|
403
|
+
const redis = this.requireRedis();
|
|
404
|
+
if (!events.length) return {
|
|
405
|
+
inserted: 0,
|
|
406
|
+
duplicates: 0
|
|
407
|
+
};
|
|
408
|
+
const parsed = events.map(parseEvent);
|
|
409
|
+
await this.waitConfigured();
|
|
410
|
+
const updates = reduce(this.reducers, parsed, parsed.reduce((oldest, event) => Math.min(oldest, getBucketStart(event.timestamp)), Infinity));
|
|
411
|
+
for (const customer of new Set(parsed.map((event) => event.customer))) await this.actor(customerArguments(customer));
|
|
412
|
+
for (const update of updates) {
|
|
413
|
+
const identity = `${update.customer}:${update.reducer}`;
|
|
414
|
+
const statistics = update.statistics;
|
|
415
|
+
const state = await redis.outpostWriteBucket(`outpost:state:${identity}`, identity, this.channel, generateEpoch(), update.bucketStart, statistics.count, statistics.sum, statistics.min ?? "", statistics.max ?? "");
|
|
416
|
+
await this.receiveState(identity, decodeState(state));
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
inserted: parsed.length,
|
|
420
|
+
duplicates: 0
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
async customerMeter(input) {
|
|
424
|
+
const customer = customerKey(input);
|
|
425
|
+
this.requireRedis();
|
|
426
|
+
await this.waitConfigured();
|
|
427
|
+
const reducer = this.reducers.find((item) => item.meter_ids?.includes(input.meter_id));
|
|
428
|
+
if (!reducer) return null;
|
|
429
|
+
const state = (await this.warmCustomer(customer, reducer)).state;
|
|
430
|
+
if (!state || state.coldUntil === void 0) throw new PolarError("Customer snapshot is not ready");
|
|
431
|
+
const combined = { ...state.cold };
|
|
432
|
+
for (const [start, statistics] of state.buckets) if (start >= state.coldUntil) addStatistics(combined, statistics);
|
|
433
|
+
const consumed = quantity(combined, reducer.aggregation.func);
|
|
434
|
+
const credited = state.credited[input.meter_id] ?? 0;
|
|
435
|
+
return {
|
|
436
|
+
...input,
|
|
437
|
+
consumed_units: consumed,
|
|
438
|
+
credited_units: credited,
|
|
439
|
+
balance: credited - consumed
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
async actor(input) {
|
|
443
|
+
await this.waitConfigured();
|
|
444
|
+
for (const reducer of this.reducers) await this.warmCustomer(customerKey(input), reducer);
|
|
445
|
+
}
|
|
446
|
+
requireRedis() {
|
|
447
|
+
if (this.failure) throw this.failure;
|
|
448
|
+
if (!this.redis) throw new PolarError("Call Outpost.start() before using Outpost");
|
|
449
|
+
return this.redis;
|
|
450
|
+
}
|
|
451
|
+
async waitConfigured() {
|
|
452
|
+
await withTimeout(this.configured.promise);
|
|
453
|
+
this.requireRedis();
|
|
454
|
+
}
|
|
455
|
+
async send(message) {
|
|
456
|
+
this.requireRedis();
|
|
457
|
+
if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) throw new PolarError("Outpost WebSocket is not connected");
|
|
458
|
+
await new Promise((resolve, reject) => this.websocket.send(JSON.stringify(message), (error) => error ? reject(error) : resolve()));
|
|
459
|
+
}
|
|
460
|
+
async warmCustomer(customer, reducer) {
|
|
461
|
+
const identity = `${customer}:${reducer.id}`;
|
|
462
|
+
this.requireRedis();
|
|
463
|
+
while (!this.isSynced) {
|
|
464
|
+
await withTimeout(this.synced.promise);
|
|
465
|
+
this.requireRedis();
|
|
466
|
+
}
|
|
467
|
+
let local = this.cache.get(identity);
|
|
468
|
+
if (!local) {
|
|
469
|
+
local = { pending: Promise.resolve() };
|
|
470
|
+
this.cache.set(identity, local);
|
|
471
|
+
if (this.cache.size > CACHE_SIZE) {
|
|
472
|
+
const evicted = this.cache.keys().next().value;
|
|
473
|
+
this.cache.delete(evicted);
|
|
474
|
+
this.actors.delete(evicted);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
this.cache.delete(identity);
|
|
478
|
+
this.cache.set(identity, local);
|
|
479
|
+
if (local.state && this.actors.has(identity)) return local;
|
|
480
|
+
const entry = local;
|
|
481
|
+
await locked(entry, async () => {
|
|
482
|
+
if (!entry.state) entry.state = await this.loadState(identity);
|
|
483
|
+
if (!this.actors.has(identity)) {
|
|
484
|
+
for (const meter_id of reducer.meter_ids ?? []) await this.send({
|
|
485
|
+
type: "meter_usage",
|
|
486
|
+
payload: {
|
|
487
|
+
...customerArguments(customer),
|
|
488
|
+
meter_id
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
this.actors.add(identity);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
return entry;
|
|
495
|
+
}
|
|
496
|
+
async loadState(customer) {
|
|
497
|
+
const epoch = generateEpoch();
|
|
498
|
+
return decodeState(await this.requireRedis().outpostReadState(`outpost:state:${customer}`, epoch));
|
|
499
|
+
}
|
|
500
|
+
async receiveState(customer, state) {
|
|
501
|
+
const local = this.cache.get(customer);
|
|
502
|
+
if (!local) return;
|
|
503
|
+
await locked(local, async () => {
|
|
504
|
+
const current = local.state;
|
|
505
|
+
if (!current) return;
|
|
506
|
+
if (current.epoch !== state.epoch) state = await this.loadState(customer);
|
|
507
|
+
else if (current.revision >= state.revision) return;
|
|
508
|
+
local.state = state;
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
async receiveRedis(data) {
|
|
512
|
+
const update = JSON.parse(data);
|
|
513
|
+
await this.receiveState(update.customer, decodeState(update.state));
|
|
514
|
+
}
|
|
515
|
+
synchronize() {
|
|
516
|
+
if (this.synchronizing) return this.synchronizing;
|
|
517
|
+
const generation = this.generation;
|
|
518
|
+
this.synchronizing = (async () => {
|
|
519
|
+
if (this.failure || !this.subscriber || this.subscriber.status !== "ready" || this.redis?.status !== "ready") return;
|
|
520
|
+
if (!this.isSynced) await this.subscriber.subscribe(this.channel);
|
|
521
|
+
const entries = Array.from(this.cache);
|
|
522
|
+
for (const [customer, local] of entries) await locked(local, async () => {
|
|
523
|
+
const state = await this.loadState(customer);
|
|
524
|
+
if (generation === this.generation) local.state = state;
|
|
525
|
+
});
|
|
526
|
+
if (generation === this.generation) {
|
|
527
|
+
this.isSynced = true;
|
|
528
|
+
this.synced.resolve();
|
|
529
|
+
}
|
|
530
|
+
})().catch((error) => {
|
|
531
|
+
if (error instanceof TypeError) this.fail(error);
|
|
532
|
+
else this.invalidate();
|
|
533
|
+
}).finally(() => {
|
|
534
|
+
this.synchronizing = void 0;
|
|
535
|
+
if (!this.failure && generation !== this.generation && this.redis?.status === "ready" && this.subscriber?.status === "ready") this.synchronize();
|
|
536
|
+
});
|
|
537
|
+
return this.synchronizing;
|
|
538
|
+
}
|
|
539
|
+
invalidate() {
|
|
540
|
+
this.generation++;
|
|
541
|
+
this.isSynced = false;
|
|
542
|
+
this.synced.resolve();
|
|
543
|
+
this.synced = signal();
|
|
544
|
+
for (const local of this.cache.values()) local.state = void 0;
|
|
545
|
+
}
|
|
546
|
+
fail(error) {
|
|
547
|
+
this.failure = error instanceof Error ? error : new PolarError(String(error));
|
|
548
|
+
this.configured.resolve();
|
|
549
|
+
this.synced.resolve();
|
|
550
|
+
}
|
|
551
|
+
async receiveWebsocket(data) {
|
|
552
|
+
const message = JSON.parse(data);
|
|
553
|
+
if (message.type === "configuration") {
|
|
554
|
+
this.reducers = message.payload.reducers;
|
|
555
|
+
this.configured.resolve();
|
|
556
|
+
} else if (message.type === "meter_usage") {
|
|
557
|
+
const snapshot = message.payload;
|
|
558
|
+
const reducer = this.reducers.find((item) => item.meter_ids?.includes(snapshot.meter_id));
|
|
559
|
+
if (!reducer) return;
|
|
560
|
+
if (snapshot.customer_id !== null && snapshot.external_customer_id !== null) return;
|
|
561
|
+
const identity = `${customerKey({
|
|
562
|
+
customer_id: snapshot.customer_id ?? void 0,
|
|
563
|
+
external_customer_id: snapshot.external_customer_id ?? void 0
|
|
564
|
+
})}:${reducer.id}`;
|
|
565
|
+
if (!Number.isSafeInteger(snapshot.version) || !Number.isSafeInteger(snapshot.cold_until)) throw new TypeError("Snapshot timestamps must be safe integer microseconds");
|
|
566
|
+
for (const value of [
|
|
567
|
+
snapshot.count,
|
|
568
|
+
snapshot.sum,
|
|
569
|
+
snapshot.min,
|
|
570
|
+
snapshot.max,
|
|
571
|
+
snapshot.credited_units
|
|
572
|
+
]) if (value !== null && !Number.isFinite(value)) throw new TypeError("Snapshot values must be finite");
|
|
573
|
+
const state = await this.requireRedis().outpostApplySnapshot(`outpost:state:${identity}`, identity, this.channel, generateEpoch(), snapshot.cold_until, snapshot.version, snapshot.meter_id, snapshot.credited_units, snapshot.count, snapshot.sum, snapshot.min ?? "", snapshot.max ?? "");
|
|
574
|
+
await this.receiveState(identity, decodeState(state));
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
//#endregion
|
|
579
|
+
export { Outpost, getBucketStart, getMatcher, reduce };
|
|
580
|
+
|
|
581
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/outpost/event.ts","../../src/outpost/reducer.ts","../../src/outpost/scripts.ts","../../src/outpost/client.ts"],"sourcesContent":["import type {\n EventCreateCustomer as EventCreateCustomer2026_04,\n EventCreateExternalCustomer as EventCreateExternalCustomer2026_04,\n} from \"../2026-04/models\";\nimport type {\n EventCreateCustomer as EventCreateCustomer2026_10,\n EventCreateExternalCustomer as EventCreateExternalCustomer2026_10,\n} from \"../2026-10/models\";\nimport type {\n EventCreateCustomer as EventCreateCustomer2027_01,\n EventCreateExternalCustomer as EventCreateExternalCustomer2027_01,\n} from \"../2027-01/models\";\n\nexport type EventInput =\n | EventCreateCustomer2026_04\n | EventCreateExternalCustomer2026_04\n | EventCreateCustomer2026_10\n | EventCreateExternalCustomer2026_10\n | EventCreateCustomer2027_01\n | EventCreateExternalCustomer2027_01;\nexport interface Event {\n name: string;\n customer: string;\n timestamp: number;\n metadata: Record<string, unknown>;\n}\n\nexport const customerKey = (input: {\n customer_id?: string;\n external_customer_id?: string;\n}): string => {\n if ((input.customer_id === undefined) === (input.external_customer_id === undefined)) {\n throw new TypeError(\"Provide exactly one customer_id or external_customer_id\");\n }\n const identifier = input.customer_id ?? input.external_customer_id;\n return `${input.customer_id !== undefined ? \"customer\" : \"external\"}:${identifier}`;\n};\n\nexport const parseEvent = (input: EventInput): Event => {\n if (input.timestamp !== undefined && !/(Z|[+-]\\d{2}:?\\d{2})$/i.test(input.timestamp)) {\n throw new TypeError(\"Event timestamps must include a timezone\");\n }\n const timestamp = input.timestamp === undefined ? Date.now() : Date.parse(input.timestamp);\n const fraction = input.timestamp?.match(/\\.(\\d+)/)?.[1] ?? \"\";\n const microseconds = timestamp * 1000 + Number(fraction.padEnd(6, \"0\").slice(3, 6));\n if (!Number.isSafeInteger(microseconds)) throw new TypeError(\"Invalid event timestamp\");\n return {\n name: input.name,\n customer: customerKey(input),\n timestamp: microseconds,\n metadata: input.metadata ?? {},\n };\n};\n\nexport const getProperty = (event: Event, property: string): unknown => {\n if (property === \"name\") return event.name;\n if (property === \"source\") return \"user\";\n if (property === \"timestamp\") return Math.trunc(event.timestamp / 1_000_000);\n let value: unknown = event.metadata;\n for (const part of property.replace(/^metadata\\./, \"\").split(\".\")) {\n if (\n value === null ||\n typeof value !== \"object\" ||\n Array.isArray(value) ||\n !Object.hasOwn(value, part)\n )\n return undefined;\n value = (value as Record<string, unknown>)[part];\n }\n return value;\n};\n","import { getProperty, type Event } from \"./event\";\n\nexport interface FilterClause {\n property: string;\n operator: \"eq\" | \"ne\" | \"gt\" | \"gte\" | \"lt\" | \"lte\";\n value: string | number | boolean;\n}\nexport interface Filter {\n conjunction: \"and\" | \"or\";\n clauses: (Filter | FilterClause)[];\n}\nexport type Aggregation =\n | { func: \"count\" }\n | { func: \"sum\" | \"min\" | \"max\" | \"avg\"; property: string };\nexport interface Reducer {\n id: string;\n filter: Filter;\n aggregation: Aggregation;\n meter_ids?: string[];\n}\nexport interface BucketStatistics {\n count: number;\n sum: number;\n min: number | null;\n max: number | null;\n}\nexport const emptyStatistics = (): BucketStatistics => ({ count: 0, sum: 0, min: null, max: null });\nexport const addStatistics = (target: BucketStatistics, other: BucketStatistics): void => {\n const total = target.sum + other.sum;\n if (!Number.isFinite(total)) throw new TypeError(\"Aggregation values must be finite\");\n target.count += other.count;\n target.sum = total;\n if (other.min !== null)\n target.min = target.min === null ? other.min : Math.min(target.min, other.min);\n if (other.max !== null)\n target.max = target.max === null ? other.max : Math.max(target.max, other.max);\n};\nexport const quantity = (statistics: BucketStatistics, func: Aggregation[\"func\"]): number => {\n switch (func) {\n case \"count\":\n return statistics.count;\n case \"sum\":\n return statistics.sum;\n case \"min\":\n return statistics.min ?? 0;\n case \"max\":\n return statistics.max ?? 0;\n case \"avg\":\n return statistics.count ? statistics.sum / statistics.count : 0;\n }\n};\nexport interface BucketUpdate {\n customer: string;\n reducer: string;\n bucketStart: number;\n statistics: BucketStatistics;\n}\nexport const BUCKET_SIZE = 300_000_000;\n\nexport const getBucketStart = (timestamp: number): number => {\n if (!Number.isSafeInteger(timestamp)) throw new TypeError(\"Invalid bucket timestamp\");\n return Math.floor(timestamp / BUCKET_SIZE) * BUCKET_SIZE;\n};\n\nexport const getMatcher = (filter: Filter | FilterClause): ((event: Event) => boolean) => {\n if (!(\"property\" in filter)) {\n const children = filter.clauses.map(getMatcher);\n return filter.conjunction === \"and\"\n ? (event) => children.every((match) => match(event))\n : (event) => children.some((match) => match(event));\n }\n return (event) => {\n const actual = getProperty(event, filter.property);\n if (actual === undefined || actual === null) return false;\n if (filter.operator === \"eq\" || filter.operator === \"ne\") {\n const actualValue = typeof actual === \"boolean\" ? Number(actual) : actual;\n const expectedValue = typeof filter.value === \"boolean\" ? Number(filter.value) : filter.value;\n return filter.operator === \"eq\"\n ? actualValue === expectedValue\n : actualValue !== expectedValue;\n }\n if (typeof actual !== typeof filter.value) return false;\n if (\n (typeof actual !== \"number\" && typeof actual !== \"string\") ||\n (typeof filter.value !== \"number\" && typeof filter.value !== \"string\")\n )\n return false;\n switch (filter.operator) {\n case \"gt\":\n return actual > filter.value;\n case \"gte\":\n return actual >= filter.value;\n case \"lt\":\n return actual < filter.value;\n case \"lte\":\n return actual <= filter.value;\n }\n };\n};\n\nexport const reduce = (\n reducers: readonly Reducer[],\n events: readonly Event[],\n oldestBucketStart: number,\n): BucketUpdate[] => {\n const updates = new Map<string, BucketUpdate>();\n const matchers = reducers.map((reducer) => [reducer, getMatcher(reducer.filter)] as const);\n for (const event of events) {\n const bucketStart = getBucketStart(event.timestamp);\n if (bucketStart < oldestBucketStart) continue;\n for (const [reducer, match] of matchers) {\n if (!match(event)) continue;\n const { aggregation } = reducer;\n const value = aggregation.func === \"count\" ? 1 : getProperty(event, aggregation.property);\n if (typeof value !== \"number\") continue;\n if (!Number.isFinite(value)) throw new TypeError(\"Aggregation values must be finite\");\n const key = JSON.stringify([event.customer, reducer.id, bucketStart]);\n let update = updates.get(key);\n if (!update) {\n update = {\n customer: event.customer,\n reducer: reducer.id,\n bucketStart,\n statistics: emptyStatistics(),\n };\n updates.set(key, update);\n }\n addStatistics(\n update.statistics,\n aggregation.func === \"count\"\n ? { count: 1, sum: 0, min: null, max: null }\n : { count: 1, sum: value, min: value, max: value },\n );\n }\n }\n return [...updates.values()];\n};\n","export const READ_STATE_SCRIPT = `\nif not redis.call('HGET', KEYS[1], 'epoch') then\n redis.call('HSET', KEYS[1], 'epoch', ARGV[1], 'revision', '0')\nend\nreturn redis.call('HGETALL', KEYS[1])\n`;\n\nexport const WRITE_BUCKET_SCRIPT = `\nlocal cutoff = redis.call('HGET', KEYS[1], 'cold_until')\nif cutoff and tonumber(ARGV[4]) < tonumber(cutoff) then\n return redis.call('HGETALL', KEYS[1])\nend\nlocal prefix = 'bucket:' .. ARGV[4] .. ':'\nlocal total = tonumber(redis.call('HGET', KEYS[1], prefix .. 'sum') or '0') + tonumber(ARGV[6])\nassert(total == total and math.abs(total) ~= math.huge, 'Aggregation values must be finite')\nredis.call('HINCRBYFLOAT', KEYS[1], prefix .. 'sum', ARGV[6])\nredis.call('HINCRBY', KEYS[1], prefix .. 'count', ARGV[5])\nfor i, func in ipairs({'min', 'max'}) do\n local raw = ARGV[6 + i]\n if raw ~= '' then\n local value = tonumber(raw)\n assert(value and value == value and math.abs(value) ~= math.huge, 'Invalid value')\n local stored = redis.call('HGET', KEYS[1], prefix .. func)\n local previous = stored and tonumber(stored)\n if not previous or (func == 'min' and value < previous)\n or (func == 'max' and value > previous) then\n redis.call('HSET', KEYS[1], prefix .. func, raw)\n end\n end\nend\n\nif not redis.call('HGET', KEYS[1], 'epoch') then\n redis.call('HSET', KEYS[1], 'epoch', ARGV[3], 'revision', '0')\nend\nredis.call('HINCRBY', KEYS[1], 'revision', 1)\nlocal state = redis.call('HGETALL', KEYS[1])\nredis.call('PUBLISH', ARGV[2], cjson.encode({customer = ARGV[1], state = state}))\nreturn state\n`;\n\nexport const APPLY_SNAPSHOT_SCRIPT = `\nlocal cutoff = tonumber(ARGV[4])\nlocal current = redis.call('HGET', KEYS[1], 'cold_until')\nif current and tonumber(current) > cutoff then\n return redis.call('HGETALL', KEYS[1])\nend\nlocal version = tonumber(ARGV[5])\nlocal previous = redis.call('HGET', KEYS[1], 'snapshot_version')\nif not previous or version >= tonumber(previous) then\n for _, field in ipairs(redis.call('HKEYS', KEYS[1])) do\n if string.sub(field, 1, 5) == 'cold:'\n or (string.sub(field, 1, 7) == 'bucket:'\n and tonumber(string.match(field, '^bucket:(%-?%d+):')) < cutoff) then\n redis.call('HDEL', KEYS[1], field)\n end\n end\n redis.call('HSET', KEYS[1], 'cold_until', ARGV[4], 'snapshot_version', ARGV[5],\n 'cold:count', ARGV[8], 'cold:sum', ARGV[9])\n if ARGV[10] ~= '' then redis.call('HSET', KEYS[1], 'cold:min', ARGV[10]) end\n if ARGV[11] ~= '' then redis.call('HSET', KEYS[1], 'cold:max', ARGV[11]) end\nend\nlocal credit_version = redis.call('HGET', KEYS[1], 'credited_version:' .. ARGV[6])\nif not credit_version or version >= tonumber(credit_version) then\n redis.call('HSET', KEYS[1], 'credited:' .. ARGV[6], ARGV[7],\n 'credited_version:' .. ARGV[6], ARGV[5])\nend\n\nif not redis.call('HGET', KEYS[1], 'epoch') then\n redis.call('HSET', KEYS[1], 'epoch', ARGV[3], 'revision', '0')\nend\nredis.call('HINCRBY', KEYS[1], 'revision', 1)\nlocal state = redis.call('HGETALL', KEYS[1])\nredis.call('PUBLISH', ARGV[2], cjson.encode({customer = ARGV[1], state = state}))\nreturn state\n`;\n","import { Redis } from \"ioredis\";\nimport { randomUUID } from \"node:crypto\";\nimport { once } from \"node:events\";\nimport WebSocket from \"ws\";\n\nimport { ClientBase, PolarError } from \"../base\";\nimport { customerKey, parseEvent, type EventInput } from \"./event\";\nimport {\n reduce,\n getBucketStart,\n type Reducer,\n type BucketStatistics,\n emptyStatistics,\n addStatistics,\n quantity,\n} from \"./reducer\";\nimport { READ_STATE_SCRIPT, WRITE_BUCKET_SCRIPT, APPLY_SNAPSHOT_SCRIPT } from \"./scripts\";\n\nconst CACHE_SIZE = 1024;\nconst SYNC_INTERVAL = 30_000;\nconst TIMEOUT = 5_000;\ninterface State {\n epoch: string;\n revision: bigint;\n coldUntil?: number;\n cold: BucketStatistics;\n buckets: Map<number, BucketStatistics>;\n credited: Record<string, number>;\n}\ntype Customer = Parameters<typeof customerKey>[0];\ninterface Snapshot {\n customer_id: string | null;\n external_customer_id: string | null;\n meter_id: string;\n cold_until: number;\n version: number;\n count: number;\n sum: number;\n min: number | null;\n max: number | null;\n credited_units: number;\n}\ntype Message =\n | { type: \"configuration\"; payload: { reducers: Reducer[] } }\n | { type: \"meter_usage\"; payload: Snapshot };\ninterface StateUpdate {\n customer: string;\n state: string[];\n}\ninterface LocalState {\n state?: State;\n pending: Promise<unknown>;\n}\ninterface OutpostRedis extends Redis {\n outpostReadState(key: string, ...args: (string | number)[]): Promise<string[]>;\n outpostWriteBucket(key: string, ...args: (string | number)[]): Promise<string[]>;\n outpostApplySnapshot(key: string, ...args: (string | number)[]): Promise<string[]>;\n}\n\nconst generateEpoch = (): string => randomUUID().replaceAll(\"-\", \"\");\nconst number = (value: string): number => {\n const parsed: number = JSON.parse(value);\n if (!Number.isFinite(parsed))\n throw new TypeError(\"Stored aggregation values must be finite numbers\");\n return parsed;\n};\nconst signal = () => {\n let resolve!: () => void;\n const promise = new Promise<void>((done) => {\n resolve = done;\n });\n return { promise, resolve };\n};\nconst withTimeout = async <T>(promise: Promise<T>): Promise<T> => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new PolarError(\"Outpost operation timed out\")), TIMEOUT);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n};\nconst locked = <T>(local: LocalState, action: () => Promise<T>): Promise<T> => {\n const result = local.pending.then(action);\n local.pending = result.catch(() => {});\n return result;\n};\nconst decodeState = (value: string[]): State => {\n const fields: Record<string, string> = {};\n for (let i = 0; i < value.length; i += 2) fields[value[i]] = value[i + 1];\n const state: State = {\n epoch: fields.epoch,\n revision: BigInt(fields.revision),\n coldUntil: fields.cold_until === undefined ? undefined : number(fields.cold_until),\n cold: emptyStatistics(),\n buckets: new Map(),\n credited: {},\n };\n for (const [field, value] of Object.entries(fields)) {\n if (field.startsWith(\"cold:\")) {\n const statistic = field.slice(5) as keyof BucketStatistics;\n state.cold[statistic] = number(value);\n } else if (field.startsWith(\"bucket:\")) {\n const [, timestamp, statistic] = field.split(\":\");\n const start = Number(timestamp);\n const bucket = state.buckets.get(start) ?? emptyStatistics();\n bucket[statistic as keyof BucketStatistics] = number(value);\n state.buckets.set(start, bucket);\n } else if (field.startsWith(\"credited:\")) state.credited[field.slice(9)] = number(value);\n }\n return state;\n};\n\nconst customerArguments = (customer: string): Customer => {\n const separator = customer.indexOf(\":\");\n return {\n [customer.startsWith(\"customer:\") ? \"customer_id\" : \"external_customer_id\"]: customer.slice(\n separator + 1,\n ),\n };\n};\n\nexport interface OutpostOptions {\n client: Pick<ClientBase, \"buildRequest\">;\n}\nexport interface IngestResult {\n inserted: number;\n duplicates: number;\n}\nexport interface CustomerMeter extends Customer {\n meter_id: string;\n consumed_units: number;\n credited_units: number;\n balance: number;\n}\n\nexport class Outpost {\n private redis?: OutpostRedis;\n private subscriber?: Redis;\n private websocket?: WebSocket;\n private reducers: Reducer[] = [];\n private readonly cache = new Map<string, LocalState>();\n private readonly actors = new Set<string>();\n private configured = signal();\n private synced = signal();\n private isSynced = false;\n private generation = 0;\n private channel = \"\";\n private refreshTimer?: ReturnType<typeof setInterval>;\n private failure?: Error;\n private messages: Promise<unknown> = Promise.resolve();\n private synchronizing?: Promise<void>;\n\n constructor(\n readonly redisUrl: string,\n private readonly options: OutpostOptions,\n ) {}\n\n async start(): Promise<this> {\n if (this.redis) throw new PolarError(\"Outpost is already connected\");\n this.failure = undefined;\n this.configured = signal();\n this.synced = signal();\n this.messages = Promise.resolve();\n try {\n this.redis = new Redis(this.redisUrl, {\n lazyConnect: true,\n enableOfflineQueue: false,\n autoResendUnfulfilledCommands: false,\n maxRetriesPerRequest: 0,\n commandTimeout: TIMEOUT,\n }) as OutpostRedis;\n this.redis.defineCommand(\"outpostReadState\", { numberOfKeys: 1, lua: READ_STATE_SCRIPT });\n this.redis.defineCommand(\"outpostWriteBucket\", { numberOfKeys: 1, lua: WRITE_BUCKET_SCRIPT });\n this.redis.defineCommand(\"outpostApplySnapshot\", {\n numberOfKeys: 1,\n lua: APPLY_SNAPSHOT_SCRIPT,\n });\n const redis = this.redis;\n this.redis.on(\"error\", () => {});\n this.redis.on(\"close\", () => {\n if (this.redis === redis) this.invalidate();\n });\n this.redis.on(\"ready\", () => {\n if (this.subscriber?.status === \"ready\") void this.synchronize();\n });\n await withTimeout(this.redis.connect());\n await this.redis.ping();\n this.channel = `outpost:${this.redis.options.db}:updates`;\n this.subscriber = this.redis.duplicate({ lazyConnect: true, autoResubscribe: false });\n const subscriber = this.subscriber;\n this.subscriber.on(\"error\", () => {});\n this.subscriber.on(\"close\", () => {\n if (this.subscriber === subscriber) this.invalidate();\n });\n this.subscriber.on(\"ready\", () => {\n void this.synchronize();\n });\n this.subscriber.on(\"message\", (_channel, data) => {\n void this.receiveRedis(data).catch((error: unknown) => this.fail(error));\n });\n await withTimeout(this.subscriber.connect());\n await withTimeout(this.synced.promise);\n const [url, request] = this.options.client.buildRequest(\"GET\", \"/v1/outpost/\");\n const endpoint = new URL(url);\n endpoint.protocol = endpoint.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n this.websocket = new WebSocket(endpoint, {\n headers: Object.fromEntries(new Headers(request.headers)),\n handshakeTimeout: TIMEOUT,\n });\n this.websocket.on(\"error\", (error) => this.fail(error));\n this.websocket.on(\"close\", () => {\n if (this.websocket) this.fail(new PolarError(\"Outpost WebSocket disconnected\"));\n });\n this.websocket.on(\"message\", (data) => {\n this.messages = this.messages\n .then(() => this.receiveWebsocket(data.toString()))\n .catch((error: unknown) => this.fail(error));\n });\n await withTimeout(once(this.websocket, \"open\"));\n await this.send({ type: \"configuration\" });\n this.refreshTimer = setInterval(() => {\n if (this.synchronizing) return;\n void this.synchronize();\n }, SYNC_INTERVAL);\n this.requireRedis();\n return this;\n } catch (error) {\n await this.close();\n throw error;\n }\n }\n\n async close(): Promise<void> {\n clearInterval(this.refreshTimer);\n const websocket = this.websocket;\n const connections = [this.subscriber, this.redis].filter(\n (redis): redis is Redis => redis !== undefined,\n );\n this.websocket = undefined;\n this.subscriber = undefined;\n this.redis = undefined;\n const closed = connections.map((redis) => {\n const ended = redis.status === \"end\" ? Promise.resolve() : once(redis, \"end\");\n redis.disconnect();\n return ended;\n });\n if (websocket && websocket.readyState !== WebSocket.CLOSED) {\n closed.push(once(websocket, \"close\"));\n websocket.terminate();\n }\n this.invalidate();\n this.synced.resolve();\n this.configured.resolve();\n await Promise.allSettled([...closed, this.messages, this.synchronizing]);\n this.cache.clear();\n this.actors.clear();\n this.reducers = [];\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n\n async ingest({ events }: { events: readonly EventInput[] }): Promise<IngestResult> {\n const redis = this.requireRedis();\n if (!events.length) return { inserted: 0, duplicates: 0 };\n const parsed = events.map(parseEvent);\n await this.waitConfigured();\n const updates = reduce(\n this.reducers,\n parsed,\n parsed.reduce((oldest, event) => Math.min(oldest, getBucketStart(event.timestamp)), Infinity),\n );\n for (const customer of new Set(parsed.map((event) => event.customer)))\n await this.actor(customerArguments(customer));\n for (const update of updates) {\n const identity = `${update.customer}:${update.reducer}`;\n const statistics = update.statistics;\n const state = await redis.outpostWriteBucket(\n `outpost:state:${identity}`,\n identity,\n this.channel,\n generateEpoch(),\n update.bucketStart,\n statistics.count,\n statistics.sum,\n statistics.min ?? \"\",\n statistics.max ?? \"\",\n );\n await this.receiveState(identity, decodeState(state));\n }\n return { inserted: parsed.length, duplicates: 0 };\n }\n\n async customerMeter(input: Customer & { meter_id: string }): Promise<CustomerMeter | null> {\n const customer = customerKey(input);\n this.requireRedis();\n await this.waitConfigured();\n const reducer = this.reducers.find((item) => item.meter_ids?.includes(input.meter_id));\n if (!reducer) return null;\n const state = (await this.warmCustomer(customer, reducer)).state;\n if (!state || state.coldUntil === undefined)\n throw new PolarError(\"Customer snapshot is not ready\");\n const combined = { ...state.cold };\n for (const [start, statistics] of state.buckets)\n if (start >= state.coldUntil) addStatistics(combined, statistics);\n const consumed = quantity(combined, reducer.aggregation.func);\n const credited = state.credited[input.meter_id] ?? 0;\n return {\n ...input,\n consumed_units: consumed,\n credited_units: credited,\n balance: credited - consumed,\n };\n }\n\n async actor(input: Customer): Promise<void> {\n await this.waitConfigured();\n for (const reducer of this.reducers) await this.warmCustomer(customerKey(input), reducer);\n }\n\n private requireRedis(): OutpostRedis {\n if (this.failure) throw this.failure;\n if (!this.redis) throw new PolarError(\"Call Outpost.start() before using Outpost\");\n return this.redis;\n }\n\n private async waitConfigured(): Promise<void> {\n await withTimeout(this.configured.promise);\n this.requireRedis();\n }\n\n private async send(message: unknown): Promise<void> {\n this.requireRedis();\n if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN)\n throw new PolarError(\"Outpost WebSocket is not connected\");\n await new Promise<void>((resolve, reject) =>\n this.websocket!.send(JSON.stringify(message), (error) => (error ? reject(error) : resolve())),\n );\n }\n\n private async warmCustomer(customer: string, reducer: Reducer): Promise<LocalState> {\n const identity = `${customer}:${reducer.id}`;\n this.requireRedis();\n while (!this.isSynced) {\n await withTimeout(this.synced.promise);\n this.requireRedis();\n }\n let local = this.cache.get(identity);\n if (!local) {\n local = { pending: Promise.resolve() };\n this.cache.set(identity, local);\n if (this.cache.size > CACHE_SIZE) {\n const evicted = this.cache.keys().next().value!;\n this.cache.delete(evicted);\n this.actors.delete(evicted);\n }\n }\n this.cache.delete(identity);\n this.cache.set(identity, local);\n if (local.state && this.actors.has(identity)) return local;\n const entry = local;\n await locked(entry, async () => {\n if (!entry.state) entry.state = await this.loadState(identity);\n if (!this.actors.has(identity)) {\n for (const meter_id of reducer.meter_ids ?? [])\n await this.send({\n type: \"meter_usage\",\n payload: { ...customerArguments(customer), meter_id },\n });\n this.actors.add(identity);\n }\n });\n return entry;\n }\n\n private async loadState(customer: string): Promise<State> {\n const epoch = generateEpoch();\n return decodeState(\n await this.requireRedis().outpostReadState(`outpost:state:${customer}`, epoch),\n );\n }\n\n private async receiveState(customer: string, state: State): Promise<void> {\n const local = this.cache.get(customer);\n if (!local) return;\n await locked(local, async () => {\n const current = local.state;\n if (!current) return;\n if (current.epoch !== state.epoch) state = await this.loadState(customer);\n else if (current.revision >= state.revision) return;\n local.state = state;\n });\n }\n\n private async receiveRedis(data: string): Promise<void> {\n const update: StateUpdate = JSON.parse(data);\n await this.receiveState(update.customer, decodeState(update.state));\n }\n\n private synchronize(): Promise<void> {\n if (this.synchronizing) return this.synchronizing;\n const generation = this.generation;\n this.synchronizing = (async () => {\n if (\n this.failure ||\n !this.subscriber ||\n this.subscriber.status !== \"ready\" ||\n this.redis?.status !== \"ready\"\n )\n return;\n if (!this.isSynced) await this.subscriber.subscribe(this.channel);\n const entries = Array.from(this.cache);\n for (const [customer, local] of entries) {\n await locked(local, async () => {\n const state = await this.loadState(customer);\n if (generation === this.generation) local.state = state;\n });\n }\n if (generation === this.generation) {\n this.isSynced = true;\n this.synced.resolve();\n }\n })()\n .catch((error: unknown) => {\n if (error instanceof TypeError) this.fail(error);\n else this.invalidate();\n })\n .finally(() => {\n this.synchronizing = undefined;\n if (\n !this.failure &&\n generation !== this.generation &&\n this.redis?.status === \"ready\" &&\n this.subscriber?.status === \"ready\"\n )\n void this.synchronize();\n });\n return this.synchronizing;\n }\n\n private invalidate(): void {\n this.generation++;\n this.isSynced = false;\n this.synced.resolve();\n this.synced = signal();\n for (const local of this.cache.values()) local.state = undefined;\n }\n\n private fail(error: unknown): void {\n this.failure = error instanceof Error ? error : new PolarError(String(error));\n this.configured.resolve();\n this.synced.resolve();\n }\n\n private async receiveWebsocket(data: string): Promise<void> {\n const message: Message = JSON.parse(data);\n if (message.type === \"configuration\") {\n this.reducers = message.payload.reducers;\n this.configured.resolve();\n } else if (message.type === \"meter_usage\") {\n const snapshot = message.payload;\n const reducer = this.reducers.find((item) => item.meter_ids?.includes(snapshot.meter_id));\n if (!reducer) return;\n if (snapshot.customer_id !== null && snapshot.external_customer_id !== null) return;\n const customer = customerKey({\n customer_id: snapshot.customer_id ?? undefined,\n external_customer_id: snapshot.external_customer_id ?? undefined,\n });\n const identity = `${customer}:${reducer.id}`;\n if (!Number.isSafeInteger(snapshot.version) || !Number.isSafeInteger(snapshot.cold_until))\n throw new TypeError(\"Snapshot timestamps must be safe integer microseconds\");\n for (const value of [\n snapshot.count,\n snapshot.sum,\n snapshot.min,\n snapshot.max,\n snapshot.credited_units,\n ])\n if (value !== null && !Number.isFinite(value))\n throw new TypeError(\"Snapshot values must be finite\");\n const state = await this.requireRedis().outpostApplySnapshot(\n `outpost:state:${identity}`,\n identity,\n this.channel,\n generateEpoch(),\n snapshot.cold_until,\n snapshot.version,\n snapshot.meter_id,\n snapshot.credited_units,\n snapshot.count,\n snapshot.sum,\n snapshot.min ?? \"\",\n snapshot.max ?? \"\",\n );\n await this.receiveState(identity, decodeState(state));\n }\n }\n}\n"],"mappings":"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"}
|