Skip to content
Draft
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
4 changes: 4 additions & 0 deletions .github/workflows/node-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
src:
- '.github/workflows/**'
- 'src/**'
- 'test/types/**'
- 'appinfo/info.xml'
- 'package.json'
- 'package-lock.json'
Expand Down Expand Up @@ -86,6 +87,9 @@ jobs:
npm ci
npm run build --if-present

- name: Check TypeScript consumers
run: npm run check:types

- name: Check build changes
run: |
bash -c "[[ ! \"`git status --porcelain `\" ]] || (echo 'Please recompile and commit the assets, see the section \"Show changes on failure\" for details' && exit 1)"
Expand Down
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,29 @@ npm run dev
npm run build

```
## TypeScript definitions

`npm run build` generates `dist/index.d.ts` for ES modules and
`dist/index.d.cts` for CommonJS. Development and watch builds also generate
these files. They are included in the npm package and resolved automatically:

```typescript
import DavClient from '@nextcloud/cdav-library'

const client = new DavClient({ rootUrl: 'https://example.com/remote.php/dav/' })
await client.connect({ enableCalDAV: true, enableCardDAV: true })
```

Definitions are generated from the JavaScript and JSDoc using the existing Vite
build. Update the JSDoc when changing the API; do not edit generated files in
`dist`. Dynamically exposed model properties need a JSDoc-annotated `this.property`
reference before they are installed in the constructor. Some legacy APIs still
use broad types where their JSDoc does not provide more detail.

Run `npm run test:types` to build and check strict TypeScript consumers with
NodeNext (ES modules and CommonJS) and bundler module resolution. To check an
existing build, use `npm run check:types`.

## Running tests
You can use the provided npm command to run all tests by using:

Expand Down
2 changes: 1 addition & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ SPDX-PackageSupplier = "Nextcloud <info@nextcloud.com>"
SPDX-PackageDownloadLocation = "https://github.com/nextcloud/cdav-library"

[[annotations]]
path = ["package.json", "package-lock.json", "test/assets/unit/parser/dprop.xml"]
path = ["package.json", "package-lock.json", "tsconfig.json", "test/types/*.json", "test/assets/unit/parser/dprop.xml"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2018 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "AGPL-3.0-or-later"
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 15 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@
"main": "dist/index.cjs",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"files": [
Expand All @@ -25,7 +31,9 @@
"test:watch": "vitest",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"test:types": "npm run build && npm run check:types",
"check:types": "tsc -p test/types/tsconfig.json && tsc -p test/types/tsconfig.bundler.json"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -61,9 +69,11 @@
"jsdom": "^30.0.1",
"vite": "^7.3.6",
"vitest": "^4.1.11",
"xpath-ts": "^1.3.13"
"xpath-ts": "^1.3.13",
"typescript": "^5.9.3"
},
"dependencies": {
"@nextcloud/axios": "^2.6.0"
}
},
"types": "./dist/index.d.ts"
}
10 changes: 5 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@
*/
export default class DavClient {
/**
* @param {object} options

Check warning on line 26 in src/index.js

View workflow job for this annotation

GitHub Actions / NPM lint

Missing JSDoc @PARAM "options" description
* @param {string} options.rootUrl

Check warning on line 27 in src/index.js

View workflow job for this annotation

GitHub Actions / NPM lint

Missing JSDoc @PARAM "options.rootUrl" description
* @param {{[name: string]: any}} [options.defaultHeaders] A dictionary of default headers to apply to each request.

Check warning on line 28 in src/index.js

View workflow job for this annotation

GitHub Actions / NPM lint

Prefer a more specific type to `any`
* @param {object} factories

Check warning on line 29 in src/index.js

View workflow job for this annotation

GitHub Actions / NPM lint

Missing JSDoc @PARAM "factories" description
*/
// eslint-disable-next-line no-unused-vars
constructor(options, factories = {}) {
Expand All @@ -51,7 +51,7 @@
/**
* Principal object of current user
*
* @type {Principal}
* @type {Principal|null}
*/
this.currentUserPrincipal = null

Expand Down Expand Up @@ -111,7 +111,7 @@
/**
* initializes the DAVClient
*
* @param {object} options
* @param {import('./models/principal.js').PrincipalPropfindOptions} options

Check warning on line 114 in src/index.js

View workflow job for this annotation

GitHub Actions / NPM lint

Missing JSDoc @PARAM "options" description
* @return {Promise<DavClient>}
*/
async connect(options = { enableCalDAV: false, enableCardDAV: false }) {
Expand Down Expand Up @@ -176,7 +176,7 @@
/**
* performs a principal property search based on a principal's displayname
*
* @param {string} name

Check warning on line 179 in src/index.js

View workflow job for this annotation

GitHub Actions / NPM lint

Missing JSDoc @PARAM "name" description
* @return {Promise<Principal[]>}
*/
async principalPropertySearchByDisplayname(name) {
Expand All @@ -188,7 +188,7 @@
/**
* performs a principal property search based on a principal's displayname OR email address
*
* @param {string} value

Check warning on line 191 in src/index.js

View workflow job for this annotation

GitHub Actions / NPM lint

Missing JSDoc @PARAM "value" description
* @return {Promise<Principal[]>}
*/
async principalPropertySearchByDisplaynameOrEmail(value) {
Expand Down Expand Up @@ -227,7 +227,7 @@
*
* @param {string} address Address of the building the room is in
* @param {string} story Story inside the building the room is in
* @return {Promise<[]>}
* @return {Promise<Principal[]>}
*/
async principalPropertySearchByAddressAndStory(address, story) {
const [skeleton] = XMLUtility.getRootSkeleton([NS.DAV, 'principal-property-search'])
Expand Down Expand Up @@ -388,7 +388,7 @@
*
* @param {Array} props
* @param {string} match
* @param {string} test 'anyof', 'allof' or none
* @param {'anyof'|'allof'} [test] 'anyof', 'allof' or none
* @return {Promise<Principal[]>}
*/
async principalPropertySearch(props, match, test) {
Expand Down Expand Up @@ -437,7 +437,7 @@
* finds one principal at a given principalUrl
*
* @param {string} principalUrl
* @return {Promise<Principal>}
* @return {Promise<Principal|undefined>}
*/
async findPrincipal(principalUrl) {
return this._request.propFind(principalUrl, Principal.getPropFindList()).then(({ body }) => {
Expand Down
16 changes: 15 additions & 1 deletion src/models/addressBook.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ const debug = debugFactory('AddressBook')
*
* The first two allowing read-write access
*
* @augments DavCollection
*/
export class AddressBook extends davCollectionShareable(DavCollection) {
/**
Expand All @@ -41,6 +40,21 @@ export class AddressBook extends davCollectionShareable(DavCollection) {
super._registerObjectFactory('text/vcard', VCard)
super._registerPropSetFactory(addressBookPropSet)

// Type declarations for properties installed dynamically below.
/**
* @type {string | undefined}
*/
this.description
/**
* @type {boolean | undefined}
*/
this.enabled
/**
* @type {boolean | undefined}
* @readonly
*/
this.readOnly

super._exposeProperty('description', NS.IETF_CARDDAV, 'addressbook-description', true)
super._exposeProperty('enabled', NS.OWNCLOUD, 'enabled', true)
super._exposeProperty('readOnly', NS.OWNCLOUD, 'read-only')
Expand Down
44 changes: 41 additions & 3 deletions src/models/calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ const debug = debugFactory('Calendar')
*
* The first seven allowing read-write access
*
* @augments DavCollection
*/
export class Calendar extends davCollectionPublishable(davCollectionShareable(DavCollection)) {
/**
Expand All @@ -48,6 +47,45 @@ export class Calendar extends davCollectionPublishable(davCollectionShareable(Da
super._registerObjectFactory('text/calendar', VObject)
super._registerPropSetFactory(calendarPropSet)

// Type declarations for properties installed dynamically below.
/**
* @type {string | undefined}
*/
this.color
/**
* @type {boolean | undefined}
*/
this.enabled
/**
* @type {number | undefined}
*/
this.order
/**
* @type {string | undefined}
*/
this.timezone
/**
* @type {string[] | undefined}
* @readonly
*/
this.components
/**
* @type {string | undefined}
*/
this.transparency
/**
* @type {number | undefined}
*/
this.defaultAlarmPartDay
/**
* @type {number | undefined}
*/
this.defaultAlarmFullDay
/**
* @type {boolean | undefined}
*/
this.disableAlarmNotifications

super._exposeProperty('color', NS.APPLE, 'calendar-color', true)
super._exposeProperty('enabled', NS.OWNCLOUD, 'calendar-enabled', true)
super._exposeProperty('order', NS.APPLE, 'calendar-order', true)
Expand Down Expand Up @@ -92,7 +130,7 @@ export class Calendar extends davCollectionPublishable(davCollectionShareable(Da
/**
* find all VObjects in a time-range filtered by type
*
* @param {number} type
* @param {string} type
* @param {Date} from
* @param {Date} to
* @return {Promise<VObject[]>}
Expand Down Expand Up @@ -122,7 +160,7 @@ export class Calendar extends davCollectionPublishable(davCollectionShareable(Da
/**
* create a VObject inside this calendar
*
* @param data
* @param {string} data
* @return {Promise<VObject>}
*/
async createVObject(data) {
Expand Down
4 changes: 4 additions & 0 deletions src/models/calendarHome.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

/**
* @typedef {import('./vobject.js').VObject} VObject
*/

import { debugFactory } from '../debug.js'
import * as NS from '../utility/namespaceUtility.js'
import * as XMLUtility from '../utility/xmlUtility.js'
Expand Down
7 changes: 7 additions & 0 deletions src/models/calendarTrashBin.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ export class CalendarTrashBin extends DavCollection {

super._registerObjectFactory('text/calendar', DeletedCalendarObject)

// Type declarations for properties installed dynamically below.
/**
* @type {number | undefined}
* @readonly
*/
this.retentionDuration

super._exposeProperty('retentionDuration', NS.NEXTCLOUD, 'trash-bin-retention-duration')
}

Expand Down
35 changes: 35 additions & 0 deletions src/models/davCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

/**
* @typedef {import('../request.js').default} Request
*/

import { debugFactory } from '../debug.js'
import davCollectionPropSet from '../propset/davCollectionPropSet.js'
import * as NS from '../utility/namespaceUtility.js'
Expand Down Expand Up @@ -52,6 +56,37 @@ export class DavCollection extends DAVEventListener {

this._registerPropSetFactory(davCollectionPropSet)

// Type declarations for properties installed dynamically below.
/**
* @type {string | undefined}
*/
this.displayname
/**
* @type {string | undefined}
* @readonly
*/
this.owner
/**
* @type {string[] | undefined}
* @readonly
*/
this.resourcetype
/**
* @type {string | undefined}
* @readonly
*/
this.syncToken
/**
* @type {string[] | undefined}
* @readonly
*/
this.currentUserPrivilegeSet
/**
* @type {string}
* @readonly
*/
this.url

this._exposeProperty('displayname', NS.DAV, 'displayname', true)
this._exposeProperty('owner', NS.DAV, 'owner')
this._exposeProperty('resourcetype', NS.DAV, 'resourcetype')
Expand Down
15 changes: 13 additions & 2 deletions src/models/davCollectionPublishable.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,24 @@ const debug = debugFactory('DavCollectionPublishable')

/**
*
* @param Base
* @template {new (...args: any[]) => object} T
* @param {T} Base
*/
export function davCollectionPublishable(Base) {
return class extends Base {
class Publishable extends Base {
/**
* @inheritDoc
*/
constructor(...args) {
super(...args)

// Type declarations for properties installed dynamically below.
/**
* @type {string | undefined}
* @readonly
*/
this.publishURL

super._exposeProperty('publishURL', NS.CALENDARSERVER, 'publish-url')
}

Expand Down Expand Up @@ -68,4 +76,7 @@ export function davCollectionPublishable(Base) {
])
}
}

// Keep inferred member types without the implicit index signature of a JS mixin.
return /** @type {T & (new (...args: any[]) => Pick<Publishable, 'publishURL' | 'publish' | 'unpublish'>)} */ (Publishable)
}
Loading
Loading