@procedures/ics 0.27.8 → 0.28.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 +31 -0
- package/README.md +17 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/module.d.ts +11 -0
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +15 -1
- package/dist/module.js.map +1 -1
- package/dist/procedures.d.ts +18 -0
- package/dist/procedures.d.ts.map +1 -1
- package/dist/procedures.js +37 -15
- package/dist/procedures.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -1
- package/dist/write.d.ts +9 -1
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +31 -4
- package/dist/write.js.map +1 -1
- package/package.json +2 -2
- package/src/module.ts +15 -1
- package/src/procedures.ts +47 -15
- package/src/types.ts +3 -1
- package/src/write.ts +38 -5
package/dist/module.d.ts
CHANGED
|
@@ -2,8 +2,19 @@
|
|
|
2
2
|
* The procedures of @procedures/ics as data. Importing this file registers nothing.
|
|
3
3
|
*/
|
|
4
4
|
import { type ProcedureModule, type ProcedureRegistry } from "@procedures/client/browser";
|
|
5
|
+
import { type IcsProceduresOptions } from "./procedures.js";
|
|
5
6
|
export declare const icsModule: ProcedureModule;
|
|
6
7
|
export default icsModule;
|
|
8
|
+
/**
|
|
9
|
+
* This function gives the module with the settings of the host: the `fetch` of `ics.fetch`.
|
|
10
|
+
* Without options, it gives `icsModule`. A Node host that takes calls from others gives
|
|
11
|
+
* `createHostFetch()` of `@procedures/http`, so the host policy examines each feed address.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* createRegistry(createIcsModule({ fetch: createHostFetch() }));
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function createIcsModule(options?: IcsProceduresOptions): ProcedureModule;
|
|
7
18
|
/**
|
|
8
19
|
* This function registers the procedures of @procedures/ics in a registry. The default is the
|
|
9
20
|
* global registry.
|
package/dist/module.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAqD,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAqD,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC7I,OAAO,EAAsC,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEhG,eAAO,MAAM,SAAS,EAAE,eAAgE,CAAC;eAE1E,SAAS;AAExB;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,oBAAyB,GAAG,eAAe,CAGnF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,GAAE,iBAAsC,GAAG,IAAI,CAE5F"}
|
package/dist/module.js
CHANGED
|
@@ -2,9 +2,23 @@
|
|
|
2
2
|
* The procedures of @procedures/ics as data. Importing this file registers nothing.
|
|
3
3
|
*/
|
|
4
4
|
import { defineModule, registerModules, PROCEDURE_REGISTRY } from "@procedures/client/browser";
|
|
5
|
-
import { icsProcedures } from "./procedures.js";
|
|
5
|
+
import { createIcsProcedures, icsProcedures } from "./procedures.js";
|
|
6
6
|
export const icsModule = defineModule("@procedures/ics", icsProcedures);
|
|
7
7
|
export default icsModule;
|
|
8
|
+
/**
|
|
9
|
+
* This function gives the module with the settings of the host: the `fetch` of `ics.fetch`.
|
|
10
|
+
* Without options, it gives `icsModule`. A Node host that takes calls from others gives
|
|
11
|
+
* `createHostFetch()` of `@procedures/http`, so the host policy examines each feed address.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* createRegistry(createIcsModule({ fetch: createHostFetch() }));
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function createIcsModule(options = {}) {
|
|
18
|
+
if (options.fetch === undefined)
|
|
19
|
+
return icsModule;
|
|
20
|
+
return defineModule("@procedures/ics", createIcsProcedures(options));
|
|
21
|
+
}
|
|
8
22
|
/**
|
|
9
23
|
* This function registers the procedures of @procedures/ics in a registry. The default is the
|
|
10
24
|
* global registry.
|
package/dist/module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAgD,MAAM,4BAA4B,CAAC;AAC7I,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAgD,MAAM,4BAA4B,CAAC;AAC7I,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAA6B,MAAM,iBAAiB,CAAC;AAEhG,MAAM,CAAC,MAAM,SAAS,GAAoB,YAAY,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;AAEzF,eAAe,SAAS,CAAC;AAEzB;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,OAAO,GAAyB,EAAE;IAChE,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAClD,OAAO,YAAY,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;AACvE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAQ,GAAsB,kBAAkB;IACpF,eAAe,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;AACzC,CAAC"}
|
package/dist/procedures.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The procedures of `@procedures/ics`: `ics.parse`, `ics.occurrences`, `ics.write` and `ics.fetch`.
|
|
3
|
+
*
|
|
4
|
+
* `ics.fetch` sends its request with the `fetch` of the module. The default is the global
|
|
5
|
+
* `fetch`, so the root stays safe for a browser. A Node host that takes calls from others gives
|
|
6
|
+
* `createHostFetch()` of `@procedures/http` to `createIcsModule()`. Then the host policy refuses a
|
|
7
|
+
* feed on a loopback, private or link-local address (audit pass 2, O55).
|
|
3
8
|
*/
|
|
4
9
|
import { type AnyProcedure } from "@procedures/client/browser";
|
|
5
10
|
export declare const icsParseProcedure: AnyProcedure;
|
|
@@ -22,6 +27,19 @@ export type FetchText = (url: string, init: {
|
|
|
22
27
|
export declare function fetchCalendar(url: string, timeoutMs: number, signal?: AbortSignal, options?: {
|
|
23
28
|
readonly fetch?: FetchText | undefined;
|
|
24
29
|
}): Promise<string>;
|
|
30
|
+
/** The settings of the host for the procedures of this package. The procedure input cannot change them. */
|
|
31
|
+
export interface IcsProceduresOptions {
|
|
32
|
+
/**
|
|
33
|
+
* The function that sends the request of `ics.fetch`. The default is the global `fetch`. A Node
|
|
34
|
+
* host gives `createHostFetch()` of `@procedures/http`.
|
|
35
|
+
*/
|
|
36
|
+
readonly fetch?: FetchText | undefined;
|
|
37
|
+
}
|
|
25
38
|
export declare const icsFetchProcedure: AnyProcedure;
|
|
26
39
|
export declare const icsProcedures: AnyProcedure[];
|
|
40
|
+
/**
|
|
41
|
+
* This function gives the procedures of this package with the settings of the host. Without a
|
|
42
|
+
* `fetch`, it gives `icsProcedures`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createIcsProcedures(options?: IcsProceduresOptions): AnyProcedure[];
|
|
27
45
|
//# sourceMappingURL=procedures.d.ts.map
|
package/dist/procedures.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAgD,KAAK,YAAY,EAAyB,MAAM,4BAA4B,CAAC;AA+CpI,eAAO,MAAM,iBAAiB,EAAE,YAUN,CAAC;AAiB3B,eAAO,MAAM,uBAAuB,EAAE,YAcZ,CAAC;AAM3B,eAAO,MAAM,iBAAiB,EAAE,YAUN,CAAC;AAY3B,6GAA6G;AAC7G,MAAM,MAAM,SAAS,GAAG,CACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,KAC3D,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAAC;AAgBvE;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,GAAE;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;CAAO,GACvD,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAED,2GAA2G;AAC3G,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CACxC;AAmBD,eAAO,MAAM,iBAAiB,EAAE,YAAwC,CAAC;AAEzE,eAAO,MAAM,aAAa,EAAE,YAAY,EAAuF,CAAC;AAEhI;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,YAAY,EAAE,CAGtF"}
|
package/dist/procedures.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The procedures of `@procedures/ics`: `ics.parse`, `ics.occurrences`, `ics.write` and `ics.fetch`.
|
|
3
|
+
*
|
|
4
|
+
* `ics.fetch` sends its request with the `fetch` of the module. The default is the global
|
|
5
|
+
* `fetch`, so the root stays safe for a browser. A Node host that takes calls from others gives
|
|
6
|
+
* `createHostFetch()` of `@procedures/http` to `createIcsModule()`. Then the host policy refuses a
|
|
7
|
+
* feed on a loopback, private or link-local address (audit pass 2, O55).
|
|
3
8
|
*/
|
|
4
|
-
import { createProcedure, zodAdapter } from "@procedures/client/browser";
|
|
9
|
+
import { InvocationError, createProcedure, zodAdapter } from "@procedures/client/browser";
|
|
5
10
|
import { z } from "zod";
|
|
6
11
|
import { calendarOccurrences, isCalendar, parseIcs } from "./ics.js";
|
|
7
12
|
import { icsWrite } from "./procedures/ics/write.js";
|
|
@@ -118,6 +123,11 @@ export async function fetchCalendar(url, timeoutMs, signal, options = {}) {
|
|
|
118
123
|
response = await fetchText(url, { signal: AbortSignal.any(signals), headers: { accept: "text/calendar, */*" } });
|
|
119
124
|
}
|
|
120
125
|
catch (error) {
|
|
126
|
+
// A refusal of the host policy keeps its code and its reason. Its message can name the
|
|
127
|
+
// address of a redirect, so the new message does not.
|
|
128
|
+
if (error instanceof InvocationError && error.code === "FORBIDDEN") {
|
|
129
|
+
throw new InvocationError("FORBIDDEN", "The host policy does not allow the host of the calendar feed or of its redirect", error.path, error.reason);
|
|
130
|
+
}
|
|
121
131
|
const code = errorCodeOf(error);
|
|
122
132
|
const reason = error.name === "TimeoutError" ? `no answer in ${timeoutMs} ms` : `the request failed${code === undefined ? "" : ` (${code})`}`;
|
|
123
133
|
throw new Error(`The calendar feed did not answer: ${reason}`);
|
|
@@ -131,19 +141,31 @@ export async function fetchCalendar(url, timeoutMs, signal, options = {}) {
|
|
|
131
141
|
throw new Error("The calendar feed did not give an iCalendar text");
|
|
132
142
|
return text;
|
|
133
143
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
})
|
|
147
|
-
|
|
144
|
+
function fetchProcedure(fetch) {
|
|
145
|
+
return createProcedure()
|
|
146
|
+
.path(["ics", "fetch"])
|
|
147
|
+
.input(zodAdapter(FetchInputSchema))
|
|
148
|
+
.output(zodAdapter(EventsOutputSchema))
|
|
149
|
+
.meta({
|
|
150
|
+
description: "Get a calendar feed and read its events",
|
|
151
|
+
args: ["url"],
|
|
152
|
+
output: "json",
|
|
153
|
+
effects: { reads: ["network"], writes: [] },
|
|
154
|
+
})
|
|
155
|
+
.handler(async (input, ctx) => {
|
|
156
|
+
return { events: parseIcs(await fetchCalendar(input.url, input.timeoutMs, ctx?.signal, { fetch })) };
|
|
157
|
+
})
|
|
158
|
+
.build();
|
|
159
|
+
}
|
|
160
|
+
export const icsFetchProcedure = fetchProcedure(undefined);
|
|
148
161
|
export const icsProcedures = [icsParseProcedure, icsOccurrencesProcedure, icsWriteProcedure, icsFetchProcedure];
|
|
162
|
+
/**
|
|
163
|
+
* This function gives the procedures of this package with the settings of the host. Without a
|
|
164
|
+
* `fetch`, it gives `icsProcedures`.
|
|
165
|
+
*/
|
|
166
|
+
export function createIcsProcedures(options = {}) {
|
|
167
|
+
if (options.fetch === undefined)
|
|
168
|
+
return icsProcedures;
|
|
169
|
+
return [icsParseProcedure, icsOccurrencesProcedure, icsWriteProcedure, fetchProcedure(options.fetch)];
|
|
170
|
+
}
|
|
149
171
|
//# sourceMappingURL=procedures.js.map
|
package/dist/procedures.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"procedures.js","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"procedures.js","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAA4C,MAAM,4BAA4B,CAAC;AACpI,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,QAAQ,EAAkC,MAAM,UAAU,CAAC;AACrG,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAA2C,MAAM,YAAY,CAAC;AAEhH,MAAM,IAAI,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAEvC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAC;AAEzG,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE;IACnB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACvC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE;IACnB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAEH,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wDAAwD,CAAC;CACpF,CAAC,CAAC;AAKH,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;AAEtE,MAAM,CAAC,MAAM,iBAAiB,GAAiB,eAAe,EAAE;KAC7D,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;KACtB,KAAK,CAAC,UAAU,CAAa,gBAAgB,CAAC,CAAC;KAC/C,MAAM,CAAC,UAAU,CAAe,kBAAkB,CAAC,CAAC;KACpD,IAAI,CAAC;IACJ,WAAW,EAAE,sCAAsC;IACnD,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,IAAI;CACd,CAAC;KACD,OAAO,CAAC,KAAK,EAAE,KAAiB,EAAyB,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;KAC/F,KAAK,EAAkB,CAAC;AAE3B,gFAAgF;AAChF,kBAAkB;AAClB,gFAAgF;AAEhF,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IAC/C,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,oCAAoC,CAAC;IAC5D,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,kCAAkC,CAAC;CACzD,CAAC,CAAC;AAKH,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;AAErF,MAAM,CAAC,MAAM,uBAAuB,GAAiB,eAAe,EAAE;KACnE,IAAI,CAAC,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;KAC5B,KAAK,CAAC,UAAU,CAAmB,sBAAsB,CAAC,CAAC;KAC3D,MAAM,CAAC,UAAU,CAAoB,uBAAuB,CAAC,CAAC;KAC9D,IAAI,CAAC;IACJ,WAAW,EAAE,gGAAgG;IAC7G,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,IAAI;CACd,CAAC;KACD,OAAO,CACN,KAAK,EAAE,KAAuB,EAA8B,EAAE,CAAC,CAAC;IAC9D,WAAW,EAAE,mBAAmB,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;CAC3F,CAAC,CACH;KACA,KAAK,EAAkB,CAAC;AAE3B,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF,MAAM,CAAC,MAAM,iBAAiB,GAAiB,eAAe,EAAE;KAC7D,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;KACtB,KAAK,CAAC,UAAU,CAAgB,mBAAmB,CAAC,CAAC;KACrD,MAAM,CAAC,UAAU,CAAiB,oBAAoB,CAAC,CAAC;KACxD,IAAI,CAAC;IACJ,WAAW,EAAE,oFAAoF;IACjG,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,IAAI;CACd,CAAC;KACD,OAAO,CAAC,KAAK,EAAE,KAAoB,EAA2B,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;KACjF,KAAK,EAAkB,CAAC;AAE3B,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,4FAA4F,CAAC;IACnH,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,iCAAiC,CAAC;CAChH,CAAC,CAAC;AASH;;;;;GAKG;AACH,SAAS,WAAW,CAAC,KAAc;IACjC,KAAK,MAAM,IAAI,IAAI,CAAE,KAAoC,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;QACzE,MAAM,IAAI,GAAI,IAA8C,EAAE,IAAI,CAAC;QACnE,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACnF,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAW,EACX,SAAiB,EACjB,MAAoB,EACpB,OAAO,GAA+C,EAAE;IAExD,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IACrG,MAAM,SAAS,GAAc,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC;IACpD,IAAI,QAAwC,CAAC;IAC7C,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,oBAAoB,EAAE,EAAE,CAAC,CAAC;IACnH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,uFAAuF;QACvF,sDAAsD;QACtD,IAAI,KAAK,YAAY,eAAe,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACnE,MAAM,IAAI,eAAe,CAAC,WAAW,EAAE,iFAAiF,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACtJ,CAAC;QACD,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QAChC,MAAM,MAAM,GACT,KAA2B,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,gBAAgB,SAAS,KAAK,CAAC,CAAC,CAAC,qBAAqB,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,EAAE,CAAC;QACxJ,MAAM,IAAI,KAAK,CAAC,qCAAqC,MAAM,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,6FAA6F;IAC7F,+BAA+B;IAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAC3F,OAAO,IAAI,CAAC;AACd,CAAC;AAWD,SAAS,cAAc,CAAC,KAA4B;IAClD,OAAO,eAAe,EAAE;SACrB,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;SACtB,KAAK,CAAC,UAAU,CAAa,gBAAgB,CAAC,CAAC;SAC/C,MAAM,CAAC,UAAU,CAAe,kBAAkB,CAAC,CAAC;SACpD,IAAI,CAAC;QACJ,WAAW,EAAE,yCAAyC;QACtD,IAAI,EAAE,CAAC,KAAK,CAAC;QACb,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;KAC5C,CAAC;SACD,OAAO,CAAC,KAAK,EAAE,KAAiB,EAAE,GAAsB,EAAyB,EAAE;QAClF,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC;IACvG,CAAC,CAAC;SACD,KAAK,EAAkB,CAAC;AAC7B,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAiB,cAAc,CAAC,SAAS,CAAC,CAAC;AAEzE,MAAM,CAAC,MAAM,aAAa,GAAmB,CAAC,iBAAiB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;AAEhI;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAO,GAAyB,EAAE;IACpE,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,aAAa,CAAC;IACtD,OAAO,CAAC,iBAAiB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AACxG,CAAC"}
|
package/dist/types.d.ts
CHANGED
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAM5C,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,KAAK,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACjC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;QAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAM5C,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,KAAK,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACjC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;QAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAC/B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC3B,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,8EAA8E;AAC9E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AA4BD,eAAO,MAAM,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAOvD,CAAC;AAEH,eAAO,MAAM,oBAAoB,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,CAEzD,CAAC"}
|
package/dist/types.js
CHANGED
|
@@ -23,7 +23,8 @@ const IcsWriteEventSchema = z.object({
|
|
|
23
23
|
exdates: z
|
|
24
24
|
.array(z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Write a date (YYYY-MM-DD)"))
|
|
25
25
|
.optional()
|
|
26
|
-
.describe("The dates (YYYY-MM-DD) that the recurrence skips"),
|
|
26
|
+
.describe("The dates (YYYY-MM-DD) that the recurrence skips, in the zone of tzid"),
|
|
27
|
+
tzid: z.string().min(1).optional().describe("The IANA zone of the event. The writer writes each EXDATE in this zone"),
|
|
27
28
|
});
|
|
28
29
|
export const IcsWriteInputSchema = z.object({
|
|
29
30
|
events: z.array(IcsWriteEventSchema).describe("The events (VEVENT) of the calendar"),
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/write.d.ts
CHANGED
|
@@ -35,8 +35,16 @@ export interface IcsWriteEvent {
|
|
|
35
35
|
readonly status?: IcsStatus | undefined;
|
|
36
36
|
/** The recurrence rule (`RRULE`) without the property name, for example `FREQ=WEEKLY;COUNT=4`. */
|
|
37
37
|
readonly rrule?: string | undefined;
|
|
38
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* The dates (`YYYY-MM-DD`) that the recurrence skips (`EXDATE`). Each date is in the zone of
|
|
40
|
+
* `tzid`, or in UTC for an event without a `tzid`.
|
|
41
|
+
*/
|
|
39
42
|
readonly exdates?: readonly string[] | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* The IANA zone of the event, as `parseIcs` gives it. The writer writes each EXDATE as the start
|
|
45
|
+
* time in this zone, with this TZID. DTSTART stays in UTC.
|
|
46
|
+
*/
|
|
47
|
+
readonly tzid?: string | undefined;
|
|
40
48
|
}
|
|
41
49
|
/** The properties of the calendar (VCALENDAR). The writer always writes `VERSION:2.0`. */
|
|
42
50
|
export interface IcsCalendarOptions {
|
package/dist/write.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../src/write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../src/write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,kDAAkD;AAClD,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAEhE,kGAAkG;AAClG,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,oGAAoG;IACpG,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,qFAAqF;IACrF,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,mFAAmF;IACnF,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IACxC,kGAAkG;IAClG,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED,0FAA0F;AAC1F,MAAM,WAAW,kBAAkB;IACjC,+FAA+F;IAC/F,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,6FAA6F;IAC7F,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,iDAAiD;IACjD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,uFAAuF;IACvF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,6CAA6C;AAC7C,eAAO,MAAM,cAAc,2BAA2B,CAAC;AAEvD,wFAAwF;AACxF,eAAO,MAAM,eAAe,KAAK,CAAC;AAQlC;;;GAGG;AACH,eAAO,MAAM,UAAU,UAAW,MAAM,KAAG,MAMY,CAAC;AAWxD;;;;GAIG;AACH,eAAO,MAAM,QAAQ,SAAU,MAAM,KAAG,MAmBvC,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,SAAS,QAAS,MAAM,KAAG,MAOvC,CAAC;AAEF,yGAAyG;AACzG,eAAO,MAAM,UAAU,UAAW,MAAM,KAAG,MAO1C,CAAC;AAqEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,WAAY,SAAS,aAAa,EAAE,YAAW,kBAAkB,KAAQ,MAS7F,CAAC"}
|
package/dist/write.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* not divide a character of more than one octet.
|
|
10
10
|
* - Each line ends with CRLF.
|
|
11
11
|
*/
|
|
12
|
+
import { localTime } from "./zone.js";
|
|
12
13
|
/** The PRODID when the caller gives none. */
|
|
13
14
|
export const DEFAULT_PRODID = "-//procedures//ics//EN";
|
|
14
15
|
/** The maximum length of a content line, in octets, without the CRLF (RFC 5545 3.1). */
|
|
@@ -84,6 +85,22 @@ export const formatDate = (value) => {
|
|
|
84
85
|
}
|
|
85
86
|
return `${m[1]}${m[2]}${m[3]}`;
|
|
86
87
|
};
|
|
88
|
+
/**
|
|
89
|
+
* The TZID parameter and the local start time (`HHMMSS`) of an event with a zone. A zone with a
|
|
90
|
+
* quote or a control character, or a zone that `Intl` does not know, gives undefined.
|
|
91
|
+
*/
|
|
92
|
+
const zonedStart = (event) => {
|
|
93
|
+
const tzid = event.tzid;
|
|
94
|
+
if (tzid === undefined || tzid.length === 0 || /["\u0000-\u001f\u007f]/.test(tzid))
|
|
95
|
+
return undefined;
|
|
96
|
+
try {
|
|
97
|
+
const time = `${localTime(event.start, tzid).replace(":", "")}${formatUtc(event.start).slice(13, 15)}`;
|
|
98
|
+
return { param: /[;:,]/.test(tzid) ? `"${tzid}"` : tzid, time };
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
87
104
|
const token = (name, value) => {
|
|
88
105
|
if (!TOKEN.test(value))
|
|
89
106
|
throw new RangeError(`${name} "${value}" is not a token (letters, digits and "-")`);
|
|
@@ -106,10 +123,20 @@ const eventLines = (event, stamp) => {
|
|
|
106
123
|
lines.push(`RRULE:${event.rrule}`);
|
|
107
124
|
}
|
|
108
125
|
if (event.exdates !== undefined && event.exdates.length > 0) {
|
|
109
|
-
// EXDATE has the value type of DTSTART: a date, or the start time of the skipped occurrence
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
126
|
+
// EXDATE has the value type of DTSTART: a date, or the start time of the skipped occurrence.
|
|
127
|
+
// For an event with a zone, the date is in that zone, so the time is the local start time
|
|
128
|
+
// with the TZID. (Before, the UTC time moved an evening EXDATE to the wrong day: audit O57.)
|
|
129
|
+
const zoned = allDay ? undefined : zonedStart(event);
|
|
130
|
+
if (allDay) {
|
|
131
|
+
lines.push(`EXDATE;VALUE=DATE:${event.exdates.map(formatDate).join(",")}`);
|
|
132
|
+
}
|
|
133
|
+
else if (zoned !== undefined) {
|
|
134
|
+
lines.push(`EXDATE;TZID=${zoned.param}:${event.exdates.map((date) => `${formatDate(date)}T${zoned.time}`).join(",")}`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const startTime = formatUtc(event.start).slice(8);
|
|
138
|
+
lines.push(`EXDATE:${event.exdates.map((date) => `${formatDate(date)}${startTime}`).join(",")}`);
|
|
139
|
+
}
|
|
113
140
|
}
|
|
114
141
|
lines.push(...text("LOCATION", event.location), ...text("DESCRIPTION", event.description));
|
|
115
142
|
if (event.url !== undefined) {
|
package/dist/write.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write.js","sourceRoot":"","sources":["../src/write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"write.js","sourceRoot":"","sources":["../src/write.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@procedures/ics",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.28.1",
|
|
5
5
|
"description": "An iCalendar (RFC 5545) reader and writer without dependencies: the events of a calendar, their occurrences in a time window with the recurrences, and a calendar from events.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"ics",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"provenance": true
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@procedures/client": "0.
|
|
62
|
+
"@procedures/client": "0.28.1",
|
|
63
63
|
"zod": "^4.6.5"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
package/src/module.ts
CHANGED
|
@@ -3,12 +3,26 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { defineModule, registerModules, PROCEDURE_REGISTRY, type ProcedureModule, type ProcedureRegistry } from "@procedures/client/browser";
|
|
6
|
-
import { icsProcedures } from "./procedures.js";
|
|
6
|
+
import { createIcsProcedures, icsProcedures, type IcsProceduresOptions } from "./procedures.js";
|
|
7
7
|
|
|
8
8
|
export const icsModule: ProcedureModule = defineModule("@procedures/ics", icsProcedures);
|
|
9
9
|
|
|
10
10
|
export default icsModule;
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* This function gives the module with the settings of the host: the `fetch` of `ics.fetch`.
|
|
14
|
+
* Without options, it gives `icsModule`. A Node host that takes calls from others gives
|
|
15
|
+
* `createHostFetch()` of `@procedures/http`, so the host policy examines each feed address.
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* createRegistry(createIcsModule({ fetch: createHostFetch() }));
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function createIcsModule(options: IcsProceduresOptions = {}): ProcedureModule {
|
|
22
|
+
if (options.fetch === undefined) return icsModule;
|
|
23
|
+
return defineModule("@procedures/ics", createIcsProcedures(options));
|
|
24
|
+
}
|
|
25
|
+
|
|
12
26
|
/**
|
|
13
27
|
* This function registers the procedures of @procedures/ics in a registry. The default is the
|
|
14
28
|
* global registry.
|
package/src/procedures.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The procedures of `@procedures/ics`: `ics.parse`, `ics.occurrences`, `ics.write` and `ics.fetch`.
|
|
3
|
+
*
|
|
4
|
+
* `ics.fetch` sends its request with the `fetch` of the module. The default is the global
|
|
5
|
+
* `fetch`, so the root stays safe for a browser. A Node host that takes calls from others gives
|
|
6
|
+
* `createHostFetch()` of `@procedures/http` to `createIcsModule()`. Then the host policy refuses a
|
|
7
|
+
* feed on a loopback, private or link-local address (audit pass 2, O55).
|
|
3
8
|
*/
|
|
4
9
|
|
|
5
|
-
import { createProcedure, zodAdapter, type AnyProcedure, type ProcedureContext } from "@procedures/client/browser";
|
|
10
|
+
import { InvocationError, createProcedure, zodAdapter, type AnyProcedure, type ProcedureContext } from "@procedures/client/browser";
|
|
6
11
|
import { z } from "zod";
|
|
7
12
|
import { calendarOccurrences, isCalendar, parseIcs, type IcsEvent, type Occurrence } from "./ics.js";
|
|
8
13
|
import { icsWrite } from "./procedures/ics/write.js";
|
|
@@ -155,6 +160,11 @@ export async function fetchCalendar(
|
|
|
155
160
|
try {
|
|
156
161
|
response = await fetchText(url, { signal: AbortSignal.any(signals), headers: { accept: "text/calendar, */*" } });
|
|
157
162
|
} catch (error) {
|
|
163
|
+
// A refusal of the host policy keeps its code and its reason. Its message can name the
|
|
164
|
+
// address of a redirect, so the new message does not.
|
|
165
|
+
if (error instanceof InvocationError && error.code === "FORBIDDEN") {
|
|
166
|
+
throw new InvocationError("FORBIDDEN", "The host policy does not allow the host of the calendar feed or of its redirect", error.path, error.reason);
|
|
167
|
+
}
|
|
158
168
|
const code = errorCodeOf(error);
|
|
159
169
|
const reason =
|
|
160
170
|
(error as { name?: string }).name === "TimeoutError" ? `no answer in ${timeoutMs} ms` : `the request failed${code === undefined ? "" : ` (${code})`}`;
|
|
@@ -168,19 +178,41 @@ export async function fetchCalendar(
|
|
|
168
178
|
return text;
|
|
169
179
|
}
|
|
170
180
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
181
|
+
/** The settings of the host for the procedures of this package. The procedure input cannot change them. */
|
|
182
|
+
export interface IcsProceduresOptions {
|
|
183
|
+
/**
|
|
184
|
+
* The function that sends the request of `ics.fetch`. The default is the global `fetch`. A Node
|
|
185
|
+
* host gives `createHostFetch()` of `@procedures/http`.
|
|
186
|
+
*/
|
|
187
|
+
readonly fetch?: FetchText | undefined;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function fetchProcedure(fetch: FetchText | undefined): AnyProcedure {
|
|
191
|
+
return createProcedure()
|
|
192
|
+
.path(["ics", "fetch"])
|
|
193
|
+
.input(zodAdapter<FetchInput>(FetchInputSchema))
|
|
194
|
+
.output(zodAdapter<EventsOutput>(EventsOutputSchema))
|
|
195
|
+
.meta({
|
|
196
|
+
description: "Get a calendar feed and read its events",
|
|
197
|
+
args: ["url"],
|
|
198
|
+
output: "json",
|
|
199
|
+
effects: { reads: ["network"], writes: [] },
|
|
200
|
+
})
|
|
201
|
+
.handler(async (input: FetchInput, ctx?: ProcedureContext): Promise<EventsOutput> => {
|
|
202
|
+
return { events: parseIcs(await fetchCalendar(input.url, input.timeoutMs, ctx?.signal, { fetch })) };
|
|
203
|
+
})
|
|
204
|
+
.build() as AnyProcedure;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export const icsFetchProcedure: AnyProcedure = fetchProcedure(undefined);
|
|
185
208
|
|
|
186
209
|
export const icsProcedures: AnyProcedure[] = [icsParseProcedure, icsOccurrencesProcedure, icsWriteProcedure, icsFetchProcedure];
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* This function gives the procedures of this package with the settings of the host. Without a
|
|
213
|
+
* `fetch`, it gives `icsProcedures`.
|
|
214
|
+
*/
|
|
215
|
+
export function createIcsProcedures(options: IcsProceduresOptions = {}): AnyProcedure[] {
|
|
216
|
+
if (options.fetch === undefined) return icsProcedures;
|
|
217
|
+
return [icsParseProcedure, icsOccurrencesProcedure, icsWriteProcedure, fetchProcedure(options.fetch)];
|
|
218
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -24,6 +24,7 @@ export interface IcsWriteInput {
|
|
|
24
24
|
status?: IcsStatus | undefined;
|
|
25
25
|
rrule?: string | undefined;
|
|
26
26
|
exdates?: string[] | undefined;
|
|
27
|
+
tzid?: string | undefined;
|
|
27
28
|
}>;
|
|
28
29
|
prodId?: string | undefined;
|
|
29
30
|
method?: string | undefined;
|
|
@@ -59,7 +60,8 @@ const IcsWriteEventSchema = z.object({
|
|
|
59
60
|
exdates: z
|
|
60
61
|
.array(z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Write a date (YYYY-MM-DD)"))
|
|
61
62
|
.optional()
|
|
62
|
-
.describe("The dates (YYYY-MM-DD) that the recurrence skips"),
|
|
63
|
+
.describe("The dates (YYYY-MM-DD) that the recurrence skips, in the zone of tzid"),
|
|
64
|
+
tzid: z.string().min(1).optional().describe("The IANA zone of the event. The writer writes each EXDATE in this zone"),
|
|
63
65
|
});
|
|
64
66
|
|
|
65
67
|
export const IcsWriteInputSchema: z.ZodType<IcsWriteInput> = z.object({
|
package/src/write.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* - Each line ends with CRLF.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { localTime } from "./zone.js";
|
|
14
|
+
|
|
13
15
|
/** The STATUS of an event (RFC 5545 3.8.1.11). */
|
|
14
16
|
export type IcsStatus = "TENTATIVE" | "CONFIRMED" | "CANCELLED";
|
|
15
17
|
|
|
@@ -37,8 +39,16 @@ export interface IcsWriteEvent {
|
|
|
37
39
|
readonly status?: IcsStatus | undefined;
|
|
38
40
|
/** The recurrence rule (`RRULE`) without the property name, for example `FREQ=WEEKLY;COUNT=4`. */
|
|
39
41
|
readonly rrule?: string | undefined;
|
|
40
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* The dates (`YYYY-MM-DD`) that the recurrence skips (`EXDATE`). Each date is in the zone of
|
|
44
|
+
* `tzid`, or in UTC for an event without a `tzid`.
|
|
45
|
+
*/
|
|
41
46
|
readonly exdates?: readonly string[] | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* The IANA zone of the event, as `parseIcs` gives it. The writer writes each EXDATE as the start
|
|
49
|
+
* time in this zone, with this TZID. DTSTART stays in UTC.
|
|
50
|
+
*/
|
|
51
|
+
readonly tzid?: string | undefined;
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
/** The properties of the calendar (VCALENDAR). The writer always writes `VERSION:2.0`. */
|
|
@@ -134,6 +144,21 @@ export const formatDate = (value: string): string => {
|
|
|
134
144
|
return `${m[1]}${m[2]}${m[3]}`;
|
|
135
145
|
};
|
|
136
146
|
|
|
147
|
+
/**
|
|
148
|
+
* The TZID parameter and the local start time (`HHMMSS`) of an event with a zone. A zone with a
|
|
149
|
+
* quote or a control character, or a zone that `Intl` does not know, gives undefined.
|
|
150
|
+
*/
|
|
151
|
+
const zonedStart = (event: IcsWriteEvent): { param: string; time: string } | undefined => {
|
|
152
|
+
const tzid = event.tzid;
|
|
153
|
+
if (tzid === undefined || tzid.length === 0 || /["\u0000-\u001f\u007f]/.test(tzid)) return undefined;
|
|
154
|
+
try {
|
|
155
|
+
const time = `${localTime(event.start, tzid).replace(":", "")}${formatUtc(event.start).slice(13, 15)}`;
|
|
156
|
+
return { param: /[;:,]/.test(tzid) ? `"${tzid}"` : tzid, time };
|
|
157
|
+
} catch {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
137
162
|
const token = (name: string, value: string): string => {
|
|
138
163
|
if (!TOKEN.test(value)) throw new RangeError(`${name} "${value}" is not a token (letters, digits and "-")`);
|
|
139
164
|
return value.toUpperCase();
|
|
@@ -154,10 +179,18 @@ const eventLines = (event: IcsWriteEvent, stamp: string): string[] => {
|
|
|
154
179
|
lines.push(`RRULE:${event.rrule}`);
|
|
155
180
|
}
|
|
156
181
|
if (event.exdates !== undefined && event.exdates.length > 0) {
|
|
157
|
-
// EXDATE has the value type of DTSTART: a date, or the start time of the skipped occurrence
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
182
|
+
// EXDATE has the value type of DTSTART: a date, or the start time of the skipped occurrence.
|
|
183
|
+
// For an event with a zone, the date is in that zone, so the time is the local start time
|
|
184
|
+
// with the TZID. (Before, the UTC time moved an evening EXDATE to the wrong day: audit O57.)
|
|
185
|
+
const zoned = allDay ? undefined : zonedStart(event);
|
|
186
|
+
if (allDay) {
|
|
187
|
+
lines.push(`EXDATE;VALUE=DATE:${event.exdates.map(formatDate).join(",")}`);
|
|
188
|
+
} else if (zoned !== undefined) {
|
|
189
|
+
lines.push(`EXDATE;TZID=${zoned.param}:${event.exdates.map((date) => `${formatDate(date)}T${zoned.time}`).join(",")}`);
|
|
190
|
+
} else {
|
|
191
|
+
const startTime = formatUtc(event.start).slice(8);
|
|
192
|
+
lines.push(`EXDATE:${event.exdates.map((date) => `${formatDate(date)}${startTime}`).join(",")}`);
|
|
193
|
+
}
|
|
161
194
|
}
|
|
162
195
|
lines.push(...text("LOCATION", event.location), ...text("DESCRIPTION", event.description));
|
|
163
196
|
if (event.url !== undefined) {
|