@happyvertical/smrt-events 0.53.4 → 0.53.6
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/README.md +77 -0
- package/dist/calendar/icalendar.d.ts +53 -0
- package/dist/calendar/icalendar.d.ts.map +1 -0
- package/dist/calendar/sync.d.ts +24 -0
- package/dist/calendar/sync.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +860 -395
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/smrt-knowledge.json +4 -3
- package/package.json +15 -14
package/README.md
CHANGED
|
@@ -184,3 +184,80 @@ Owned asset helpers are available on both `Event` and `EventCollection` via
|
|
|
184
184
|
| `@happyvertical/smrt-types` | Shared TypeScript types |
|
|
185
185
|
| `@happyvertical/sql` | Database operations |
|
|
186
186
|
| `@happyvertical/ai` | AI integration |
|
|
187
|
+
|
|
188
|
+
### iCalendar source synchronization
|
|
189
|
+
|
|
190
|
+
`parseICalendarEvents(source, ics, { floatingTimeZone?, limits? })` validates
|
|
191
|
+
caller-supplied RFC 5545 text through the published `@happyvertical/icalendar`
|
|
192
|
+
facade. `expandICalendarEvents(source, ics, options)` adds a required inclusive
|
|
193
|
+
`rangeStart`/`rangeEnd` window. `syncICalendarSource({ db, source, ics, tenantId?,
|
|
194
|
+
parse?, expansion? })` persists those records and returns `created`, `updated`,
|
|
195
|
+
`unchanged`, and `retainedOnOmission: true`. Recurring feeds require `expansion`.
|
|
196
|
+
Parsing/expansion require no database or network; sync supports SQLite and
|
|
197
|
+
PostgreSQL with migrated Event/EventSeries schemas. Other dialects are not
|
|
198
|
+
currently supported ([EventSeries FK gap #3469](https://github.com/happyvertical/smrt/issues/3469)); runtime-schema adapters are rejected before mutation.
|
|
199
|
+
|
|
200
|
+
A tenant, source and UID identify a series. Its expanded occurrences use the
|
|
201
|
+
original RECURRENCE-ID (UTC ISO instant for timed events, calendar date for
|
|
202
|
+
all-day events), so a moved instance retains its ID. Non-recurring records use
|
|
203
|
+
UID without an occurrence suffix. Recurring masters live in series/provenance;
|
|
204
|
+
they are not duplicate Event rows alongside their first occurrence. RDATE is
|
|
205
|
+
normalized using each RDATE property's own timezone and unioned with RRULE,
|
|
206
|
+
duplicate instants collapse, and EXDATE slots persist as
|
|
207
|
+
cancelled events. Detached records override their original slot, including a
|
|
208
|
+
move beyond the requested window. Duplicate VEVENT identities reject the feed.
|
|
209
|
+
|
|
210
|
+
Daily, weekly, monthly and yearly RRULEs use the SDK's public recurrence engine.
|
|
211
|
+
The supported bounded subset is daily without BY filters; weekly BYDAY without
|
|
212
|
+
ordinals; monthly BYDAY or BYMONTHDAY (not both), with BYSETPOS supported only for BYDAY; and yearly
|
|
213
|
+
BYMONTH/BYMONTHDAY. Other BY combinations reject rather than risking unbounded
|
|
214
|
+
search inside the recurrence engine. COUNT, UNTIL and INTERVAL apply throughout.
|
|
215
|
+
Expansion rejects rather than truncates when it exceeds `limit` (default 1,000;
|
|
216
|
+
maximum 10,000) or `maxIterations` (default 10,000; maximum 100,000, including
|
|
217
|
+
slots before the window). Unsupported frequency, unknown recurrence properties,
|
|
218
|
+
multiple RRULEs, EXRULE, DURATION, RDATE PERIOD and RECURRENCE-ID RANGE reject.
|
|
219
|
+
SDK parser resource limits remain in force. Expand a practical window near the
|
|
220
|
+
series start; very old or dense feeds may need a deliberate higher scan budget.
|
|
221
|
+
|
|
222
|
+
IANA zones use the runtime's timezone database and the framework's wall-time
|
|
223
|
+
conversion: DST gaps move forward and overlaps choose the first instant. UTC
|
|
224
|
+
values stay UTC. Floating timed values require `parse.floatingTimeZone` (or the
|
|
225
|
+
same option directly on parse/expand); no host zone is inferred. All-day DATEs
|
|
226
|
+
use that policy or UTC, with exclusive DTEND and a default next-day end. A timed
|
|
227
|
+
event without DTEND has zero duration. Timed recurrence keeps the exact elapsed
|
|
228
|
+
duration of DTSTART to DTEND; all-day recurrence keeps calendar-day duration
|
|
229
|
+
across DST. Date-only recurrence remains date-only.
|
|
230
|
+
|
|
231
|
+
A higher SEQUENCE **or** a newer DTSTAMP accepts an update; equal/older versions
|
|
232
|
+
are unchanged. Missing SEQUENCE is zero and missing DTSTAMP is the oldest
|
|
233
|
+
stamp. A producer changing payload without either version marker is deliberately
|
|
234
|
+
ignored. Each series retains the authoritative master's SDK-serialized document
|
|
235
|
+
and version, including when a requested window contains no occurrences. Later
|
|
236
|
+
windows expand that retained document when the supplied master is stale or equal,
|
|
237
|
+
so unseen slots cannot be created from older payloads or recurrence rules. The
|
|
238
|
+
master document is written in the same transaction as occurrence updates. The
|
|
239
|
+
expansion requirement also applies to the retained document: a stale minimal
|
|
240
|
+
cancellation that resolves to a recurring master requires a window. Without one,
|
|
241
|
+
sync rejects and rolls back all writes; retry with bounded expansion.
|
|
242
|
+
A cancellation is an update, and a newer scheduled record restores it.
|
|
243
|
+
A minimal master cancellation (UID/STATUS/version without DTSTART) cancels known
|
|
244
|
+
series events and stores a series tombstone that blocks older resurrection.
|
|
245
|
+
No feed omission deletes events: outside-window records remain; omitted detached
|
|
246
|
+
exceptions stay authoritative until that detached identity reappears with a
|
|
247
|
+
newer version. A newer master removing EXDATE restores that generated slot.
|
|
248
|
+
|
|
249
|
+
Every sync resolves its database configuration once and performs all writes on
|
|
250
|
+
one transaction executor, including series creation. Failures propagate and
|
|
251
|
+
roll back; callers may retry the same feed. An existing SQLite/PostgreSQL
|
|
252
|
+
transaction is joined through an adapter savepoint, preserving caller rollback.
|
|
253
|
+
The caller must authorize tenant/source access and serialize concurrent syncs
|
|
254
|
+
for the same tenant/source; this API does not implement scheduling or locking.
|
|
255
|
+
An active tenant context supplies an omitted tenantId and rejects a conflicting
|
|
256
|
+
explicit tenantId, including a request for global rows. System contexts may
|
|
257
|
+
explicitly import a tenant or global source.
|
|
258
|
+
|
|
259
|
+
Source fetching (including conditional requests, unavailable-feed detection and
|
|
260
|
+
retry scheduling) remains caller-owned. Pass only a successfully fetched feed;
|
|
261
|
+
partial feeds are safe under omission retention. Sync performs no drafting or
|
|
262
|
+
publication. Event metadata records UID, occurrence ID, version markers,
|
|
263
|
+
RRULE/EXDATE/RDATE and whether the record is detached.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type ICalendarParseLimits } from '@happyvertical/icalendar';
|
|
2
|
+
/** A stable upstream identity for a calendar master or detached occurrence. */
|
|
3
|
+
export interface CalendarEventIdentity {
|
|
4
|
+
source: string;
|
|
5
|
+
uid: string;
|
|
6
|
+
recurrenceId?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Parsed data SMRT needs before applying its persistence policy. */
|
|
9
|
+
export interface CalendarParseOptions {
|
|
10
|
+
floatingTimeZone?: string;
|
|
11
|
+
limits?: ICalendarParseLimits;
|
|
12
|
+
}
|
|
13
|
+
export interface CalendarEventInput {
|
|
14
|
+
identity: CalendarEventIdentity;
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
startDate: Date | null;
|
|
18
|
+
endDate: Date | null;
|
|
19
|
+
allDay: boolean;
|
|
20
|
+
timeZone: string;
|
|
21
|
+
status: 'scheduled' | 'cancelled';
|
|
22
|
+
sequence: number | null;
|
|
23
|
+
dtstamp: Date | null;
|
|
24
|
+
recurrence: string | null;
|
|
25
|
+
/** True for a detached record, false for a generated occurrence. */
|
|
26
|
+
detached?: boolean;
|
|
27
|
+
excludedRecurrenceIds: string[];
|
|
28
|
+
additionalRecurrenceIds: string[];
|
|
29
|
+
}
|
|
30
|
+
/** A malformed calendar event cannot be given a stable source identity. */
|
|
31
|
+
export declare class CalendarEventValidationError extends Error {
|
|
32
|
+
constructor(message: string);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Parse VEVENT records with their RFC 5545 source identities and recurrence
|
|
36
|
+
* exception data. This does not fetch a feed or persist anything.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseICalendarEvents(source: string, ics: string, options?: CalendarParseOptions): CalendarEventInput[];
|
|
39
|
+
export interface CalendarExpansionOptions extends CalendarParseOptions {
|
|
40
|
+
rangeStart: Date;
|
|
41
|
+
rangeEnd: Date;
|
|
42
|
+
/** Hard cap: exceeding it rejects the feed, never returns a partial success. */
|
|
43
|
+
limit?: number;
|
|
44
|
+
/** Includes occurrences scanned before the requested window. */
|
|
45
|
+
maxIterations?: number;
|
|
46
|
+
}
|
|
47
|
+
/** Expand a caller-bounded window through the SDK's public recurrence engine.
|
|
48
|
+
* Range endpoints are inclusive. Detached records replace their original slot;
|
|
49
|
+
* EXDATE slots are returned as cancellations so previously persisted occurrences
|
|
50
|
+
* are cancelled as well. No master is returned alongside its first occurrence.
|
|
51
|
+
*/
|
|
52
|
+
export declare function expandICalendarEvents(source: string, ics: string, options: CalendarExpansionOptions): CalendarEventInput[];
|
|
53
|
+
//# sourceMappingURL=icalendar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icalendar.d.ts","sourceRoot":"","sources":["../../src/calendar/icalendar.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,oBAAoB,EAE1B,MAAM,0BAA0B,CAAC;AAOlC,+EAA+E;AAC/E,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,qBAAqB,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,WAAW,GAAG,WAAW,CAAC;IAClC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,uBAAuB,EAAE,MAAM,EAAE,CAAC;CACnC;AAED,2EAA2E;AAC3E,qBAAa,4BAA6B,SAAQ,KAAK;gBACzC,OAAO,EAAE,MAAM;CAI5B;AAsED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,oBAAyB,GACjC,kBAAkB,EAAE,CAkGtB;AAyBD,MAAM,WAAW,wBAAyB,SAAQ,oBAAoB;IACpE,UAAU,EAAE,IAAI,CAAC;IACjB,QAAQ,EAAE,IAAI,CAAC;IACf,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,wBAAwB,GAChC,kBAAkB,EAAE,CAqOtB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { getDatabase } from '@happyvertical/sql';
|
|
3
|
+
import { type CalendarExpansionOptions, type CalendarParseOptions } from './icalendar';
|
|
4
|
+
export interface CalendarSourceSyncOptions extends Omit<SmrtClassOptions, 'db'> {
|
|
5
|
+
db: NonNullable<Parameters<typeof getDatabase>[0]>;
|
|
6
|
+
source: string;
|
|
7
|
+
ics: string;
|
|
8
|
+
tenantId?: string | null;
|
|
9
|
+
parse?: CalendarParseOptions;
|
|
10
|
+
expansion?: Omit<CalendarExpansionOptions, keyof CalendarParseOptions>;
|
|
11
|
+
}
|
|
12
|
+
export interface CalendarSourceSyncResult {
|
|
13
|
+
created: number;
|
|
14
|
+
updated: number;
|
|
15
|
+
unchanged: number;
|
|
16
|
+
/** Missing feed entries are deliberately retained; sync never deletes by omission. */
|
|
17
|
+
retainedOnOmission: true;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Persist one already-fetched calendar feed. The caller owns fetch, retries and
|
|
21
|
+
* authorization; every write runs through one required database transaction. Feed omission never deletes an existing event.
|
|
22
|
+
*/
|
|
23
|
+
export declare function syncICalendarSource(options: CalendarSourceSyncOptions): Promise<CalendarSourceSyncResult>;
|
|
24
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/calendar/sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAGjE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIjD,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EAG1B,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,yBACf,SAAQ,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC;IACpC,EAAE,EAAE,WAAW,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,SAAS,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,MAAM,oBAAoB,CAAC,CAAC;CACxE;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,kBAAkB,EAAE,IAAI,CAAC;CAC1B;AAsTD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,wBAAwB,CAAC,CA4BnC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
* @packageDocumentation
|
|
7
7
|
*/
|
|
8
8
|
import './__smrt-register__.js';
|
|
9
|
+
export type { CalendarEventIdentity, CalendarEventInput, CalendarExpansionOptions, CalendarParseOptions, } from './calendar/icalendar';
|
|
10
|
+
export { CalendarEventValidationError, expandICalendarEvents, parseICalendarEvents, } from './calendar/icalendar';
|
|
11
|
+
export type { CalendarSourceSyncOptions, CalendarSourceSyncResult, } from './calendar/sync';
|
|
12
|
+
export { syncICalendarSource } from './calendar/sync';
|
|
9
13
|
export { EventAssetCollection } from './collections/EventAssetCollection';
|
|
10
14
|
export { EventCollection } from './collections/EventCollection';
|
|
11
15
|
export { EventParticipantCollection } from './collections/EventParticipantCollection';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,wBAAwB,CAAC;AAEhC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAE5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,YAAY,EACV,SAAS,EACT,uBAAuB,EACvB,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,YAAY,EACZ,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAE3D,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,uBAAuB,EACvB,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,GACpB,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,wBAAwB,CAAC;AAEhC,YAAY,EACV,qBAAqB,EACrB,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,4BAA4B,EAC5B,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAE5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,YAAY,EACV,SAAS,EACT,uBAAuB,EACvB,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,YAAY,EACZ,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAE3D,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,uBAAuB,EACvB,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,GACpB,MAAM,SAAS,CAAC"}
|