@particle-academy/fancy-connector-core 0.9.0 → 0.10.1

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/index.d.cts CHANGED
@@ -1256,6 +1256,92 @@ declare function classifyThrown(cause: unknown, ctx: ConnectorErrorContext): Con
1256
1256
  */
1257
1257
  declare function httpFailure(status: number, body: string, retryAfter?: string | null): Error;
1258
1258
 
1259
+ /**
1260
+ * RFC 5545 in, a structured invite and its join targets out — the read MOIC's
1261
+ * meeting bot needs from nothing but the text a calendar API hands back.
1262
+ *
1263
+ * Written from scratch (the owner: no third-party code), as ONE
1264
+ * implementation in two runtimes — `php/src/Ical.php` is the twin — and held
1265
+ * to the authored corpus under `fixtures/ical/`, which both must reproduce
1266
+ * exactly. `fixtures/ical/README.md` lists every decision a case pins; the
1267
+ * ones easy to get differently in two languages are restated here:
1268
+ *
1269
+ * - Folded lines (a line break followed by a single space or tab) join
1270
+ * before anything else runs; TEXT values are then RFC 5545-unescaped
1271
+ * (`\n`, `\,`, `\;`, `\\`).
1272
+ * - `DTSTART`/`DTEND` always come back as UTC instants. A `Z` suffix passes
1273
+ * through; a `TZID` parameter resolves through the calendar's own
1274
+ * `VTIMEZONE` (an unknown TZID is refused BY NAME, never guessed); a value
1275
+ * with neither — RFC 5545's FLOATING time — is treated as UTC, a decision
1276
+ * named here rather than left to look like an oversight: resolving a
1277
+ * floating time correctly needs the READER's timezone, which nothing in
1278
+ * the invite carries.
1279
+ * - `RRULE` support is intentionally narrow: `FREQ` daily/weekly/monthly/
1280
+ * yearly with `INTERVAL`, `COUNT` and `UNTIL`, and `BYDAY` for WEEKLY only.
1281
+ * No `BYMONTHDAY`, `BYSETPOS`, `BYWEEKNO`, `BYYEARDAY`, `WKST`, `EXDATE` or
1282
+ * `RDATE` — a recurring invite using one of those still parses (its RRULE
1283
+ * fields are read, never dropped), but `nextOccurrence` does not attempt
1284
+ * to walk it.
1285
+ * - Join targets are ALLOW-LISTED: Google Meet, Zoom (with its meeting id
1286
+ * and passcode read out of the URL), Microsoft Teams, and a dial-in phone
1287
+ * number with a passcode read from its own line. Anything else is ignored,
1288
+ * never guessed at — a bot that tried an unknown link shape would attempt
1289
+ * to join something it cannot handle.
1290
+ */
1291
+ type InviteMethod = "REQUEST" | "CANCEL";
1292
+ type Attendee = {
1293
+ name: string | null;
1294
+ email: string;
1295
+ role: string | null;
1296
+ partstat: string | null;
1297
+ };
1298
+ type RecurrenceRule = {
1299
+ freq: "DAILY" | "WEEKLY" | "MONTHLY" | "YEARLY";
1300
+ interval: number;
1301
+ count: number | null;
1302
+ until: string | null;
1303
+ byDay: string[] | null;
1304
+ };
1305
+ type Invite = {
1306
+ method: InviteMethod;
1307
+ uid: string;
1308
+ sequence: number;
1309
+ summary: string | null;
1310
+ description: string | null;
1311
+ location: string | null;
1312
+ status: string | null;
1313
+ dtstart: string;
1314
+ dtend: string | null;
1315
+ organizer: {
1316
+ name: string | null;
1317
+ email: string;
1318
+ } | null;
1319
+ attendees: Attendee[];
1320
+ rrule: RecurrenceRule | null;
1321
+ };
1322
+ type JoinTargetKind = "google-meet" | "zoom" | "teams" | "dial-in";
1323
+ type JoinTarget = {
1324
+ kind: JoinTargetKind;
1325
+ url: string | null;
1326
+ meetingId: string | null;
1327
+ passcode: string | null;
1328
+ phone: string | null;
1329
+ };
1330
+ declare function parseInvite(raw: string): Invite;
1331
+ /**
1332
+ * The next occurrence at or after `afterIso`, or `null` when the series (or
1333
+ * the single occurrence, for a non-recurring invite) has nothing left to
1334
+ * offer at or after that instant.
1335
+ */
1336
+ declare function nextOccurrence(invite: Invite, afterIso: string): string | null;
1337
+ /**
1338
+ * The allow-listed places a bot can actually join, in the order they appear
1339
+ * in the text. Anything else — Webex, a generic link, an unrecognised
1340
+ * dial-in shape — is not returned: a shape this package does not know is a
1341
+ * shape a bot should not guess at.
1342
+ */
1343
+ declare function joinTargets(invite: Invite): JoinTarget[];
1344
+
1259
1345
  /**
1260
1346
  * A run, a position inside it, and how many times that position has been tried.
1261
1347
  *
@@ -1567,18 +1653,6 @@ declare function byteRangeOf(text: string, charIndex: number, substring: string)
1567
1653
  * exception three frames away.
1568
1654
  */
