Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ jobs:
if: matrix.node-version >= 22
run: pnpm lint

- name: Build package for entry tests
if: matrix.node-version >= 22
run: pnpm build

- name: Run `test`
if: matrix.node-version >= 22
run: pnpm test
Expand All @@ -59,6 +63,13 @@ jobs:
CREDENTIAL_NEXTCLOUD_PASSWORD: ${{secrets.CREDENTIAL_NEXTCLOUD_PASSWORD}}
CREDENTIAL_NEXTCLOUD_SERVER_URL: ${{secrets.CREDENTIAL_NEXTCLOUD_SERVER_URL}}

- name: Check public consumers and examples
if: matrix.node-version >= 22
run: |
pnpm test:consumer
pnpm test:examples
pnpm exec knip --dependencies --workspace .

- name: Build package for runtime smoke test
if: matrix.node-version < 22
run: |
Expand Down Expand Up @@ -232,6 +243,12 @@ jobs:
cache: 'pnpm'
cache-dependency-path: docs/pnpm-lock.yaml
- run: pnpm install --frozen-lockfile
- name: Install shared-source verification tools
working-directory: .
run: pnpm install --frozen-lockfile
- name: Check docs dependencies
working-directory: .
run: pnpm exec knip --dependencies --workspace docs
- run: pnpm test
- run: pnpm exec tsc --noEmit
- run: pnpm typecheck
- run: pnpm build
13 changes: 11 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ jobs:
- name: check types
run: pnpm typecheck

# - name: lint
# run: pnpm lint
- name: lint
run: pnpm lint

- name: build package for verification
run: pnpm build

- name: test
run: pnpm test
Expand All @@ -57,6 +60,12 @@ jobs:
CREDENTIAL_NEXTCLOUD_PASSWORD: ${{secrets.CREDENTIAL_NEXTCLOUD_PASSWORD}}
CREDENTIAL_NEXTCLOUD_SERVER_URL: ${{secrets.CREDENTIAL_NEXTCLOUD_SERVER_URL}}

- name: check public consumers and examples
run: |
pnpm test:consumer
pnpm test:examples
pnpm exec knip --dependencies --workspace .

# Publish to npm with provenance statements
# Requires NPM_TOKEN secret to be configured in repository secrets
# or in the Production environment secrets
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,5 @@ typings/
.DS_Store
.vercel
.vscode/

/tests/.examples-*/
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
## v2.3.5

##### bug fixes

