@particle-academy/fancy-connector-core 0.13.0 → 0.15.0
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/CHANGELOG.md +63 -0
- package/README.md +6 -1
- package/dist/index.cjs +150 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +117 -1
- package/dist/index.d.ts +117 -1
- package/dist/index.js +142 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -916,6 +916,72 @@ type ConnectorResult<T = unknown> = {
|
|
|
916
916
|
declare function registerTransport(next: Transport | null): void;
|
|
917
917
|
declare function callConnector<T = unknown>(service: ServiceDescriptor, options: CallOptions): Promise<ConnectorResult<T>>;
|
|
918
918
|
|
|
919
|
+
/**
|
|
920
|
+
* A read that resumes from a HOST-HELD cursor, and pages.
|
|
921
|
+
*
|
|
922
|
+
* Gmail's push and Drive's channel notifications carry no changes: they say
|
|
923
|
+
* "something moved", and the follow-up read fetches what did, starting from
|
|
924
|
+
* the position the HOST stored last time (Gmail's `startHistoryId`, Drive's
|
|
925
|
+
* `pageToken`). Reading from the delivery's own position instead misses the
|
|
926
|
+
* very change that caused it. The package holds no state: the host stores the
|
|
927
|
+
* cursor, passes it in, and persists `cursor.next` when the run succeeds.
|
|
928
|
+
*
|
|
929
|
+
* The PHP twin is `CursorRead::read`; both read `fixtures/cursor-read/`.
|
|
930
|
+
*
|
|
931
|
+
* The decisions, each pinned by a case:
|
|
932
|
+
*
|
|
933
|
+
* - **`next` comes ONLY from the LAST page.** Gmail's `historyId` is the
|
|
934
|
+
* mailbox's CURRENT one on every page, and Google says to store it only "if
|
|
935
|
+
* you receive no nextPageToken". Taking it after page one would skip every
|
|
936
|
+
* page not yet read, silently.
|
|
937
|
+
* - **Too many pages is refused** (`cursor-overflow`), never a partial read
|
|
938
|
+
* with a cursor that skips the rest. The host's answer is a bounded resync.
|
|
939
|
+
* - **The provider's own "cursor out of range"** (`expiredStatus`, Gmail's
|
|
940
|
+
* 404) is `cursor-expired`; every OTHER failure is rethrown unchanged, so a
|
|
941
|
+
* 500 is never mistaken for a lapse.
|
|
942
|
+
* - **No stored cursor is `cursor-missing`**, and the provider is not called:
|
|
943
|
+
* a read with no starting point is a read from the beginning of time.
|
|
944
|
+
* - `collect` absent is `[]` (Gmail omits `history` when nothing changed); not
|
|
945
|
+
* a list is `bad-page`. The cursor and `next` are strings: history ids are
|
|
946
|
+
* uint64.
|
|
947
|
+
*/
|
|
948
|
+
declare const CURSOR_READ_REASONS: readonly ["cursor-missing", "cursor-expired", "cursor-overflow", "bad-page"];
|
|
949
|
+
type CursorReadReason = (typeof CURSOR_READ_REASONS)[number];
|
|
950
|
+
type CursorReadSpec = {
|
|
951
|
+
/** The read's parameter the stored cursor fills (Gmail: `startHistoryId`). */
|
|
952
|
+
param: string;
|
|
953
|
+
/** Where the NEXT cursor is read in the last page's data (Gmail: `historyId`). */
|
|
954
|
+
advanceFrom: string;
|
|
955
|
+
/** Paging, when the read pages (Gmail: `pageToken` / `nextPageToken`, `history`). */
|
|
956
|
+
page?: {
|
|
957
|
+
param: string;
|
|
958
|
+
from: string;
|
|
959
|
+
collect: string;
|
|
960
|
+
maxPages: number;
|
|
961
|
+
};
|
|
962
|
+
/** The status the provider answers for a cursor out of range (Gmail: 404). */
|
|
963
|
+
expiredStatus?: number;
|
|
964
|
+
};
|
|
965
|
+
type CursorReadResult = {
|
|
966
|
+
data: unknown;
|
|
967
|
+
cursor: {
|
|
968
|
+
used: string;
|
|
969
|
+
next: string | null;
|
|
970
|
+
};
|
|
971
|
+
};
|
|
972
|
+
/** Refused BY NAME; `reason` is the same string PHP's exception carries. */
|
|
973
|
+
declare class CursorReadError extends Error {
|
|
974
|
+
readonly reason: CursorReadReason;
|
|
975
|
+
/** The host's bounded resync, when the caller declared one (Gmail: `newer_than:7d`). */
|
|
976
|
+
readonly resyncQuery?: string | undefined;
|
|
977
|
+
constructor(reason: CursorReadReason, message: string,
|
|
978
|
+
/** The host's bounded resync, when the caller declared one (Gmail: `newer_than:7d`). */
|
|
979
|
+
resyncQuery?: string | undefined);
|
|
980
|
+
}
|
|
981
|
+
declare function readWithCursor(spec: CursorReadSpec, cursor: unknown, call: (params: Record<string, string>) => Promise<unknown>, options?: {
|
|
982
|
+
resyncQuery?: string;
|
|
983
|
+
}): Promise<CursorReadResult>;
|
|
984
|
+
|
|
919
985
|
/**
|
|
920
986
|
* API drift — being told the provider changed, rather than discovering it.
|
|
921
987
|
*
|
|
@@ -1982,11 +2048,27 @@ type CredentialField = {
|
|
|
1982
2048
|
/** A hint for an empty field. NEVER a real value. */
|
|
1983
2049
|
placeholder?: string;
|
|
1984
2050
|
};
|
|
2051
|
+
/** Who performs a setup step. Ships as data: a host mirrors it across JSON. */
|
|
2052
|
+
declare const SETUP_ACTORS: readonly ["person", "agent"];
|
|
2053
|
+
type SetupActor = (typeof SETUP_ACTORS)[number];
|
|
1985
2054
|
type SetupStep = {
|
|
1986
2055
|
title: string;
|
|
1987
2056
|
/** The step, and the trap in it. A step with no trap named is usually wrong. */
|
|
1988
2057
|
detail: string;
|
|
1989
2058
|
url?: string;
|
|
2059
|
+
/** Who does it. Absent means `person` (0.15.0). */
|
|
2060
|
+
actor?: SetupActor;
|
|
2061
|
+
/** What an agent is asked to do. Required when `actor` is `agent`. */
|
|
2062
|
+
prompt?: string;
|
|
2063
|
+
/** Values the person pastes somewhere, e.g. the callback URL. */
|
|
2064
|
+
copy?: {
|
|
2065
|
+
label: string;
|
|
2066
|
+
value: string;
|
|
2067
|
+
}[];
|
|
2068
|
+
/** The credential keys this step yields. */
|
|
2069
|
+
fields?: string[];
|
|
2070
|
+
/** How the person knows the step is done. */
|
|
2071
|
+
done?: string;
|
|
1990
2072
|
};
|
|
1991
2073
|
/**
|
|
1992
2074
|
* The result of a read-only credential check.
|
|
@@ -2784,6 +2866,40 @@ type PubSubPushMessage = {
|
|
|
2784
2866
|
};
|
|
2785
2867
|
declare function decodePubSubPush(raw: Uint8Array | string): PubSubPushMessage;
|
|
2786
2868
|
|
|
2869
|
+
/**
|
|
2870
|
+
* A connector's setup steps, rendered for ONE app.
|
|
2871
|
+
*
|
|
2872
|
+
* A definition is written once for every host, so the values that differ per
|
|
2873
|
+
* host — where its webhooks live, what it calls itself, which connector this
|
|
2874
|
+
* is — are tokens: `{{appUrl}}`, `{{appName}}`, `{{connector}}`, and only
|
|
2875
|
+
* those. Held to `fixtures/setup-render/cases.json`, mirrored in
|
|
2876
|
+
* `php/src/Setup.php`.
|
|
2877
|
+
*
|
|
2878
|
+
* Two refusals, both BY NAME, because each would otherwise reach a person as a
|
|
2879
|
+
* broken instruction that looks deliberate:
|
|
2880
|
+
*
|
|
2881
|
+
* - `unknown-token`: anything else shaped like `{{...}}` — a typo, a spaced
|
|
2882
|
+
* `{{ appUrl }}`, a token a definition invented. Left in, a person pastes a
|
|
2883
|
+
* literal brace into a provider console.
|
|
2884
|
+
* - `missing-context`: a token the host gave no (or an empty) value for. Left
|
|
2885
|
+
* empty, a callback URL becomes `/hooks/twilio` and points nowhere.
|
|
2886
|
+
*
|
|
2887
|
+
* `appUrl` loses ONE trailing slash first, so `{{appUrl}}/hooks` never doubles
|
|
2888
|
+
* it. A context value is inserted once and never expanded again.
|
|
2889
|
+
*/
|
|
2890
|
+
|
|
2891
|
+
declare const SETUP_TOKENS: readonly ["appUrl", "appName", "connector"];
|
|
2892
|
+
type SetupToken = (typeof SETUP_TOKENS)[number];
|
|
2893
|
+
declare const SETUP_RENDER_REASONS: readonly ["unknown-token", "missing-context"];
|
|
2894
|
+
type SetupRenderReason = (typeof SETUP_RENDER_REASONS)[number];
|
|
2895
|
+
type SetupContext = Partial<Record<SetupToken, string>>;
|
|
2896
|
+
declare class SetupRenderError extends Error {
|
|
2897
|
+
readonly reason: SetupRenderReason;
|
|
2898
|
+
constructor(reason: SetupRenderReason, message: string);
|
|
2899
|
+
}
|
|
2900
|
+
/** Render every step for one app: tokens filled, `actor` and `fields` defaulted. */
|
|
2901
|
+
declare function renderSetup(steps: readonly SetupStep[], context: SetupContext): SetupStep[];
|
|
2902
|
+
|
|
2787
2903
|
/**
|
|
2788
2904
|
* How a connector trigger learns that something happened.
|
|
2789
2905
|
*
|
|
@@ -2946,4 +3062,4 @@ oidc?: OidcExpectation): Promise<WebhookVerification>;
|
|
|
2946
3062
|
*/
|
|
2947
3063
|
declare function triggerEvent(trigger: TriggerDescriptor, connection: ResolvedConnection, injected: unknown, config: Record<string, unknown>): unknown;
|
|
2948
3064
|
|
|
2949
|
-
export { AMBIGUOUS_REFUSAL, type ApiContract, type Attempt, type Attendee, type ByteRange, CANONICAL_METRICS, CONNECTOR_API_VERSION, CONNECTOR_MODES, CREDENTIAL_SCOPES, type CallOptions, type CallResult, type Capabilities, type Catalogue, type ChainLinks, type ChainOutcome, type ChainRef, type ChallengeHandshake, type Citation, type Classified, type ConnectionCredentials, type ConnectionHost, type ConnectionSpec, type Connector, ConnectorAmbiguous, ConnectorApiMismatch, ConnectorAuthError, ConnectorConfigError, ConnectorError, type ConnectorErrorContext, type ConnectorFaker, ConnectorIdempotencyExpiredError, type ConnectorMode, ConnectorModeError, ConnectorRateLimited, type ConnectorRequest, ConnectorRequestError, type ConnectorResult, ConnectorTransient, ConnectorUnreachable, type ContractOperation, type CredentialField, type CredentialScope, DEFAULT_IDEMPOTENCY_WINDOW_SECONDS, DEFAULT_RETRY, DELIVERY_DECODE_KINDS, type DeliveryDeclaration, type DeliveryDecode, type DeliveryMechanism, type DeliveryOutcome, type DeliveryRoute, type DriftFinding, type DriftReport, EXPIRES_AT_UNITS, type ExpiresAtUnit, FAKE_EPOCH, FORM_FIELD_JSON_REASONS, type FailureKind, type FakeRequest, type FakeValues, FormFieldJsonError, type FormFieldJsonReason, type HmacScheme, type HmacVerificationSpec, type HostEnvironment, type IdempotencyOptions, type InboundDelivery, type Invite, type InviteMethod, JWT_ALGORITHMS, type JoinTarget, type JoinTargetKind, type Jwk, type JwtVerification, type LeaseAction, type LeaseDeclaration, type LeaseState, MAX_IDEMPOTENCY_KEY_LENGTH, type MetricDescriptor, type MetricSample, type MimeAttachment, type MimeHeader, type MimeMessage, type MimePart, type ModeInputs, NO_IDEMPOTENCY_KEY_WARNING, OIDC_JWT_SCHEME_KIND, type OidcExpectation, type OidcJwtScheme, type OidcJwtVerificationSpec, PROBLEM_SEVERITIES, PUBSUB_PUSH_REASONS, type PreparedRequest, type ProbeOutcome, type ProbeReport, type ProbeResult, type ProbeSpec, type Problem, type ProviderAdapter, PubSubPushError, type PubSubPushMessage, type PubSubPushReason, RENDERER_VERSION, REVIEW_STALE_DAYS, type RecordedShape, type RecurrenceRule, type RenderRules, type RenderedPayload, type RequestedMode, type ResolveOptions, type ResolvedConnection, type RetryPolicy, type RunIdentity, SANDBOX_KINDS, SIGNED_URI_RULES, SUPPORTED_CONNECTOR_API, type SandboxKind, type Segment, type ServiceDescriptor, type SetupStep, type ShapeMismatch, type SharedTokenScheme, type SharedTokenVerificationSpec, type SignedRequestLine, type SignedUriRule, type SpecSource, type SubscriptionLease, type TextUnit, type Transport, type TransportResponse, type TriggerDescriptor, type VerifyJwtOptions, type VerifyResult, type WebhookVerification, type WebhookVerificationSpec, assertConnectorApi, byteRangeOf, callConnector, capabilityProblems, checkAgainstOpenApi, checkAgainstRecordedShape, classifyError, classifyHttp, classifyStatus, classifyThrown, compareShape, connectionsFor, constantTimeEquals, decodeDelivery, decodeFormFieldJson, decodePubSubPush, deliver, fakeRequest, fakeValues, getConnectionHost, graphemes, handshakeResponse, header, hmac, httpFailure, idempotencyKeyFor, isEmptyPayload, isOidcJwtScheme, isOidcJwtSpec, isRemote, isSharedTokenScheme, isSharedTokenSpec, isUnconditionallyRetryable, joinTargets, leaseAction, leaseFromResponse, leaseRenewAt, leaseState, linkRanges, measure, nextOccurrence, normaliseSignedUri, parseInvite, parseMime, payloadHash, postChain, probe, providerProblems, providerSandboxIsSelectable, registerConnectionHost, registerTransport, render, reported, resetRateState, resolveConnection, resolveConnectorMode, respectRate, runIdentityFrom, runKeyFrom, runProbes, sandboxIsSelectable, sandboxRefusal, satisfiesMinimum, secretKeyBytes, seedFrom, shapeOf, shouldRetry, sliceByteRange, splitToFit, subscriptionLease, triggerEvent, unchecked, utf8Length, verifyDelivery, verifyHmac, verifyJwt, verifyOidcDelivery, verifySharedToken, withResolvedLimit };
|
|
3065
|
+
export { AMBIGUOUS_REFUSAL, type ApiContract, type Attempt, type Attendee, type ByteRange, CANONICAL_METRICS, CONNECTOR_API_VERSION, CONNECTOR_MODES, CREDENTIAL_SCOPES, CURSOR_READ_REASONS, type CallOptions, type CallResult, type Capabilities, type Catalogue, type ChainLinks, type ChainOutcome, type ChainRef, type ChallengeHandshake, type Citation, type Classified, type ConnectionCredentials, type ConnectionHost, type ConnectionSpec, type Connector, ConnectorAmbiguous, ConnectorApiMismatch, ConnectorAuthError, ConnectorConfigError, ConnectorError, type ConnectorErrorContext, type ConnectorFaker, ConnectorIdempotencyExpiredError, type ConnectorMode, ConnectorModeError, ConnectorRateLimited, type ConnectorRequest, ConnectorRequestError, type ConnectorResult, ConnectorTransient, ConnectorUnreachable, type ContractOperation, type CredentialField, type CredentialScope, CursorReadError, type CursorReadReason, type CursorReadResult, type CursorReadSpec, DEFAULT_IDEMPOTENCY_WINDOW_SECONDS, DEFAULT_RETRY, DELIVERY_DECODE_KINDS, type DeliveryDeclaration, type DeliveryDecode, type DeliveryMechanism, type DeliveryOutcome, type DeliveryRoute, type DriftFinding, type DriftReport, EXPIRES_AT_UNITS, type ExpiresAtUnit, FAKE_EPOCH, FORM_FIELD_JSON_REASONS, type FailureKind, type FakeRequest, type FakeValues, FormFieldJsonError, type FormFieldJsonReason, type HmacScheme, type HmacVerificationSpec, type HostEnvironment, type IdempotencyOptions, type InboundDelivery, type Invite, type InviteMethod, JWT_ALGORITHMS, type JoinTarget, type JoinTargetKind, type Jwk, type JwtVerification, type LeaseAction, type LeaseDeclaration, type LeaseState, MAX_IDEMPOTENCY_KEY_LENGTH, type MetricDescriptor, type MetricSample, type MimeAttachment, type MimeHeader, type MimeMessage, type MimePart, type ModeInputs, NO_IDEMPOTENCY_KEY_WARNING, OIDC_JWT_SCHEME_KIND, type OidcExpectation, type OidcJwtScheme, type OidcJwtVerificationSpec, PROBLEM_SEVERITIES, PUBSUB_PUSH_REASONS, type PreparedRequest, type ProbeOutcome, type ProbeReport, type ProbeResult, type ProbeSpec, type Problem, type ProviderAdapter, PubSubPushError, type PubSubPushMessage, type PubSubPushReason, RENDERER_VERSION, REVIEW_STALE_DAYS, type RecordedShape, type RecurrenceRule, type RenderRules, type RenderedPayload, type RequestedMode, type ResolveOptions, type ResolvedConnection, type RetryPolicy, type RunIdentity, SANDBOX_KINDS, SETUP_ACTORS, SETUP_RENDER_REASONS, SETUP_TOKENS, SIGNED_URI_RULES, SUPPORTED_CONNECTOR_API, type SandboxKind, type Segment, type ServiceDescriptor, type SetupActor, type SetupContext, SetupRenderError, type SetupRenderReason, type SetupStep, type SetupToken, type ShapeMismatch, type SharedTokenScheme, type SharedTokenVerificationSpec, type SignedRequestLine, type SignedUriRule, type SpecSource, type SubscriptionLease, type TextUnit, type Transport, type TransportResponse, type TriggerDescriptor, type VerifyJwtOptions, type VerifyResult, type WebhookVerification, type WebhookVerificationSpec, assertConnectorApi, byteRangeOf, callConnector, capabilityProblems, checkAgainstOpenApi, checkAgainstRecordedShape, classifyError, classifyHttp, classifyStatus, classifyThrown, compareShape, connectionsFor, constantTimeEquals, decodeDelivery, decodeFormFieldJson, decodePubSubPush, deliver, fakeRequest, fakeValues, getConnectionHost, graphemes, handshakeResponse, header, hmac, httpFailure, idempotencyKeyFor, isEmptyPayload, isOidcJwtScheme, isOidcJwtSpec, isRemote, isSharedTokenScheme, isSharedTokenSpec, isUnconditionallyRetryable, joinTargets, leaseAction, leaseFromResponse, leaseRenewAt, leaseState, linkRanges, measure, nextOccurrence, normaliseSignedUri, parseInvite, parseMime, payloadHash, postChain, probe, providerProblems, providerSandboxIsSelectable, readWithCursor, registerConnectionHost, registerTransport, render, renderSetup, reported, resetRateState, resolveConnection, resolveConnectorMode, respectRate, runIdentityFrom, runKeyFrom, runProbes, sandboxIsSelectable, sandboxRefusal, satisfiesMinimum, secretKeyBytes, seedFrom, shapeOf, shouldRetry, sliceByteRange, splitToFit, subscriptionLease, triggerEvent, unchecked, utf8Length, verifyDelivery, verifyHmac, verifyJwt, verifyOidcDelivery, verifySharedToken, withResolvedLimit };
|
package/dist/index.d.ts
CHANGED
|
@@ -916,6 +916,72 @@ type ConnectorResult<T = unknown> = {
|
|
|
916
916
|
declare function registerTransport(next: Transport | null): void;
|
|
917
917
|
declare function callConnector<T = unknown>(service: ServiceDescriptor, options: CallOptions): Promise<ConnectorResult<T>>;
|
|
918
918
|
|
|
919
|
+
/**
|
|
920
|
+
* A read that resumes from a HOST-HELD cursor, and pages.
|
|
921
|
+
*
|
|
922
|
+
* Gmail's push and Drive's channel notifications carry no changes: they say
|
|
923
|
+
* "something moved", and the follow-up read fetches what did, starting from
|
|
924
|
+
* the position the HOST stored last time (Gmail's `startHistoryId`, Drive's
|
|
925
|
+
* `pageToken`). Reading from the delivery's own position instead misses the
|
|
926
|
+
* very change that caused it. The package holds no state: the host stores the
|
|
927
|
+
* cursor, passes it in, and persists `cursor.next` when the run succeeds.
|
|
928
|
+
*
|
|
929
|
+
* The PHP twin is `CursorRead::read`; both read `fixtures/cursor-read/`.
|
|
930
|
+
*
|
|
931
|
+
* The decisions, each pinned by a case:
|
|
932
|
+
*
|
|
933
|
+
* - **`next` comes ONLY from the LAST page.** Gmail's `historyId` is the
|
|
934
|
+
* mailbox's CURRENT one on every page, and Google says to store it only "if
|
|
935
|
+
* you receive no nextPageToken". Taking it after page one would skip every
|
|
936
|
+
* page not yet read, silently.
|
|
937
|
+
* - **Too many pages is refused** (`cursor-overflow`), never a partial read
|
|
938
|
+
* with a cursor that skips the rest. The host's answer is a bounded resync.
|
|
939
|
+
* - **The provider's own "cursor out of range"** (`expiredStatus`, Gmail's
|
|
940
|
+
* 404) is `cursor-expired`; every OTHER failure is rethrown unchanged, so a
|
|
941
|
+
* 500 is never mistaken for a lapse.
|
|
942
|
+
* - **No stored cursor is `cursor-missing`**, and the provider is not called:
|
|
943
|
+
* a read with no starting point is a read from the beginning of time.
|
|
944
|
+
* - `collect` absent is `[]` (Gmail omits `history` when nothing changed); not
|
|
945
|
+
* a list is `bad-page`. The cursor and `next` are strings: history ids are
|
|
946
|
+
* uint64.
|
|
947
|
+
*/
|
|
948
|
+
declare const CURSOR_READ_REASONS: readonly ["cursor-missing", "cursor-expired", "cursor-overflow", "bad-page"];
|
|
949
|
+
type CursorReadReason = (typeof CURSOR_READ_REASONS)[number];
|
|
950
|
+
type CursorReadSpec = {
|
|
951
|
+
/** The read's parameter the stored cursor fills (Gmail: `startHistoryId`). */
|
|
952
|
+
param: string;
|
|
953
|
+
/** Where the NEXT cursor is read in the last page's data (Gmail: `historyId`). */
|
|
954
|
+
advanceFrom: string;
|
|
955
|
+
/** Paging, when the read pages (Gmail: `pageToken` / `nextPageToken`, `history`). */
|
|
956
|
+
page?: {
|
|
957
|
+
param: string;
|
|
958
|
+
from: string;
|
|
959
|
+
collect: string;
|
|
960
|
+
maxPages: number;
|
|
961
|
+
};
|
|
962
|
+
/** The status the provider answers for a cursor out of range (Gmail: 404). */
|
|
963
|
+
expiredStatus?: number;
|
|
964
|
+
};
|
|
965
|
+
type CursorReadResult = {
|
|
966
|
+
data: unknown;
|
|
967
|
+
cursor: {
|
|
968
|
+
used: string;
|
|
969
|
+
next: string | null;
|
|
970
|
+
};
|
|
971
|
+
};
|
|
972
|
+
/** Refused BY NAME; `reason` is the same string PHP's exception carries. */
|
|
973
|
+
declare class CursorReadError extends Error {
|
|
974
|
+
readonly reason: CursorReadReason;
|
|
975
|
+
/** The host's bounded resync, when the caller declared one (Gmail: `newer_than:7d`). */
|
|
976
|
+
readonly resyncQuery?: string | undefined;
|
|
977
|
+
constructor(reason: CursorReadReason, message: string,
|
|
978
|
+
/** The host's bounded resync, when the caller declared one (Gmail: `newer_than:7d`). */
|
|
979
|
+
resyncQuery?: string | undefined);
|
|
980
|
+
}
|
|
981
|
+
declare function readWithCursor(spec: CursorReadSpec, cursor: unknown, call: (params: Record<string, string>) => Promise<unknown>, options?: {
|
|
982
|
+
resyncQuery?: string;
|
|
983
|
+
}): Promise<CursorReadResult>;
|
|
984
|
+
|
|
919
985
|
/**
|
|
920
986
|
* API drift — being told the provider changed, rather than discovering it.
|
|
921
987
|
*
|
|
@@ -1982,11 +2048,27 @@ type CredentialField = {
|
|
|
1982
2048
|
/** A hint for an empty field. NEVER a real value. */
|
|
1983
2049
|
placeholder?: string;
|
|
1984
2050
|
};
|
|
2051
|
+
/** Who performs a setup step. Ships as data: a host mirrors it across JSON. */
|
|
2052
|
+
declare const SETUP_ACTORS: readonly ["person", "agent"];
|
|
2053
|
+
type SetupActor = (typeof SETUP_ACTORS)[number];
|
|
1985
2054
|
type SetupStep = {
|
|
1986
2055
|
title: string;
|
|
1987
2056
|
/** The step, and the trap in it. A step with no trap named is usually wrong. */
|
|
1988
2057
|
detail: string;
|
|
1989
2058
|
url?: string;
|
|
2059
|
+
/** Who does it. Absent means `person` (0.15.0). */
|
|
2060
|
+
actor?: SetupActor;
|
|
2061
|
+
/** What an agent is asked to do. Required when `actor` is `agent`. */
|
|
2062
|
+
prompt?: string;
|
|
2063
|
+
/** Values the person pastes somewhere, e.g. the callback URL. */
|
|
2064
|
+
copy?: {
|
|
2065
|
+
label: string;
|
|
2066
|
+
value: string;
|
|
2067
|
+
}[];
|
|
2068
|
+
/** The credential keys this step yields. */
|
|
2069
|
+
fields?: string[];
|
|
2070
|
+
/** How the person knows the step is done. */
|
|
2071
|
+
done?: string;
|
|
1990
2072
|
};
|
|
1991
2073
|
/**
|
|
1992
2074
|
* The result of a read-only credential check.
|
|
@@ -2784,6 +2866,40 @@ type PubSubPushMessage = {
|
|
|
2784
2866
|
};
|
|
2785
2867
|
declare function decodePubSubPush(raw: Uint8Array | string): PubSubPushMessage;
|
|
2786
2868
|
|
|
2869
|
+
/**
|
|
2870
|
+
* A connector's setup steps, rendered for ONE app.
|
|
2871
|
+
*
|
|
2872
|
+
* A definition is written once for every host, so the values that differ per
|
|
2873
|
+
* host — where its webhooks live, what it calls itself, which connector this
|
|
2874
|
+
* is — are tokens: `{{appUrl}}`, `{{appName}}`, `{{connector}}`, and only
|
|
2875
|
+
* those. Held to `fixtures/setup-render/cases.json`, mirrored in
|
|
2876
|
+
* `php/src/Setup.php`.
|
|
2877
|
+
*
|
|
2878
|
+
* Two refusals, both BY NAME, because each would otherwise reach a person as a
|
|
2879
|
+
* broken instruction that looks deliberate:
|
|
2880
|
+
*
|
|
2881
|
+
* - `unknown-token`: anything else shaped like `{{...}}` — a typo, a spaced
|
|
2882
|
+
* `{{ appUrl }}`, a token a definition invented. Left in, a person pastes a
|
|
2883
|
+
* literal brace into a provider console.
|
|
2884
|
+
* - `missing-context`: a token the host gave no (or an empty) value for. Left
|
|
2885
|
+
* empty, a callback URL becomes `/hooks/twilio` and points nowhere.
|
|
2886
|
+
*
|
|
2887
|
+
* `appUrl` loses ONE trailing slash first, so `{{appUrl}}/hooks` never doubles
|
|
2888
|
+
* it. A context value is inserted once and never expanded again.
|
|
2889
|
+
*/
|
|
2890
|
+
|
|
2891
|
+
declare const SETUP_TOKENS: readonly ["appUrl", "appName", "connector"];
|
|
2892
|
+
type SetupToken = (typeof SETUP_TOKENS)[number];
|
|
2893
|
+
declare const SETUP_RENDER_REASONS: readonly ["unknown-token", "missing-context"];
|
|
2894
|
+
type SetupRenderReason = (typeof SETUP_RENDER_REASONS)[number];
|
|
2895
|
+
type SetupContext = Partial<Record<SetupToken, string>>;
|
|
2896
|
+
declare class SetupRenderError extends Error {
|
|
2897
|
+
readonly reason: SetupRenderReason;
|
|
2898
|
+
constructor(reason: SetupRenderReason, message: string);
|
|
2899
|
+
}
|
|
2900
|
+
/** Render every step for one app: tokens filled, `actor` and `fields` defaulted. */
|
|
2901
|
+
declare function renderSetup(steps: readonly SetupStep[], context: SetupContext): SetupStep[];
|
|
2902
|
+
|
|
2787
2903
|
/**
|
|
2788
2904
|
* How a connector trigger learns that something happened.
|
|
2789
2905
|
*
|
|
@@ -2946,4 +3062,4 @@ oidc?: OidcExpectation): Promise<WebhookVerification>;
|
|
|
2946
3062
|
*/
|
|
2947
3063
|
declare function triggerEvent(trigger: TriggerDescriptor, connection: ResolvedConnection, injected: unknown, config: Record<string, unknown>): unknown;
|
|
2948
3064
|
|
|
2949
|
-
export { AMBIGUOUS_REFUSAL, type ApiContract, type Attempt, type Attendee, type ByteRange, CANONICAL_METRICS, CONNECTOR_API_VERSION, CONNECTOR_MODES, CREDENTIAL_SCOPES, type CallOptions, type CallResult, type Capabilities, type Catalogue, type ChainLinks, type ChainOutcome, type ChainRef, type ChallengeHandshake, type Citation, type Classified, type ConnectionCredentials, type ConnectionHost, type ConnectionSpec, type Connector, ConnectorAmbiguous, ConnectorApiMismatch, ConnectorAuthError, ConnectorConfigError, ConnectorError, type ConnectorErrorContext, type ConnectorFaker, ConnectorIdempotencyExpiredError, type ConnectorMode, ConnectorModeError, ConnectorRateLimited, type ConnectorRequest, ConnectorRequestError, type ConnectorResult, ConnectorTransient, ConnectorUnreachable, type ContractOperation, type CredentialField, type CredentialScope, DEFAULT_IDEMPOTENCY_WINDOW_SECONDS, DEFAULT_RETRY, DELIVERY_DECODE_KINDS, type DeliveryDeclaration, type DeliveryDecode, type DeliveryMechanism, type DeliveryOutcome, type DeliveryRoute, type DriftFinding, type DriftReport, EXPIRES_AT_UNITS, type ExpiresAtUnit, FAKE_EPOCH, FORM_FIELD_JSON_REASONS, type FailureKind, type FakeRequest, type FakeValues, FormFieldJsonError, type FormFieldJsonReason, type HmacScheme, type HmacVerificationSpec, type HostEnvironment, type IdempotencyOptions, type InboundDelivery, type Invite, type InviteMethod, JWT_ALGORITHMS, type JoinTarget, type JoinTargetKind, type Jwk, type JwtVerification, type LeaseAction, type LeaseDeclaration, type LeaseState, MAX_IDEMPOTENCY_KEY_LENGTH, type MetricDescriptor, type MetricSample, type MimeAttachment, type MimeHeader, type MimeMessage, type MimePart, type ModeInputs, NO_IDEMPOTENCY_KEY_WARNING, OIDC_JWT_SCHEME_KIND, type OidcExpectation, type OidcJwtScheme, type OidcJwtVerificationSpec, PROBLEM_SEVERITIES, PUBSUB_PUSH_REASONS, type PreparedRequest, type ProbeOutcome, type ProbeReport, type ProbeResult, type ProbeSpec, type Problem, type ProviderAdapter, PubSubPushError, type PubSubPushMessage, type PubSubPushReason, RENDERER_VERSION, REVIEW_STALE_DAYS, type RecordedShape, type RecurrenceRule, type RenderRules, type RenderedPayload, type RequestedMode, type ResolveOptions, type ResolvedConnection, type RetryPolicy, type RunIdentity, SANDBOX_KINDS, SIGNED_URI_RULES, SUPPORTED_CONNECTOR_API, type SandboxKind, type Segment, type ServiceDescriptor, type SetupStep, type ShapeMismatch, type SharedTokenScheme, type SharedTokenVerificationSpec, type SignedRequestLine, type SignedUriRule, type SpecSource, type SubscriptionLease, type TextUnit, type Transport, type TransportResponse, type TriggerDescriptor, type VerifyJwtOptions, type VerifyResult, type WebhookVerification, type WebhookVerificationSpec, assertConnectorApi, byteRangeOf, callConnector, capabilityProblems, checkAgainstOpenApi, checkAgainstRecordedShape, classifyError, classifyHttp, classifyStatus, classifyThrown, compareShape, connectionsFor, constantTimeEquals, decodeDelivery, decodeFormFieldJson, decodePubSubPush, deliver, fakeRequest, fakeValues, getConnectionHost, graphemes, handshakeResponse, header, hmac, httpFailure, idempotencyKeyFor, isEmptyPayload, isOidcJwtScheme, isOidcJwtSpec, isRemote, isSharedTokenScheme, isSharedTokenSpec, isUnconditionallyRetryable, joinTargets, leaseAction, leaseFromResponse, leaseRenewAt, leaseState, linkRanges, measure, nextOccurrence, normaliseSignedUri, parseInvite, parseMime, payloadHash, postChain, probe, providerProblems, providerSandboxIsSelectable, registerConnectionHost, registerTransport, render, reported, resetRateState, resolveConnection, resolveConnectorMode, respectRate, runIdentityFrom, runKeyFrom, runProbes, sandboxIsSelectable, sandboxRefusal, satisfiesMinimum, secretKeyBytes, seedFrom, shapeOf, shouldRetry, sliceByteRange, splitToFit, subscriptionLease, triggerEvent, unchecked, utf8Length, verifyDelivery, verifyHmac, verifyJwt, verifyOidcDelivery, verifySharedToken, withResolvedLimit };
|
|
3065
|
+
export { AMBIGUOUS_REFUSAL, type ApiContract, type Attempt, type Attendee, type ByteRange, CANONICAL_METRICS, CONNECTOR_API_VERSION, CONNECTOR_MODES, CREDENTIAL_SCOPES, CURSOR_READ_REASONS, type CallOptions, type CallResult, type Capabilities, type Catalogue, type ChainLinks, type ChainOutcome, type ChainRef, type ChallengeHandshake, type Citation, type Classified, type ConnectionCredentials, type ConnectionHost, type ConnectionSpec, type Connector, ConnectorAmbiguous, ConnectorApiMismatch, ConnectorAuthError, ConnectorConfigError, ConnectorError, type ConnectorErrorContext, type ConnectorFaker, ConnectorIdempotencyExpiredError, type ConnectorMode, ConnectorModeError, ConnectorRateLimited, type ConnectorRequest, ConnectorRequestError, type ConnectorResult, ConnectorTransient, ConnectorUnreachable, type ContractOperation, type CredentialField, type CredentialScope, CursorReadError, type CursorReadReason, type CursorReadResult, type CursorReadSpec, DEFAULT_IDEMPOTENCY_WINDOW_SECONDS, DEFAULT_RETRY, DELIVERY_DECODE_KINDS, type DeliveryDeclaration, type DeliveryDecode, type DeliveryMechanism, type DeliveryOutcome, type DeliveryRoute, type DriftFinding, type DriftReport, EXPIRES_AT_UNITS, type ExpiresAtUnit, FAKE_EPOCH, FORM_FIELD_JSON_REASONS, type FailureKind, type FakeRequest, type FakeValues, FormFieldJsonError, type FormFieldJsonReason, type HmacScheme, type HmacVerificationSpec, type HostEnvironment, type IdempotencyOptions, type InboundDelivery, type Invite, type InviteMethod, JWT_ALGORITHMS, type JoinTarget, type JoinTargetKind, type Jwk, type JwtVerification, type LeaseAction, type LeaseDeclaration, type LeaseState, MAX_IDEMPOTENCY_KEY_LENGTH, type MetricDescriptor, type MetricSample, type MimeAttachment, type MimeHeader, type MimeMessage, type MimePart, type ModeInputs, NO_IDEMPOTENCY_KEY_WARNING, OIDC_JWT_SCHEME_KIND, type OidcExpectation, type OidcJwtScheme, type OidcJwtVerificationSpec, PROBLEM_SEVERITIES, PUBSUB_PUSH_REASONS, type PreparedRequest, type ProbeOutcome, type ProbeReport, type ProbeResult, type ProbeSpec, type Problem, type ProviderAdapter, PubSubPushError, type PubSubPushMessage, type PubSubPushReason, RENDERER_VERSION, REVIEW_STALE_DAYS, type RecordedShape, type RecurrenceRule, type RenderRules, type RenderedPayload, type RequestedMode, type ResolveOptions, type ResolvedConnection, type RetryPolicy, type RunIdentity, SANDBOX_KINDS, SETUP_ACTORS, SETUP_RENDER_REASONS, SETUP_TOKENS, SIGNED_URI_RULES, SUPPORTED_CONNECTOR_API, type SandboxKind, type Segment, type ServiceDescriptor, type SetupActor, type SetupContext, SetupRenderError, type SetupRenderReason, type SetupStep, type SetupToken, type ShapeMismatch, type SharedTokenScheme, type SharedTokenVerificationSpec, type SignedRequestLine, type SignedUriRule, type SpecSource, type SubscriptionLease, type TextUnit, type Transport, type TransportResponse, type TriggerDescriptor, type VerifyJwtOptions, type VerifyResult, type WebhookVerification, type WebhookVerificationSpec, assertConnectorApi, byteRangeOf, callConnector, capabilityProblems, checkAgainstOpenApi, checkAgainstRecordedShape, classifyError, classifyHttp, classifyStatus, classifyThrown, compareShape, connectionsFor, constantTimeEquals, decodeDelivery, decodeFormFieldJson, decodePubSubPush, deliver, fakeRequest, fakeValues, getConnectionHost, graphemes, handshakeResponse, header, hmac, httpFailure, idempotencyKeyFor, isEmptyPayload, isOidcJwtScheme, isOidcJwtSpec, isRemote, isSharedTokenScheme, isSharedTokenSpec, isUnconditionallyRetryable, joinTargets, leaseAction, leaseFromResponse, leaseRenewAt, leaseState, linkRanges, measure, nextOccurrence, normaliseSignedUri, parseInvite, parseMime, payloadHash, postChain, probe, providerProblems, providerSandboxIsSelectable, readWithCursor, registerConnectionHost, registerTransport, render, renderSetup, reported, resetRateState, resolveConnection, resolveConnectorMode, respectRate, runIdentityFrom, runKeyFrom, runProbes, sandboxIsSelectable, sandboxRefusal, satisfiesMinimum, secretKeyBytes, seedFrom, shapeOf, shouldRetry, sliceByteRange, splitToFit, subscriptionLease, triggerEvent, unchecked, utf8Length, verifyDelivery, verifyHmac, verifyJwt, verifyOidcDelivery, verifySharedToken, withResolvedLimit };
|
package/dist/index.js
CHANGED
|
@@ -163,10 +163,10 @@ var lastCallAt = /* @__PURE__ */ new Map();
|
|
|
163
163
|
async function respectRate(channel, minIntervalMs, now = () => Date.now(), sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
|
|
164
164
|
if (minIntervalMs <= 0) return { waitedMs: 0 };
|
|
165
165
|
const previous = lastCallAt.get(channel);
|
|
166
|
-
const
|
|
167
|
-
const waitedMs = previous === void 0 ? 0 : Math.max(0, previous + minIntervalMs -
|
|
166
|
+
const at2 = now();
|
|
167
|
+
const waitedMs = previous === void 0 ? 0 : Math.max(0, previous + minIntervalMs - at2);
|
|
168
168
|
if (waitedMs > 0) await sleep(waitedMs);
|
|
169
|
-
lastCallAt.set(channel,
|
|
169
|
+
lastCallAt.set(channel, at2 + waitedMs);
|
|
170
170
|
return { waitedMs };
|
|
171
171
|
}
|
|
172
172
|
function resetRateState() {
|
|
@@ -664,6 +664,71 @@ function readString(config, key) {
|
|
|
664
664
|
return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
665
665
|
}
|
|
666
666
|
|
|
667
|
+
// src/cursor-read.ts
|
|
668
|
+
var CURSOR_READ_REASONS = ["cursor-missing", "cursor-expired", "cursor-overflow", "bad-page"];
|
|
669
|
+
var CursorReadError = class extends Error {
|
|
670
|
+
constructor(reason, message, resyncQuery) {
|
|
671
|
+
super(message);
|
|
672
|
+
this.reason = reason;
|
|
673
|
+
this.resyncQuery = resyncQuery;
|
|
674
|
+
this.name = "CursorReadError";
|
|
675
|
+
}
|
|
676
|
+
reason;
|
|
677
|
+
resyncQuery;
|
|
678
|
+
};
|
|
679
|
+
async function readWithCursor(spec, cursor, call, options = {}) {
|
|
680
|
+
const refuse = (reason, message) => new CursorReadError(reason, message, options.resyncQuery);
|
|
681
|
+
const used = asCursor(cursor);
|
|
682
|
+
if (used === null) throw refuse("cursor-missing", `no stored ${spec.param}: the host seeds one before the first read`);
|
|
683
|
+
const collected = [];
|
|
684
|
+
let last;
|
|
685
|
+
let token = null;
|
|
686
|
+
let pages = 0;
|
|
687
|
+
for (; ; ) {
|
|
688
|
+
if (spec.page && pages === spec.page.maxPages) {
|
|
689
|
+
throw refuse("cursor-overflow", `more than ${spec.page.maxPages} pages since ${spec.param} ${used}`);
|
|
690
|
+
}
|
|
691
|
+
const params = { [spec.param]: used };
|
|
692
|
+
if (spec.page && token !== null) params[spec.page.param] = token;
|
|
693
|
+
try {
|
|
694
|
+
last = await call(params);
|
|
695
|
+
} catch (error) {
|
|
696
|
+
const status = error?.status;
|
|
697
|
+
if (spec.expiredStatus !== void 0 && status === spec.expiredStatus) {
|
|
698
|
+
throw refuse("cursor-expired", `${spec.param} ${used} is out of the provider's range (${status})`);
|
|
699
|
+
}
|
|
700
|
+
throw error;
|
|
701
|
+
}
|
|
702
|
+
pages++;
|
|
703
|
+
if (!spec.page) break;
|
|
704
|
+
const items = at(last, spec.page.collect);
|
|
705
|
+
if (items !== void 0 && !Array.isArray(items)) {
|
|
706
|
+
throw refuse("bad-page", `${spec.page.collect} is not a list`);
|
|
707
|
+
}
|
|
708
|
+
if (items) collected.push(...items);
|
|
709
|
+
token = asCursor(at(last, spec.page.from));
|
|
710
|
+
if (token === null) break;
|
|
711
|
+
}
|
|
712
|
+
const data = spec.page ? withList(last, spec.page.collect, collected) : last;
|
|
713
|
+
return { data, cursor: { used, next: asCursor(at(last, spec.advanceFrom)) } };
|
|
714
|
+
}
|
|
715
|
+
function asCursor(value) {
|
|
716
|
+
if (typeof value === "string") return value === "" ? null : value;
|
|
717
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
function at(value, path) {
|
|
721
|
+
return path.split(".").reduce(
|
|
722
|
+
(v, key) => v !== null && typeof v === "object" && !Array.isArray(v) ? v[key] : void 0,
|
|
723
|
+
value
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
function withList(data, path, list) {
|
|
727
|
+
const [head, ...rest] = path.split(".");
|
|
728
|
+
const base = data !== null && typeof data === "object" && !Array.isArray(data) ? data : {};
|
|
729
|
+
return { ...base, [head]: rest.length ? withList(base[head], rest.join("."), list) : list };
|
|
730
|
+
}
|
|
731
|
+
|
|
667
732
|
// src/drift.ts
|
|
668
733
|
function resolveRef(document, node, depth = 0) {
|
|
669
734
|
if (depth > 8 || node === null || typeof node !== "object") return node;
|
|
@@ -1318,32 +1383,32 @@ function base64urlBytes(segment) {
|
|
|
1318
1383
|
var EXPIRES_AT_UNITS = ["rfc3339", "epoch-ms"];
|
|
1319
1384
|
var INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
1320
1385
|
var DEFAULT_CONTEXT = { service: "", operation: "subscription" };
|
|
1321
|
-
function instantMs(value, field,
|
|
1386
|
+
function instantMs(value, field, at2) {
|
|
1322
1387
|
if (typeof value !== "string" || !INSTANT.test(value)) {
|
|
1323
1388
|
throw new ConnectorConfigError(
|
|
1324
1389
|
`${field} must be an RFC 3339 instant such as "2026-09-22T00:00:00Z", got ${JSON.stringify(value)}. A provider's own shape \u2014 Google's epoch milliseconds, say \u2014 is converted by the connector, never guessed at here.`,
|
|
1325
|
-
|
|
1390
|
+
at2
|
|
1326
1391
|
);
|
|
1327
1392
|
}
|
|
1328
1393
|
const ms = Date.parse(value.replace(/(\.\d{3})\d+/, "$1"));
|
|
1329
1394
|
if (Number.isNaN(ms)) {
|
|
1330
|
-
throw new ConnectorConfigError(`${field} is not a real instant: ${JSON.stringify(value)}`,
|
|
1395
|
+
throw new ConnectorConfigError(`${field} is not a real instant: ${JSON.stringify(value)}`, at2);
|
|
1331
1396
|
}
|
|
1332
1397
|
return ms;
|
|
1333
1398
|
}
|
|
1334
|
-
function leaseFromResponse(declaration, response,
|
|
1399
|
+
function leaseFromResponse(declaration, response, at2 = DEFAULT_CONTEXT) {
|
|
1335
1400
|
const { expiresAtFrom: path, expiresAtUnit: unit } = declaration;
|
|
1336
1401
|
if (!EXPIRES_AT_UNITS.includes(unit)) {
|
|
1337
1402
|
throw new ConnectorConfigError(
|
|
1338
1403
|
`expiresAtUnit must be one of ${EXPIRES_AT_UNITS.join(" | ")}, got ${JSON.stringify(unit)}.`,
|
|
1339
|
-
|
|
1404
|
+
at2
|
|
1340
1405
|
);
|
|
1341
1406
|
}
|
|
1342
1407
|
const value = readPath(response, path);
|
|
1343
1408
|
if (value === void 0 || value === null) {
|
|
1344
1409
|
throw new ConnectorConfigError(
|
|
1345
1410
|
`${path} is not present in the response, so no lease can be built \u2014 a subscription with no known expiry is one nobody renews.`,
|
|
1346
|
-
|
|
1411
|
+
at2
|
|
1347
1412
|
);
|
|
1348
1413
|
}
|
|
1349
1414
|
let expiresAt;
|
|
@@ -1352,7 +1417,7 @@ function leaseFromResponse(declaration, response, at = DEFAULT_CONTEXT) {
|
|
|
1352
1417
|
if (!Number.isSafeInteger(ms)) {
|
|
1353
1418
|
throw new ConnectorConfigError(
|
|
1354
1419
|
`${path} must be epoch milliseconds as digits under "epoch-ms", got ${JSON.stringify(value)}.`,
|
|
1355
|
-
|
|
1420
|
+
at2
|
|
1356
1421
|
);
|
|
1357
1422
|
}
|
|
1358
1423
|
expiresAt = new Date(ms).toISOString();
|
|
@@ -1360,14 +1425,14 @@ function leaseFromResponse(declaration, response, at = DEFAULT_CONTEXT) {
|
|
|
1360
1425
|
if (typeof value !== "string" || !INSTANT.test(value)) {
|
|
1361
1426
|
throw new ConnectorConfigError(
|
|
1362
1427
|
`${path} must be an RFC 3339 instant under "rfc3339", got ${JSON.stringify(value)}.`,
|
|
1363
|
-
|
|
1428
|
+
at2
|
|
1364
1429
|
);
|
|
1365
1430
|
}
|
|
1366
|
-
expiresAt = new Date(instantMs(value, path,
|
|
1431
|
+
expiresAt = new Date(instantMs(value, path, at2)).toISOString();
|
|
1367
1432
|
}
|
|
1368
1433
|
return subscriptionLease(
|
|
1369
1434
|
{ expiresAt, renewBeforeSeconds: declaration.renewBeforeSeconds, renewOperation: declaration.renewOperation },
|
|
1370
|
-
|
|
1435
|
+
at2
|
|
1371
1436
|
);
|
|
1372
1437
|
}
|
|
1373
1438
|
function readPath(value, path) {
|
|
@@ -1378,18 +1443,18 @@ function readPath(value, path) {
|
|
|
1378
1443
|
}
|
|
1379
1444
|
return current;
|
|
1380
1445
|
}
|
|
1381
|
-
function subscriptionLease(input,
|
|
1382
|
-
instantMs(input.expiresAt, "expiresAt",
|
|
1446
|
+
function subscriptionLease(input, at2 = DEFAULT_CONTEXT) {
|
|
1447
|
+
instantMs(input.expiresAt, "expiresAt", at2);
|
|
1383
1448
|
if (!Number.isInteger(input.renewBeforeSeconds) || input.renewBeforeSeconds <= 0) {
|
|
1384
1449
|
throw new ConnectorConfigError(
|
|
1385
1450
|
`renewBeforeSeconds must be a positive whole number of seconds, got ${JSON.stringify(input.renewBeforeSeconds)}. Zero would make the lease due only at the instant it expires, which is a lease nobody renews.`,
|
|
1386
|
-
|
|
1451
|
+
at2
|
|
1387
1452
|
);
|
|
1388
1453
|
}
|
|
1389
1454
|
if (typeof input.renewOperation !== "string" || input.renewOperation.trim() === "") {
|
|
1390
1455
|
throw new ConnectorConfigError(
|
|
1391
1456
|
"renewOperation must name the operation the host calls when the lease is due; a due lease with nothing to call is one nobody renews.",
|
|
1392
|
-
|
|
1457
|
+
at2
|
|
1393
1458
|
);
|
|
1394
1459
|
}
|
|
1395
1460
|
return { expiresAt: input.expiresAt, renewBeforeSeconds: input.renewBeforeSeconds, renewOperation: input.renewOperation };
|
|
@@ -1398,10 +1463,10 @@ function leaseRenewAt(lease) {
|
|
|
1398
1463
|
return new Date(instantMs(lease.expiresAt, "expiresAt", DEFAULT_CONTEXT) - lease.renewBeforeSeconds * 1e3).toISOString();
|
|
1399
1464
|
}
|
|
1400
1465
|
function leaseState(lease, now) {
|
|
1401
|
-
const
|
|
1466
|
+
const at2 = now instanceof Date ? now.getTime() : instantMs(now, "now", DEFAULT_CONTEXT);
|
|
1402
1467
|
const expires = instantMs(lease.expiresAt, "expiresAt", DEFAULT_CONTEXT);
|
|
1403
|
-
if (
|
|
1404
|
-
if (
|
|
1468
|
+
if (at2 >= expires) return "expired";
|
|
1469
|
+
if (at2 >= expires - lease.renewBeforeSeconds * 1e3) return "due";
|
|
1405
1470
|
return "active";
|
|
1406
1471
|
}
|
|
1407
1472
|
function leaseAction(lease, now) {
|
|
@@ -1721,8 +1786,8 @@ function splitMultipart(body, boundary) {
|
|
|
1721
1786
|
if (partStart >= 0 && partStart <= body.length) children.push(body.subarray(partStart));
|
|
1722
1787
|
return children;
|
|
1723
1788
|
}
|
|
1724
|
-
function startsWith(haystack,
|
|
1725
|
-
for (let k = 0; k < needle.length; k++) if (haystack[
|
|
1789
|
+
function startsWith(haystack, at2, needle) {
|
|
1790
|
+
for (let k = 0; k < needle.length; k++) if (haystack[at2 + k] !== needle[k]) return false;
|
|
1726
1791
|
return true;
|
|
1727
1792
|
}
|
|
1728
1793
|
function parseMime(raw) {
|
|
@@ -2289,6 +2354,53 @@ function withResolvedLimit(rules, limit) {
|
|
|
2289
2354
|
var CREDENTIAL_SCOPES = ["provider", "account"];
|
|
2290
2355
|
var PROBLEM_SEVERITIES = ["block", "warn"];
|
|
2291
2356
|
var CANONICAL_METRICS = ["like", "share", "reply", "quote", "view"];
|
|
2357
|
+
var SETUP_ACTORS = ["person", "agent"];
|
|
2358
|
+
|
|
2359
|
+
// src/setup.ts
|
|
2360
|
+
var SETUP_TOKENS = ["appUrl", "appName", "connector"];
|
|
2361
|
+
var SETUP_RENDER_REASONS = ["unknown-token", "missing-context"];
|
|
2362
|
+
var SetupRenderError = class extends Error {
|
|
2363
|
+
constructor(reason, message) {
|
|
2364
|
+
super(message);
|
|
2365
|
+
this.reason = reason;
|
|
2366
|
+
this.name = "SetupRenderError";
|
|
2367
|
+
}
|
|
2368
|
+
reason;
|
|
2369
|
+
};
|
|
2370
|
+
var TOKEN = /\{\{([^{}]*)\}\}/g;
|
|
2371
|
+
function fill(text, context) {
|
|
2372
|
+
return text.replace(TOKEN, (whole, name) => {
|
|
2373
|
+
if (!SETUP_TOKENS.includes(name)) {
|
|
2374
|
+
throw new SetupRenderError(
|
|
2375
|
+
"unknown-token",
|
|
2376
|
+
`setup step text uses ${whole}, which is not one of ${SETUP_TOKENS.map((t) => `{{${t}}}`).join(", ")}`
|
|
2377
|
+
);
|
|
2378
|
+
}
|
|
2379
|
+
let value = context[name];
|
|
2380
|
+
if (value === void 0 || value === null || value === "") {
|
|
2381
|
+
throw new SetupRenderError("missing-context", `setup step text uses ${whole} and the host gave no value for ${name}`);
|
|
2382
|
+
}
|
|
2383
|
+
if (name === "appUrl" && value.endsWith("/")) value = value.slice(0, -1);
|
|
2384
|
+
return value;
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2387
|
+
function renderSetup(steps, context) {
|
|
2388
|
+
return steps.map((step) => {
|
|
2389
|
+
const out = {
|
|
2390
|
+
title: fill(step.title, context),
|
|
2391
|
+
detail: fill(step.detail, context),
|
|
2392
|
+
actor: step.actor ?? "person",
|
|
2393
|
+
fields: [...step.fields ?? []]
|
|
2394
|
+
};
|
|
2395
|
+
if (step.url !== void 0) out.url = fill(step.url, context);
|
|
2396
|
+
if (step.prompt !== void 0) out.prompt = fill(step.prompt, context);
|
|
2397
|
+
if (step.done !== void 0) out.done = fill(step.done, context);
|
|
2398
|
+
if (step.copy !== void 0) {
|
|
2399
|
+
out.copy = step.copy.map((c) => ({ label: fill(c.label, context), value: fill(c.value, context) }));
|
|
2400
|
+
}
|
|
2401
|
+
return out;
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
2292
2404
|
|
|
2293
2405
|
// src/trigger.ts
|
|
2294
2406
|
function isOidcJwtSpec(spec) {
|
|
@@ -2363,6 +2475,7 @@ export {
|
|
|
2363
2475
|
CONNECTOR_API_VERSION,
|
|
2364
2476
|
CONNECTOR_MODES,
|
|
2365
2477
|
CREDENTIAL_SCOPES,
|
|
2478
|
+
CURSOR_READ_REASONS,
|
|
2366
2479
|
ConnectorAmbiguous,
|
|
2367
2480
|
ConnectorApiMismatch,
|
|
2368
2481
|
ConnectorAuthError,
|
|
@@ -2374,6 +2487,7 @@ export {
|
|
|
2374
2487
|
ConnectorRequestError,
|
|
2375
2488
|
ConnectorTransient,
|
|
2376
2489
|
ConnectorUnreachable,
|
|
2490
|
+
CursorReadError,
|
|
2377
2491
|
DEFAULT_IDEMPOTENCY_WINDOW_SECONDS,
|
|
2378
2492
|
DEFAULT_RETRY,
|
|
2379
2493
|
DELIVERY_DECODE_KINDS,
|
|
@@ -2391,8 +2505,12 @@ export {
|
|
|
2391
2505
|
RENDERER_VERSION,
|
|
2392
2506
|
REVIEW_STALE_DAYS,
|
|
2393
2507
|
SANDBOX_KINDS,
|
|
2508
|
+
SETUP_ACTORS,
|
|
2509
|
+
SETUP_RENDER_REASONS,
|
|
2510
|
+
SETUP_TOKENS,
|
|
2394
2511
|
SIGNED_URI_RULES,
|
|
2395
2512
|
SUPPORTED_CONNECTOR_API,
|
|
2513
|
+
SetupRenderError,
|
|
2396
2514
|
assertConnectorApi,
|
|
2397
2515
|
byteRangeOf,
|
|
2398
2516
|
callConnector,
|
|
@@ -2442,9 +2560,11 @@ export {
|
|
|
2442
2560
|
probe,
|
|
2443
2561
|
providerProblems,
|
|
2444
2562
|
providerSandboxIsSelectable,
|
|
2563
|
+
readWithCursor,
|
|
2445
2564
|
registerConnectionHost,
|
|
2446
2565
|
registerTransport,
|
|
2447
2566
|
render,
|
|
2567
|
+
renderSetup,
|
|
2448
2568
|
reported,
|
|
2449
2569
|
resetRateState,
|
|
2450
2570
|
resolveConnection,
|