1569
1655
  declare function sliceByteRange(text: string, range: ByteRange): string;
1570
- /**
1571
- * Bare URLs in `text`, with their UTF-8 byte ranges.
1572
- *
1573
- * Trailing punctuation is excluded: a link at the end of a sentence would
1574
- * otherwise swallow the full stop and 404. Closing brackets go too, for the same
1575
- * reason and because a URL in parentheses is common in prose.
1576
- *
1577
- * This is the generic half of a "link facet" — a connector maps the ranges into
1578
- * whatever its provider's rich-text shape is. The package owns the offsets
1579
- * because the offsets are what people get wrong; it does not own the provider's
1580
- * schema, because that is the provider's.
1581
- */
1582
1656
  declare function linkRanges(text: string): Array<ByteRange & {
1583
1657
  url: string;
1584
1658
  }>;
@@ -2603,4 +2677,4 @@ declare function verifyDelivery(trigger: TriggerDescriptor, delivery: InboundDel
2603
2677
  */
2604
2678
  declare function triggerEvent(trigger: TriggerDescriptor, connection: ResolvedConnection, injected: unknown, config: Record<string, unknown>): unknown;
2605
2679
 
2606
- export { AMBIGUOUS_REFUSAL, type ApiContract, type Attempt, 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, type DeliveryDeclaration, type DeliveryMechanism, type DeliveryOutcome, type DriftFinding, type DriftReport, EXPIRES_AT_UNITS, type ExpiresAtUnit, FAKE_EPOCH, type FailureKind, type FakeRequest, type FakeValues, type HmacScheme, type HmacVerificationSpec, type HostEnvironment, type IdempotencyOptions, type InboundDelivery, 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, PROBLEM_SEVERITIES, type PreparedRequest, type ProbeOutcome, type ProbeReport, type ProbeResult, type ProbeSpec, type Problem, type ProviderAdapter, RENDERER_VERSION, REVIEW_STALE_DAYS, type RecordedShape, type RenderRules, type RenderedPayload, type RequestedMode, type ResolveOptions, type ResolvedConnection, type RetryPolicy, type RunIdentity, SANDBOX_KINDS, SUPPORTED_CONNECTOR_API, type SandboxKind, type Segment, type ServiceDescriptor, type SetupStep, type ShapeMismatch, type SharedTokenScheme, type SharedTokenVerificationSpec, type SpecSource, type SubscriptionLease, type TextUnit, type Transport, type TransportResponse, type TriggerDescriptor, type VerifyResult, type WebhookVerification, type WebhookVerificationSpec, assertConnectorApi, byteRangeOf, callConnector, capabilityProblems, checkAgainstOpenApi, checkAgainstRecordedShape, classifyError, classifyHttp, classifyStatus, classifyThrown, compareShape, connectionsFor, constantTimeEquals, deliver, fakeRequest, fakeValues, getConnectionHost, graphemes, handshakeResponse, header, hmac, httpFailure, idempotencyKeyFor, isEmptyPayload, isRemote, isSharedTokenScheme, isSharedTokenSpec, isUnconditionallyRetryable, leaseAction, leaseFromResponse, leaseRenewAt, leaseState, linkRanges, measure, 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, verifySharedToken, withResolvedLimit };
2680
+ 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, type DeliveryDeclaration, type DeliveryMechanism, type DeliveryOutcome, type DriftFinding, type DriftReport, EXPIRES_AT_UNITS, type ExpiresAtUnit, FAKE_EPOCH, type FailureKind, type FakeRequest, type FakeValues, type HmacScheme, type HmacVerificationSpec, type HostEnvironment, type IdempotencyOptions, type InboundDelivery, type Invite, type InviteMethod, type JoinTarget, type JoinTargetKind, 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, PROBLEM_SEVERITIES, type PreparedRequest, type ProbeOutcome, type ProbeReport, type ProbeResult, type ProbeSpec, type Problem, type ProviderAdapter, 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, SUPPORTED_CONNECTOR_API, type SandboxKind, type Segment, type ServiceDescriptor, type SetupStep, type ShapeMismatch, type SharedTokenScheme, type SharedTokenVerificationSpec, type SpecSource, type SubscriptionLease, type TextUnit, type Transport, type TransportResponse, type TriggerDescriptor, type VerifyResult, type WebhookVerification, type WebhookVerificationSpec, assertConnectorApi, byteRangeOf, callConnector, capabilityProblems, checkAgainstOpenApi, checkAgainstRecordedShape, classifyError, classifyHttp, classifyStatus, classifyThrown, compareShape, connectionsFor, constantTimeEquals, deliver, fakeRequest, fakeValues, getConnectionHost, graphemes, handshakeResponse, header, hmac, httpFailure, idempotencyKeyFor, isEmptyPayload, isRemote, isSharedTokenScheme, isSharedTokenSpec, isUnconditionallyRetryable, joinTargets, leaseAction, leaseFromResponse, leaseRenewAt, leaseState, linkRanges, measure, nextOccurrence, 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, verifySharedToken, withResolvedLimit };
package/dist/index.d.ts CHANGED
@@ -1256,6 +1256,92 @@ declare function classifyThrown(cause: unknown, ctx: ConnectorErrorContext): Con
1256
1256
  */
