@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/CHANGELOG.md +64 -0
- package/dist/index.cjs +332 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -13
- package/dist/index.d.ts +87 -13
- package/dist/index.js +329 -1
- package/dist/index.js.map +1 -1
- package/package.json +69 -69
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]
|
|
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,
|