- resolve relative discovery redirects against the request URL; thanks to [@bensynapse](https://github.com/bensynapse) for [#281](https://github.com/natelindev/tsdav/pull/281)
- preserve redirect origins and explicit ports, and remove request bodies from discovery's GET fallback
- preserve opaque DAV XML values as strings, mixed text/CDATA ordering, namespaces, and property status details while retaining `xml-js`
- preserve complete successful raw responses, including large free/busy payloads
- reject failed or incomplete discovery, object retrieval, and sync responses before calculating deletions or advancing sync state
- include tasks, journals, and extensionless resources in collection synchronization, preserve REPORT sync tokens, and retain unchanged local objects
- refresh expired OAuth credentials before requests through both client APIs, deduplicate concurrent refreshes, and stop DAV requests when refresh fails
- apply header exclusions after merging all request headers and respect CardDAV object URL filters when multi-get is disabled
- require a time range when expanding calendar recurrences, correct client sync return types, and bind multi-get callbacks to the client
- restrict package cleanup to the `dist` directory

##### improvements

- export `makeCollection` and the `DAVPropStat` type, and replace repeated sync URL scans with indexed comparisons
- correct browser, synchronization, iCalendar import, authentication, and response documentation; preserve recurrence exceptions, timezones, all-day events, and escaping in the feed-import example
- replace executable input in the docs XML converter with JSON parsing and share the library's XML normalizer
- derive the documentation version from the package and verify executable examples, public consumer types, dependency usage, and fresh package artifacts in CI and release checks

##### security

- remove unused direct dependencies and constrain vulnerable documentation transitive dependencies to fixed versions; root and documentation dependency audits report no known vulnerabilities at release verification

## v2.3.4

##### bug fixes
Expand Down
2 changes: 1 addition & 1 deletion biome.jsonc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.5.1/schema.json",
"$schema": "https://biomejs.dev/schemas/2.5.12/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
Expand Down
111 changes: 83 additions & 28 deletions dist/client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,16 @@ import { calendarMultiGet as rawCalendarMultiGet, calendarQuery as rawCalendarQu
import { collectionQuery as rawCollectionQuery, isCollectionDirty as rawIsCollectionDirty, makeCollection as rawMakeCollection, supportedReportSet as rawSupportedReportSet, syncCollection as rawSyncCollection } from './collection';
import { createObject as rawCreateObject, deleteObject as rawDeleteObject, propfind as rawPropfind, updateObject as rawUpdateObject } from './request';
import { DAVRequest, DAVResponse } from './types/DAVTypes';
import { SmartCollectionSync, SmartCollectionSyncDetailed, SmartCollectionSyncDetailedResult, SyncCalendars, SyncCalendarsDetailed, SyncCalendarsDetailedResult } from './types/functionsOverloads';
import { SmartCollectionSyncDetailedResult, SyncCalendars, SyncCalendarsDetailed, SyncCalendarsDetailedResult } from './types/functionsOverloads';
import { DAVAccount, DAVAddressBook, DAVCalendar, DAVCalendarObject, DAVCollection, DAVCredentials, DAVVCard } from './types/models';
import { Optional } from './util/typeHelpers';
export declare const createDAVClient: (params: {
serverUrl: string;
credentials: DAVCredentials;
authMethod?: 'Basic' | 'Oauth' | 'Digest' | 'Custom' | 'Bearer';
authFunction?: (credentials: DAVCredentials) => Promise<Record<string, string>>;
defaultAccountType?: DAVAccount['accountType'] | undefined;
fetchOptions?: RequestInit;
fetch?: typeof globalThis.fetch;
}) => Promise<{
export declare const createDAVClient: (params: ConstructorParameters<typeof DAVClient>[0]) => Promise<{
davRequest: (params0: {
url: string;
init: DAVRequest;
convertIncoming?: boolean;
parseOutgoing?: boolean;
headersToExclude?: string[];
fetchOptions?: RequestInit;
fetch?: typeof globalThis.fetch;
}) => Promise<DAVResponse[]>;
Expand All @@ -32,14 +25,7 @@ export declare const createDAVClient: (params: {
fetchOptions?: RequestInit;
fetch?: typeof import("./util/fetch").fetch;
}) => Promise<DAVResponse[]>;
createAccount: (params0: {
account: Optional<DAVAccount, 'serverUrl'>;
headers?: Record<string, string>;
loadCollections?: boolean;
loadObjects?: boolean;
fetchOptions?: RequestInit;
fetch?: typeof globalThis.fetch;
}) => Promise<DAVAccount>;
createAccount: (...args: Parameters<DAVClient['createAccount']>) => Promise<DAVAccount>;
createObject: (params: {
url: string;
data: BodyInit;
Expand Down Expand Up @@ -163,10 +149,52 @@ export declare const createDAVClient: (params: {
fetch?: typeof fetch;
}) => Promise<{
isDirty: boolean;
newCtag: string;
newCtag: string | undefined;
}>;
smartCollectionSync: SmartCollectionSync;
smartCollectionSyncDetailed: SmartCollectionSyncDetailed;
smartCollectionSync: {
<T extends DAVCollection>(param: {
collection: T;
method?: 'basic' | 'webdav';
headers?: Record<string, string>;
headersToExclude?: string[];
fetchOptions?: RequestInit;
fetch?: typeof globalThis.fetch;
account?: DAVAccount;
/** @deprecated Use smartCollectionSyncDetailed instead. */
detailedResult?: false;
}): Promise<T>;
<T extends DAVCollection>(param: {
collection: T;
method?: 'basic' | 'webdav';
headers?: Record<string, string>;
headersToExclude?: string[];
fetchOptions?: RequestInit;
fetch?: typeof globalThis.fetch;
account?: DAVAccount;
/** @deprecated Use smartCollectionSyncDetailed instead. */
detailedResult: true;
}): Promise<SmartCollectionSyncDetailedResult<T>>;
<T extends DAVCollection>(param: {
collection: T;
method?: 'basic' | 'webdav';
headers?: Record<string, string>;
headersToExclude?: string[];
fetchOptions?: RequestInit;
fetch?: typeof globalThis.fetch;
account?: DAVAccount;
/** @deprecated Use smartCollectionSyncDetailed instead. */
detailedResult?: boolean;
}): Promise<T | SmartCollectionSyncDetailedResult<T>>;
};
smartCollectionSyncDetailed: <T extends DAVCollection>(param: {
collection: T;
method?: 'basic' | 'webdav';
headers?: Record<string, string>;
headersToExclude?: string[];
fetchOptions?: RequestInit;
fetch?: typeof globalThis.fetch;
account?: DAVAccount;
}) => Promise<SmartCollectionSyncDetailedResult<T>>;
fetchCalendars: (params?: {
account?: DAVAccount;
props?: import("xml-js").ElementCompact;
Expand Down Expand Up @@ -198,7 +226,7 @@ export declare const createDAVClient: (params: {
useMultiGet?: boolean;
fetchOptions?: RequestInit;
fetch?: typeof fetch;
}) => Promise<import(".").DAVObject[]>;
}) => Promise<DAVCalendarObject[]>;
createCalendarObject: (params: {
calendar: DAVCalendar;
iCalString: string;
Expand All @@ -222,16 +250,31 @@ export declare const createDAVClient: (params: {
fetchOptions?: RequestInit;
fetch?: typeof fetch;
}) => Promise<Response>;
syncCalendars: SyncCalendars;
syncCalendarsDetailed: SyncCalendarsDetailed;
syncCalendars: {
(params: Parameters<SyncCalendars>[0] & {
detailedResult: true;
}): Promise<SyncCalendarsDetailedResult>;
(params: Parameters<SyncCalendars>[0] & {
detailedResult?: false;
}): Promise<DAVCalendar[]>;
(params: Parameters<SyncCalendars>[0]): Promise<DAVCalendar[] | SyncCalendarsDetailedResult>;
};
syncCalendarsDetailed: (params: {
oldCalendars: DAVCalendar[];
headers?: Record<string, string>;
headersToExclude?: string[];
fetchOptions?: RequestInit;
fetch?: typeof fetch;
account?: DAVAccount;
}) => Promise<SyncCalendarsDetailedResult>;
fetchAddressBooks: (params?: {
account?: DAVAccount;
props?: import("xml-js").ElementCompact;
headers?: Record<string, string>;
headersToExclude?: string[];
fetchOptions?: RequestInit;
fetch?: typeof fetch;
} | undefined) => Promise<DAVCollection[]>;
} | undefined) => Promise<DAVAddressBook[]>;
addressBookMultiGet: (params: {
url: string;
props: import("xml-js").ElementCompact;
Expand All @@ -251,7 +294,7 @@ export declare const createDAVClient: (params: {
headersToExclude?: string[];
fetchOptions?: RequestInit;
fetch?: typeof fetch;
}) => Promise<import(".").DAVObject[]>;
}) => Promise<DAVVCard[]>;
createVCard: (params: {
addressBook: DAVAddressBook;
vCardString: string;
Expand Down Expand Up @@ -295,6 +338,10 @@ export declare class DAVClient {
fetchOptions?: RequestInit;
fetch?: typeof globalThis.fetch;
});
private authentication?;
private authenticate;
private requestDefaults;
private invoke;
login(options?: {
loadCollections?: boolean;
loadObjects?: boolean;
Expand All @@ -304,6 +351,7 @@ export declare class DAVClient {
init: DAVRequest;
convertIncoming?: boolean;
parseOutgoing?: boolean;
headersToExclude?: string[];
fetchOptions?: RequestInit;
fetch?: typeof globalThis.fetch;
}): Promise<DAVResponse[]>;
Expand All @@ -314,6 +362,7 @@ export declare class DAVClient {
createAccount(params0: {
account: Optional<DAVAccount, 'serverUrl'>;
headers?: Record<string, string>;
headersToExclude?: string[];
loadCollections?: boolean;
loadObjects?: boolean;
fetchOptions?: RequestInit;
Expand All @@ -325,7 +374,7 @@ export declare class DAVClient {
supportedReportSet(...params: Parameters<typeof rawSupportedReportSet>): Promise<string[]>;
isCollectionDirty(...params: Parameters<typeof rawIsCollectionDirty>): Promise<{
isDirty: boolean;
newCtag: string;
newCtag: string | undefined;
}>;
smartCollectionSync<T extends DAVCollection>(param: {
collection: T;
Expand Down Expand Up @@ -379,7 +428,13 @@ export declare class DAVClient {
createCalendarObject(...params: Parameters<typeof rawCreateCalendarObject>): Promise<Response>;
updateCalendarObject(...params: Parameters<typeof rawUpdateCalendarObject>): Promise<Response>;
deleteCalendarObject(...params: Parameters<typeof rawDeleteCalendarObject>): Promise<Response>;
syncCalendars(...params: Parameters<SyncCalendars>): Promise<ReturnType<SyncCalendars>>;
syncCalendars(params: Parameters<SyncCalendars>[0] & {
detailedResult: true;
}): Promise<SyncCalendarsDetailedResult>;
syncCalendars(params: Parameters<SyncCalendars>[0] & {
detailedResult?: false;
}): Promise<DAVCalendar[]>;
syncCalendars(params: Parameters<SyncCalendars>[0]): Promise<DAVCalendar[] | SyncCalendarsDetailedResult>;
syncCalendarsDetailed(...params: Parameters<SyncCalendarsDetailed>): Promise<SyncCalendarsDetailedResult>;
addressBookQuery(...params: Parameters<typeof rawAddressBookQuery>): Promise<DAVResponse[]>;
addressBookMultiGet(...params: Parameters<typeof rawAddressBookMultiGet>): Promise<DAVResponse[]>;
Expand Down
2 changes: 1 addition & 1 deletion dist/collection.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export declare const isCollectionDirty: (params: {
fetch?: typeof fetch;
}) => Promise<{
isDirty: boolean;
newCtag: string;
newCtag: string | undefined;
}>;
/**
* This is for webdav sync-collection only
Expand Down
Loading
Loading