1257
1257
  declare function httpFailure(status: number, body: string, retryAfter?: string | null): Error;
1258
1258
 
1259
+ /**
1260
+ * RFC 5545 in, a structured invite and its join targets out — the read MOIC's
1261
+ * meeting bot needs from nothing but the text a calendar API hands back.
1262
+ *
1263
+ * Written from scratch (the owner: no third-party code), as ONE
1264
+ * implementation in two runtimes — `php/src/Ical.php` is the twin — and held
1265
+ * to the authored corpus under `fixtures/ical/`, which both must reproduce
1266
+ * exactly. `fixtures/ical/README.md` lists every decision a case pins; the
1267
+ * ones easy to get differently in two languages are restated here:
1268
+ *
1269
+ * - Folded lines (a line break followed by a single space or tab) join
1270
+ * before anything else runs; TEXT values are then RFC 5545-unescaped
1271
+ * (`\n`, `\,`, `\;`, `\\`).
1272
+ * - `DTSTART`/`DTEND` always come back as UTC instants. A `Z` suffix passes
1273
+ * through; a `TZID` parameter resolves through the calendar's own
1274
+ * `VTIMEZONE` (an unknown TZID is refused BY NAME, never guessed); a value
1275
+ * with neither — RFC 5545's FLOATING time — is treated as UTC, a decision
1276
+ * named here rather than left to look like an oversight: resolving a
1277
+ * floating time correctly needs the READER's timezone, which nothing in
1278
+ * the invite carries.
1279
+ * - `RRULE` support is intentionally narrow: `FREQ` daily/weekly/monthly/
1280
+ * yearly with `INTERVAL`, `COUNT` and `UNTIL`, and `BYDAY` for WEEKLY only.
1281
+ * No `BYMONTHDAY`, `BYSETPOS`, `BYWEEKNO`, `BYYEARDAY`, `WKST`, `EXDATE` or
1282
+ * `RDATE` — a recurring invite using one of those still parses (its RRULE
1283
+ * fields are read, never dropped), but `nextOccurrence` does not attempt
1284
+ * to walk it.
1285
+ * - Join targets are ALLOW-LISTED: Google Meet, Zoom (with its meeting id
1286
+ * and passcode read out of the URL), Microsoft Teams, and a dial-in phone
1287
+ * number with a passcode read from its own line. Anything else is ignored,
1288
+ * never guessed at — a bot that tried an unknown link shape would attempt
1289
+ * to join something it cannot handle.
1290
+ */
1291
+ type InviteMethod = "REQUEST" | "CANCEL";
1292
+ type Attendee = {
1293
+ name: string | null;
1294
+ email: string;
1295
+ role: string | null;
1296
+ partstat: string | null;
1297
+ };
1298
+ type RecurrenceRule = {
1299
+ freq: "DAILY" | "WEEKLY" | "MONTHLY" | "YEARLY";
1300
+ interval: number;
1301
+ count: number | null;
1302
+ until: string | null;
1303
+ byDay: string[] | null;
1304
+ };
1305
+ type Invite = {
1306
+ method: InviteMethod;
1307
+ uid: string;
1308
+ sequence: number;
1309
+ summary: string | null;
1310
+ description: string | null;
1311
+ location: string | null;
1312
+ status: string | null;
1313
+ dtstart: string;
1314
+ dtend: string | null;
1315
+ organizer: {
1316
+ name: string | null;
1317
+ email: string;
1318
+ } | null;
1319
+ attendees: Attendee[];
1320
+ rrule: RecurrenceRule | null;
1321
+ };
1322
+ type JoinTargetKind = "google-meet" | "zoom" | "teams" | "dial-in";
1323
+ type JoinTarget = {
1324
+ kind: JoinTargetKind;
1325
+ url: string | null;
1326
+ meetingId: string | null;
1327
+ passcode: string | null;
1328
+ phone: string | null;
1329
+ };
1330
+ declare function parseInvite(raw: string): Invite;
1331
+ /**
1332
+ * The next occurrence at or after `afterIso`, or `null` when the series (or
1333
+ * the single occurrence, for a non-recurring invite) has nothing left to
1334
+ * offer at or after that instant.
1335
+ */
1336
+ declare function nextOccurrence(invite: Invite, afterIso: string): string | null;
1337
+ /**
1338
+ * The allow-listed places a bot can actually join, in the order they appear
1339
+ * in the text. Anything else — Webex, a generic link, an unrecognised
1340
+ * dial-in shape — is not returned: a shape this package does not know is a
1341
+ * shape a bot should not guess at.
1342
+ */
1343
+ declare function joinTargets(invite: Invite): JoinTarget[];
1344
+
1259
1345
  /**
1260
1346
  * A run, a position inside it, and how many times that position has been tried.
1261
1347
  *
@@ -1567,18 +1653,6 @@ declare function byteRangeOf(text: string, charIndex: number, substring: string)
1567
1653
  * exception three frames away.
1568
1654
  */
