forked from GitHubMirrors/silverbullet-icalendar
Compare commits
14 Commits
523b49dd3a
...
c9a703975d
| Author | SHA1 | Date | |
|---|---|---|---|
| c9a703975d | |||
| 842586c129 | |||
| c6532e5aca | |||
| 42fb8be61c | |||
| f8a6fbafda | |||
| dbc7ef29aa | |||
| 7edc0997b2 | |||
| c3fd3aee20 | |||
| e5b063269f | |||
| 31ca364a7c | |||
| e7c69aa3f7 | |||
| 255988e6f3 | |||
| ada9f6694c | |||
| cd0fdf5f98 |
2
PLUG.md
2
PLUG.md
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Library/sstent/icalendar
|
name: Library/sstent/icalendar
|
||||||
version: "0.3.31"
|
version: "0.3.32"
|
||||||
tags: meta/library
|
tags: meta/library
|
||||||
files:
|
files:
|
||||||
- icalendar.plug.js
|
- icalendar.plug.js
|
||||||
|
|||||||
@@ -16,3 +16,8 @@ This file tracks all major tracks for the project. Each track has its own detail
|
|||||||
|
|
||||||
- [x] **Track: Fix RRULE object expansion error by correctly mapping object keys to standard iCalendar RRULE properties.**
|
- [x] **Track: Fix RRULE object expansion error by correctly mapping object keys to standard iCalendar RRULE properties.**
|
||||||
*Link: [./tracks/fix_rrule_object_mapping_20260219/](./tracks/fix_rrule_object_mapping_20260219/)*
|
*Link: [./tracks/fix_rrule_object_mapping_20260219/](./tracks/fix_rrule_object_mapping_20260219/)*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [x] **Track: Fix RRULE UNTIL object conversion error: Invalid UNTIL value: [object Object]**
|
||||||
|
*Link: [./tracks/fix_rrule_until_conversion_20260219/](./tracks/fix_rrule_until_conversion_20260219/)*
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Track fix_rrule_until_conversion_20260219 Context
|
||||||
|
|
||||||
|
- [Specification](./spec.md)
|
||||||
|
- [Implementation Plan](./plan.md)
|
||||||
|
- [Metadata](./metadata.json)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"track_id": "fix_rrule_until_conversion_20260219",
|
||||||
|
"type": "bug",
|
||||||
|
"status": "new",
|
||||||
|
"created_at": "2026-02-19T00:00:00Z",
|
||||||
|
"updated_at": "2026-02-19T00:00:00Z",
|
||||||
|
"description": "Fix RRULE UNTIL object conversion error: Invalid UNTIL value: [object Object]"
|
||||||
|
}
|
||||||
19
conductor/tracks/fix_rrule_until_conversion_20260219/plan.md
Normal file
19
conductor/tracks/fix_rrule_until_conversion_20260219/plan.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Implementation Plan - Fix RRULE UNTIL Conversion
|
||||||
|
|
||||||
|
## Phase 1: Reproduction [checkpoint: 02fcb7e]
|
||||||
|
- [x] Task: Reproduce `Invalid UNTIL value` error 17de604
|
||||||
|
- [x] Add a test case in `icalendar_test.ts` where `rrule` object has an `until` property as a `Date`.
|
||||||
|
- [x] Run the test and confirm it fails with `Error: Invalid UNTIL value: [object Object]`.
|
||||||
|
- [x] Task: Conductor - User Manual Verification 'Reproduction' (Protocol in workflow.md)
|
||||||
|
|
||||||
|
## Phase 2: Fix Implementation [checkpoint: 0f334b2]
|
||||||
|
- [x] Task: Implement value formatting logic in `icalendar.ts` 331d0ab
|
||||||
|
- [x] Update `expandRecurrences` to use a helper for property value conversion.
|
||||||
|
- [x] Ensure `Date` objects are formatted as `YYYYMMDDTHHMMSSZ`.
|
||||||
|
- [x] Run the test to confirm it passes.
|
||||||
|
- [x] Task: Conductor - User Manual Verification 'Fix Implementation' (Protocol in workflow.md)
|
||||||
|
|
||||||
|
## Phase 3: Verification & Cleanup [checkpoint: a02f228]
|
||||||
|
- [x] Task: Full Regression Check d090334
|
||||||
|
- [x] Run all tests in `icalendar_test.ts`.
|
||||||
|
- [x] Task: Conductor - User Manual Verification 'Verification & Cleanup' (Protocol in workflow.md)
|
||||||
20
conductor/tracks/fix_rrule_until_conversion_20260219/spec.md
Normal file
20
conductor/tracks/fix_rrule_until_conversion_20260219/spec.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Specification: Fix RRULE UNTIL Object Conversion
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
The conversion of RRULE objects to strings fails for nested objects like `UNTIL` dates, resulting in `UNTIL=[object Object]`. This causes the `rrule` library to fail during expansion.
|
||||||
|
|
||||||
|
## Functional Requirements
|
||||||
|
- **Robust Value Formatting:** Implement `formatRRuleValue` to handle various types of RRULE values:
|
||||||
|
- **Date Objects:** Convert to `YYYYMMDDTHHMMSSZ`.
|
||||||
|
- **Nested Date Objects:** (e.g., `{ date: Date }` from `ts-ics`) Extract and convert.
|
||||||
|
- **Other Types:** Default to string conversion.
|
||||||
|
- **Mapping Preservation:** Maintain the existing `RRULE_KEY_MAP` for key translation.
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
1. **Reproduce:** Add test `expandRecurrences - object rrule with until` in `icalendar_test.ts` using a Date object for `until`. Verify it fails with `Invalid UNTIL value`.
|
||||||
|
2. **Fix:** Update `expandRecurrences` in `icalendar.ts` to use a formatting helper for values.
|
||||||
|
3. **Verify:** Confirm the test case passes.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- [ ] Test `expandRecurrences - object rrule with until` passes.
|
||||||
|
- [ ] The generated string correctly represents the date (e.g., `UNTIL=20260219T000000Z`).
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "icalendar-plug",
|
"name": "icalendar-plug",
|
||||||
"version": "0.3.31",
|
"version": "0.3.32",
|
||||||
"nodeModulesDir": "auto",
|
"nodeModulesDir": "auto",
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"sync-version": "deno run -A scripts/sync-version.ts",
|
"sync-version": "deno run -A scripts/sync-version.ts",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: icalendar
|
name: icalendar
|
||||||
version: 0.3.31
|
version: 0.3.32
|
||||||
author: sstent
|
author: sstent
|
||||||
index: icalendar.ts
|
index: icalendar.ts
|
||||||
# Legacy SilverBullet permission name
|
# Legacy SilverBullet permission name
|
||||||
|
|||||||
18
icalendar.ts
18
icalendar.ts
@@ -3,7 +3,7 @@ import { convertIcsCalendar } from "https://esm.sh/ts-ics@2.4.0";
|
|||||||
import { RRule, RRuleSet } from "rrule";
|
import { RRule, RRuleSet } from "rrule";
|
||||||
import { getUtcOffsetMs, resolveIanaName } from "./timezones.ts";
|
import { getUtcOffsetMs, resolveIanaName } from "./timezones.ts";
|
||||||
|
|
||||||
const VERSION = "0.3.31";
|
const VERSION = "0.3.32";
|
||||||
const CACHE_KEY = "icalendar:lastSync";
|
const CACHE_KEY = "icalendar:lastSync";
|
||||||
|
|
||||||
console.log(`[iCalendar] Plug script executing at top level (Version ${VERSION})`);
|
console.log(`[iCalendar] Plug script executing at top level (Version ${VERSION})`);
|
||||||
@@ -29,6 +29,20 @@ const RRULE_KEY_MAP: Record<string, string> = {
|
|||||||
"freq": "FREQ", // Just in case
|
"freq": "FREQ", // Just in case
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats an RRULE value for the string representation.
|
||||||
|
* Specifically handles Date objects and nested date objects from ts-ics.
|
||||||
|
*/
|
||||||
|
function formatRRuleValue(v: any): string {
|
||||||
|
if (v instanceof Date) {
|
||||||
|
return v.toISOString().replace(/[-:]/g, "").split(".")[0] + "Z";
|
||||||
|
}
|
||||||
|
if (typeof v === "object" && v !== null && v.date instanceof Date) {
|
||||||
|
return v.date.toISOString().replace(/[-:]/g, "").split(".")[0] + "Z";
|
||||||
|
}
|
||||||
|
return String(v);
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Utility Functions
|
// Utility Functions
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -186,7 +200,7 @@ export function expandRecurrences(icsEvent: any, windowDays = 365): any[] {
|
|||||||
cleanRule = Object.entries(rruleStr)
|
cleanRule = Object.entries(rruleStr)
|
||||||
.map(([k, v]) => {
|
.map(([k, v]) => {
|
||||||
const standardKey = RRULE_KEY_MAP[k.toLowerCase()] || k.toUpperCase();
|
const standardKey = RRULE_KEY_MAP[k.toLowerCase()] || k.toUpperCase();
|
||||||
return `${standardKey}=${v}`;
|
return `${standardKey}=${formatRRuleValue(v)}`;
|
||||||
})
|
})
|
||||||
.join(";");
|
.join(";");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -206,3 +206,22 @@ Deno.test("expandRecurrences - object rrule (Reproduction of missing events)", (
|
|||||||
assert(!warningLogged, "Should NOT have logged a warning for object rrule");
|
assert(!warningLogged, "Should NOT have logged a warning for object rrule");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Deno.test("expandRecurrences - object rrule with until", () => {
|
||||||
|
const now = new Date();
|
||||||
|
const start = new Date(now.getTime() - 10 * 86400000);
|
||||||
|
const startStr = localDateString(start);
|
||||||
|
const untilDate = new Date(now.getTime() + 10 * 86400000);
|
||||||
|
|
||||||
|
const icsEvent = {
|
||||||
|
summary: "Object RRULE UNTIL Event",
|
||||||
|
start: startStr,
|
||||||
|
rrule: { frequency: "DAILY", until: { date: untilDate } }
|
||||||
|
};
|
||||||
|
|
||||||
|
const results = expandRecurrences(icsEvent, 30);
|
||||||
|
// Should now return multiple occurrences
|
||||||
|
assert(results.length > 1, `Expected > 1 occurrences, got ${results.length}`);
|
||||||
|
assertEquals(results[0].recurrent, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user