1569
1655
  declare function sliceByteRange(text: string, range: ByteRange): string;
1570
- /**
1571
- * Bare URLs in `text`, with their UTF-8 byte ranges.
1572
- *
1573
- * Trailing punctuation is excluded: a link at the end of a sentence would
1574
- * otherwise swallow the full stop and 404. Closing brackets go too, for the same
1575
- * reason and because a URL in parentheses is common in prose.
1576
- *
1577
- * This is the generic half of a "link facet" — a connector maps the ranges into
1578
- * whatever its provider's rich-text shape is. The package owns the offsets
1579
- * because the offsets are what people get wrong; it does not own the provider's
1580
- * schema, because that is the provider's.
1581
- */
1582
1656
  declare function linkRanges(text: string): Array<ByteRange & {
1583
1657
  url: string;
1584
1658
  }>;
@@ -2603,4 +2677,4 @@ declare function verifyDelivery(trigger: TriggerDescriptor, delivery: InboundDel
2603
2677
  */
2604
2678
  declare function triggerEvent(trigger: TriggerDescriptor, connection: ResolvedConnection, injected: unknown, config: Record<string, unknown>): unknown;
2605
2679
 
2606
- export { AMBIGUOUS_REFUSAL, type ApiContract, type Attempt, 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, type DeliveryDeclaration, type DeliveryMechanism, type DeliveryOutcome, type DriftFinding, type DriftReport, EXPIRES_AT_UNITS, type ExpiresAtUnit, FAKE_EPOCH, type FailureKind, type FakeRequest, type FakeValues, type HmacScheme, type HmacVerificationSpec, type HostEnvironment, type IdempotencyOptions, type InboundDelivery, 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, PROBLEM_SEVERITIES, type PreparedRequest, type ProbeOutcome, type ProbeReport, type ProbeResult, type ProbeSpec, type Problem, type ProviderAdapter, RENDERER_VERSION, REVIEW_STALE_DAYS, type RecordedShape, type RenderRules, type RenderedPayload, type RequestedMode, type ResolveOptions, type ResolvedConnection, type RetryPolicy, type RunIdentity, SANDBOX_KINDS, SUPPORTED_CONNECTOR_API, type SandboxKind, type Segment, type ServiceDescriptor, type SetupStep, type ShapeMismatch, type SharedTokenScheme, type SharedTokenVerificationSpec, type SpecSource, type SubscriptionLease, type TextUnit, type Transport, type TransportResponse, type TriggerDescriptor, type VerifyResult, type WebhookVerification, type WebhookVerificationSpec, assertConnectorApi, byteRangeOf, callConnector, capabilityProblems, checkAgainstOpenApi, checkAgainstRecordedShape, classifyError, classifyHttp, classifyStatus, classifyThrown, compareShape, connectionsFor, constantTimeEquals, deliver, fakeRequest, fakeValues, getConnectionHost, graphemes, handshakeResponse, header, hmac, httpFailure, idempotencyKeyFor, isEmptyPayload, isRemote, isSharedTokenScheme, isSharedTokenSpec, isUnconditionallyRetryable, leaseAction, leaseFromResponse, leaseRenewAt, leaseState, linkRanges, measure, 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, verifySharedToken, withResolvedLimit };
2680
+ 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, type DeliveryDeclaration, type DeliveryMechanism, type DeliveryOutcome, type DriftFinding, type DriftReport, EXPIRES_AT_UNITS, type ExpiresAtUnit, FAKE_EPOCH, type FailureKind, type FakeRequest, type FakeValues, type HmacScheme, type HmacVerificationSpec, type HostEnvironment, type IdempotencyOptions, type InboundDelivery, type Invite, type InviteMethod, type JoinTarget, type JoinTargetKind, 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, PROBLEM_SEVERITIES, type PreparedRequest, type ProbeOutcome, type ProbeReport, type ProbeResult, type ProbeSpec, type Problem, type ProviderAdapter, 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, SUPPORTED_CONNECTOR_API, type SandboxKind, type Segment, type ServiceDescriptor, type SetupStep, type ShapeMismatch, type SharedTokenScheme, type SharedTokenVerificationSpec, type SpecSource, type SubscriptionLease, type TextUnit, type Transport, type TransportResponse, type TriggerDescriptor, type VerifyResult, type WebhookVerification, type WebhookVerificationSpec, assertConnectorApi, byteRangeOf, callConnector, capabilityProblems, checkAgainstOpenApi, checkAgainstRecordedShape, classifyError, classifyHttp, classifyStatus, classifyThrown, compareShape, connectionsFor, constantTimeEquals, deliver, fakeRequest, fakeValues, getConnectionHost, graphemes, handshakeResponse, header, hmac, httpFailure, idempotencyKeyFor, isEmptyPayload, isRemote, isSharedTokenScheme, isSharedTokenSpec, isUnconditionallyRetryable, joinTargets, leaseAction, leaseFromResponse, leaseRenewAt, leaseState, linkRanges, measure, nextOccurrence, 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, verifySharedToken, withResolvedLimit };
package/dist/index.js CHANGED
@@ -860,6 +860,325 @@ function unchecked(contract) {
860
860
  };
861
861
  }
862
862
 
863
+ // src/ical.ts
864
+ function unfold(text) {
865
+ return text.replace(/\r\n|\r|\n/g, "\n").replace(/\n[ \t]/g, "");
866
+ }
867
+ function unescapeText(value) {
868
+ return value.replace(/\\[nN]/g, "\n").replace(/\\,/g, ",").replace(/\\;/g, ";").replace(/\\\\/g, "\\");
869
+ }
870
+ function parseLine(line) {
871
+ const colon = findUnquoted(line, ":");
872
+ const head = colon === -1 ? line : line.slice(0, colon);
873
+ const value = colon === -1 ? "" : line.slice(colon + 1);
874
+ const [name = "", ...paramParts] = head.split(";");
875
+ const params = {};
876
+ for (const part of paramParts) {
877
+ const eq = part.indexOf("=");
878
+ if (eq === -1) continue;
879
+ const key = part.slice(0, eq).toUpperCase();
880
+ let raw = part.slice(eq + 1);
881
+ if (raw.startsWith('"') && raw.endsWith('"')) raw = raw.slice(1, -1);
882
+ params[key] = raw;
883
+ }
884
+ return { name: name.toUpperCase(), params, value };
885
+ }
886
+ function findUnquoted(line, char) {
887
+ let quoted = false;
888
+ for (let i = 0; i < line.length; i++) {
889
+ if (line[i] === '"') quoted = !quoted;
890
+ else if (line[i] === char && !quoted) return i;
891
+ }
892
+ return -1;
893
+ }
894
+ function blockLines(lines, name) {
895
+ const blocks = [];
896
+ let depth = 0;
897
+ let current = null;
898
+ for (const raw of lines) {
899
+ const upper = raw.toUpperCase();
900
+ if (upper === `BEGIN:${name}`) {
901
+ if (depth === 0) current = [];
902
+ depth++;
903
+ continue;
904
+ }
905
+ if (upper === `END:${name}`) {
906
+ depth--;
907
+ if (depth === 0 && current !== null) {
908
+ blocks.push(current);
909
+ current = null;
910
+ }
911
+ continue;
912
+ }
913
+ if (current !== null) current.push(raw);
914
+ }
915
+ return blocks;
916
+ }
917
+ var WEEKDAY_CODES = ["SU", "MO", "TU", "WE", "TH", "FR", "SA"];
918
+ function parseByDayOrdinal(token) {
919
+ const match = /^(-?\d+)([A-Z]{2})$/.exec(token);
920
+ if (!match) throw new Error(`ical: VTIMEZONE rule has an unreadable BYDAY "${token}"`);
921
+ const [, ordinalText, weekdayText] = match;
922
+ const weekday = WEEKDAY_CODES.indexOf(weekdayText ?? "");
923
+ if (weekday === -1) throw new Error(`ical: VTIMEZONE rule names an unknown weekday "${weekdayText}"`);
924
+ return { ordinal: Number(ordinalText), weekday };
925
+ }
926
+ function parseTzTransition(lines) {
927
+ const props = lines.map(parseLine);
928
+ const get = (name) => props.find((p) => p.name === name)?.value;
929
+ const offsetFrom = get("TZOFFSETFROM");
930
+ const offsetTo = get("TZOFFSETTO");
931
+ const rrule = get("RRULE");
932
+ if (!offsetFrom || !offsetTo || !rrule) {
933
+ throw new Error("ical: a VTIMEZONE sub-component needs TZOFFSETFROM, TZOFFSETTO and an RRULE");
934
+ }
935
+ const parts = Object.fromEntries(rrule.split(";").map((p) => p.split("=")));
936
+ const month = Number(parts.BYMONTH);
937
+ const { ordinal, weekday } = parseByDayOrdinal(parts.BYDAY ?? "");
938
+ if (!month || Number.isNaN(ordinal)) {
939
+ throw new Error(`ical: this package only reads a yearly BYMONTH+BYDAY transition rule, got "${rrule}"`);
940
+ }
941
+ return { offsetFrom, offsetTo, month, ordinal, weekday };
942
+ }
943
+ function offsetMinutes(offset) {
944
+ const match = /^([+-])(\d{2}):?(\d{2})/.exec(offset);
945
+ if (!match) throw new Error(`ical: unreadable UTC offset "${offset}"`);
946
+ const sign = match[1] === "-" ? -1 : 1;
947
+ return sign * (Number(match[2]) * 60 + Number(match[3]));
948
+ }
949
+ function nthWeekdayOfMonth(year, month1, weekday, ordinal) {
950
+ if (ordinal > 0) {
951
+ const first = Date.UTC(year, month1 - 1, 1);
952
+ const firstWeekday = new Date(first).getUTCDay();
953
+ const offset2 = (weekday - firstWeekday + 7) % 7;
954
+ return 1 + offset2 + (ordinal - 1) * 7;
955
+ }
956
+ const lastDay = new Date(Date.UTC(year, month1, 0)).getUTCDate();
957
+ const last = Date.UTC(year, month1 - 1, lastDay);
958
+ const lastWeekday = new Date(last).getUTCDay();
959
+ const offset = (lastWeekday - weekday + 7) % 7;
960
+ return lastDay - offset - (-ordinal - 1) * 7;
961
+ }
962
+ function parseTimezones(lines) {
963
+ const zones = /* @__PURE__ */ new Map();
964
+ for (const block of blockLines(lines, "VTIMEZONE")) {
965
+ const tzid = block.map(parseLine).find((p) => p.name === "TZID")?.value;
966
+ if (!tzid) continue;
967
+ const standardBlocks = blockLines(block, "STANDARD");
968
+ const daylightBlocks = blockLines(block, "DAYLIGHT");
969
+ const standardBlock = standardBlocks[0];
970
+ if (!standardBlock) {
971
+ throw new Error(`ical: VTIMEZONE "${tzid}" has no STANDARD component`);
972
+ }
973
+ zones.set(tzid, {
974
+ standard: parseTzTransition(standardBlock),
975
+ daylight: daylightBlocks[0] ? parseTzTransition(daylightBlocks[0]) : null
976
+ });
977
+ }
978
+ return zones;
979
+ }
980
+ function resolveTz(zone, y, mo, d, h, mi, s) {
981
+ const naiveUtcMs = Date.UTC(y, mo - 1, d, h, mi, s);
982
+ if (!zone.daylight) {
983
+ return new Date(naiveUtcMs - offsetMinutes(zone.standard.offsetTo) * 6e4).toISOString();
984
+ }
985
+ const springDay = nthWeekdayOfMonth(y, zone.daylight.month, zone.daylight.weekday, zone.daylight.ordinal);
986
+ const fallDay = nthWeekdayOfMonth(y, zone.standard.month, zone.standard.weekday, zone.standard.ordinal);
987
+ const spring = Date.UTC(y, zone.daylight.month - 1, springDay);
988
+ const fall = Date.UTC(y, zone.standard.month - 1, fallDay);
989
+ const inDaylight = naiveUtcMs >= spring && naiveUtcMs < fall;
990
+ const offset = inDaylight ? zone.daylight.offsetTo : zone.standard.offsetTo;
991
+ return new Date(naiveUtcMs - offsetMinutes(offset) * 6e4).toISOString();
992
+ }
993
+ function parseDateTimeParts(value) {
994
+ const match = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z)?$/.exec(value);
995
+ if (!match) throw new Error(`ical: unreadable date-time "${value}"`);
996
+ return {
997
+ y: Number(match[1]),
998
+ mo: Number(match[2]),
999
+ d: Number(match[3]),
1000
+ h: Number(match[4]),
1001
+ mi: Number(match[5]),
1002
+ s: Number(match[6]),
1003
+ utc: Boolean(match[7])
1004
+ };
1005
+ }
1006
+ function resolveInstant(prop, zones) {
1007
+ const parts = parseDateTimeParts(prop.value);
1008
+ if (parts.utc) return new Date(Date.UTC(parts.y, parts.mo - 1, parts.d, parts.h, parts.mi, parts.s)).toISOString();
1009
+ const tzid = prop.params.TZID;
1010
+ if (!tzid) {
1011
+ return new Date(Date.UTC(parts.y, parts.mo - 1, parts.d, parts.h, parts.mi, parts.s)).toISOString();
1012
+ }
1013
+ const zone = zones.get(tzid);
1014
+ if (!zone) {
1015
+ throw new Error(`ical: DTSTART/DTEND names TZID "${tzid}", which this calendar's VTIMEZONE does not define`);
1016
+ }
1017
+ return resolveTz(zone, parts.y, parts.mo, parts.d, parts.h, parts.mi, parts.s);
1018
+ }
1019
+ function emailOf(value) {
1020
+ return value.replace(/^mailto:/i, "");
1021
+ }
1022
+ function parseAttendee(prop) {
1023
+ return {
1024
+ name: prop.params.CN ?? null,
1025
+ email: emailOf(prop.value),
1026
+ role: prop.params.ROLE ?? null,
1027
+ partstat: prop.params.PARTSTAT ?? null
1028
+ };
1029
+ }
1030
+ var FREQS = ["DAILY", "WEEKLY", "MONTHLY", "YEARLY"];
1031
+ function parseRRule(value) {
1032
+ const parts = Object.fromEntries(
1033
+ value.split(";").map((p) => {
1034
+ const eq = p.indexOf("=");
1035
+ return [p.slice(0, eq).toUpperCase(), p.slice(eq + 1)];
1036
+ })
1037
+ );
1038
+ const freq = parts.FREQ;
1039
+ if (!FREQS.includes(freq)) {
1040
+ throw new Error(`ical: RRULE FREQ must be one of ${FREQS.join(", ")}, got "${parts.FREQ}"`);
1041
+ }
1042
+ return {
1043
+ freq,
1044
+ interval: parts.INTERVAL ? Number(parts.INTERVAL) : 1,
1045
+ count: parts.COUNT ? Number(parts.COUNT) : null,
1046
+ until: parts.UNTIL ? resolveInstant({ name: "UNTIL", params: {}, value: parts.UNTIL }, /* @__PURE__ */ new Map()) : null,
1047
+ byDay: parts.BYDAY ? parts.BYDAY.split(",") : null
1048
+ };
1049
+ }
1050
+ function parseInvite(raw) {
1051
+ const lines = unfold(raw).split("\n").filter((l) => l.length > 0);
1052
+ const zones = parseTimezones(lines);
1053
+ const eventBlocks = blockLines(lines, "VEVENT");
1054
+ const firstEvent = eventBlocks[0];
1055
+ if (!firstEvent) throw new Error("ical: no VEVENT in this calendar");
1056
+ const props = firstEvent.map(parseLine);
1057
+ const methodValue = (lines.find((l) => l.toUpperCase().startsWith("METHOD:")) ?? "").split(":")[1];
1058
+ const get = (name) => props.find((p) => p.name === name);
1059
+ const getAll = (name) => props.filter((p) => p.name === name);
1060
+ const text = (name) => {
1061
+ const p = get(name);
1062
+ return p ? unescapeText(p.value) : null;
1063
+ };
1064
+ const method = (methodValue ?? "REQUEST").trim().toUpperCase();
1065
+ if (method !== "REQUEST" && method !== "CANCEL") {
1066
+ throw new Error(`ical: METHOD must be REQUEST or CANCEL, got "${methodValue}"`);
1067
+ }
1068
+ const uidProp = get("UID");
1069
+ if (!uidProp) throw new Error("ical: VEVENT has no UID");
1070
+ const dtstartProp = get("DTSTART");
1071
+ if (!dtstartProp) throw new Error("ical: VEVENT has no DTSTART");
1072
+ const dtendProp = get("DTEND");
1073
+ const organizerProp = get("ORGANIZER");
1074
+ const rruleProp = get("RRULE");
1075
+ return {
1076
+ method,
1077
+ uid: uidProp.value,
1078
+ sequence: get("SEQUENCE") ? Number(get("SEQUENCE").value) : 0,
1079
+ summary: text("SUMMARY"),
1080
+ description: text("DESCRIPTION"),
1081
+ location: text("LOCATION"),
1082
+ status: get("STATUS")?.value ?? null,
1083
+ dtstart: resolveInstant(dtstartProp, zones),
1084
+ dtend: dtendProp ? resolveInstant(dtendProp, zones) : null,
1085
+ organizer: organizerProp ? { name: organizerProp.params.CN ?? null, email: emailOf(organizerProp.value) } : null,
1086
+ attendees: getAll("ATTENDEE").map(parseAttendee),
1087
+ rrule: rruleProp ? parseRRule(rruleProp.value) : null
1088
+ };
1089
+ }
1090
+ var DAY_MS = 864e5;
1091
+ function addCalendarUnit(ms, freq, interval) {
1092
+ const d = new Date(ms);
1093
+ if (freq === "DAILY") return ms + interval * DAY_MS;
1094
+ if (freq === "WEEKLY") return ms + interval * 7 * DAY_MS;
1095
+ const months = freq === "YEARLY" ? interval * 12 : interval;
1096
+ const day = d.getUTCDate();
1097
+ const next = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + months, 1, d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds()));
1098
+ const daysInNext = new Date(Date.UTC(next.getUTCFullYear(), next.getUTCMonth() + 1, 0)).getUTCDate();
1099
+ next.setUTCDate(Math.min(day, daysInNext));
1100
+ return next.getTime();
1101
+ }
1102
+ function* occurrences(rrule, dtstartMs) {
1103
+ const untilMs = rrule.until ? Date.parse(rrule.until) : null;
1104
+ let n = 0;
1105
+ const emit = (ms) => {
1106
+ if (untilMs !== null && ms > untilMs) return false;
1107
+ n++;
1108
+ return true;
1109
+ };
1110
+ if (rrule.freq === "WEEKLY" && rrule.byDay?.length) {
1111
+ const wanted = [...new Set(rrule.byDay.map((d) => WEEKDAY_CODES.indexOf(d)))].sort((a, b) => a - b);
1112
+ const dtstart = new Date(dtstartMs);
1113
+ const timeOfDay = dtstartMs - Date.UTC(dtstart.getUTCFullYear(), dtstart.getUTCMonth(), dtstart.getUTCDate());
1114
+ const startOfWeek = dtstartMs - timeOfDay - dtstart.getUTCDay() * DAY_MS;
1115
+ let weekIndex = 0;
1116
+ while (true) {
1117
+ if (weekIndex % rrule.interval === 0) {
1118
+ for (const weekday of wanted) {
1119
+ const candidate = startOfWeek + weekIndex * 7 * DAY_MS + weekday * DAY_MS + timeOfDay;
1120
+ if (candidate < dtstartMs) continue;
1121
+ if (!emit(candidate)) return;
1122
+ yield candidate;
1123
+ if (rrule.count && n >= rrule.count) return;
1124
+ }
1125
+ }
1126
+ weekIndex++;
1127
+ }
1128
+ }
1129
+ let current = dtstartMs;
1130
+ while (true) {
1131
+ if (!emit(current)) return;
1132
+ yield current;
1133
+ if (rrule.count && n >= rrule.count) return;
1134
+ current = addCalendarUnit(current, rrule.freq, rrule.interval);
1135
+ }
1136
+ }
1137
+ function nextOccurrence(invite, afterIso) {
1138
+ const afterMs = Date.parse(afterIso);
1139
+ const dtstartMs = Date.parse(invite.dtstart);
1140
+ if (!invite.rrule) {
1141
+ return dtstartMs >= afterMs ? invite.dtstart : null;
1142
+ }
1143
+ for (const ms of occurrences(invite.rrule, dtstartMs)) {
1144
+ if (ms >= afterMs) return new Date(ms).toISOString();
1145
+ }
1146
+ return null;
1147
+ }
1148
+ var ZOOM_LINK = /https:\/\/[\w.-]*zoom\.us\/j\/(\d+)(\?[^\s)]*)?/g;
1149
+ var MEET_LINK = /https:\/\/meet\.google\.com\/[a-z0-9-]+/gi;
1150
+ var TEAMS_LINK = /https:\/\/teams\.(?:microsoft|live)\.com\/l\/meetup-join\/[^\s)]+/gi;
1151
+ var DIAL_LINE = /^Dial(?:-in)?[:\s]+([+0-9][0-9\s().-]{6,}\d)/im;
1152
+ var PASSCODE_LINE = /Passcode[:\s]+([A-Za-z0-9]+)/i;
1153
+ function joinTargets(invite) {
1154
+ const text = invite.description ?? "";
1155
+ const targets = [];
1156
+ for (const match of text.matchAll(ZOOM_LINK)) {
1157
+ const query = match[2] ?? "";
1158
+ const passcode = /[?&]pwd=([^&\s]+)/.exec(query)?.[1] ?? null;
1159
+ targets.push({
1160
+ index: match.index ?? 0,
1161
+ target: { kind: "zoom", url: match[0], meetingId: match[1] ?? null, passcode, phone: null }
1162
+ });
1163
+ }
1164
+ for (const match of text.matchAll(MEET_LINK)) {
1165
+ targets.push({ index: match.index ?? 0, target: { kind: "google-meet", url: match[0], meetingId: null, passcode: null, phone: null } });
1166
+ }
1167
+ for (const match of text.matchAll(TEAMS_LINK)) {
1168
+ targets.push({ index: match.index ?? 0, target: { kind: "teams", url: match[0], meetingId: null, passcode: null, phone: null } });
1169
+ }
1170
+ const dial = DIAL_LINE.exec(text);
1171
+ const dialedPhone = dial?.[1];
1172
+ if (dial && dialedPhone !== void 0) {
1173
+ const passcode = PASSCODE_LINE.exec(text)?.[1] ?? null;
1174
+ targets.push({
1175
+ index: dial.index,
1176
+ target: { kind: "dial-in", url: null, meetingId: null, passcode, phone: dialedPhone.trim() }
1177
+ });
1178
+ }
1179
+ return targets.sort((a, b) => a.index - b.index).map((t) => t.target);
1180
+ }
1181
+
863
1182
  // src/idempotency.ts
864
1183
  var DEFAULT_IDEMPOTENCY_WINDOW_SECONDS = 24 * 60 * 60;
865
1184
  var MAX_IDEMPOTENCY_KEY_LENGTH = 255;
@@ -1424,11 +1743,17 @@ function sliceByteRange(text, range) {
1424
1743
  const bytes = encoder.encode(text);
1425
1744
  return new TextDecoder().decode(bytes.slice(range.byteStart, range.byteEnd));
1426
1745
  }
1746
+ var TRAILING_PUNCTUATION = /* @__PURE__ */ new Set([".", ",", ";", ":", "!", "?", ")", "]", "}", "'", '"']);
1747
+ function trimTrailingPunctuation(raw) {
1748
+ let end = raw.length;
1749
+ while (end > 0 && TRAILING_PUNCTUATION.has(raw[end - 1])) end -= 1;
1750
+ return raw.slice(0, end);
1751
+ }
1427
1752
  function linkRanges(text) {
1428
1753
  const found = [];
1429
1754
  for (const match of text.matchAll(/https?:\/\/[^\s]+/g)) {
1430
1755
  if (match.index === void 0) continue;
1431
- const url = match[0].replace(/[.,;:!?)\]}'"]+$/, "");
1756
+ const url = trimTrailingPunctuation(match[0]);
1432
1757
  found.push({ ...byteRangeOf(text, match.index, url), url });
1433
1758
  }
1434
1759
  return found;
@@ -1799,12 +2124,15 @@ export {
1799
2124
  isSharedTokenScheme,
1800
2125
  isSharedTokenSpec,
1801
2126
  isUnconditionallyRetryable,
2127
+ joinTargets,
1802
2128
  leaseAction,
1803
2129
  leaseFromResponse,
1804
2130
  leaseRenewAt,
1805
2131
  leaseState,
1806
2132
  linkRanges,
1807
2133
  measure,
2134
+ nextOccurrence,
2135
+ parseInvite,
1808
2136
  parseMime,
1809
2137
  payloadHash,
1810
2138
  postChain,