mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 21:11:30 +08:00 
			
		
		
		
	Merge branch 'develop' into shortcuts-i18n
This commit is contained in:
		
						commit
						555c596a0f
					
				| @ -40,7 +40,7 @@ | ||||
|     "@types/express": "5.0.1",     | ||||
|     "@types/node": "22.15.29",     | ||||
|     "@types/yargs": "17.0.33", | ||||
|     "@vitest/coverage-v8": "3.2.0", | ||||
|     "@vitest/coverage-v8": "3.2.1", | ||||
|     "eslint": "9.28.0", | ||||
|     "eslint-plugin-simple-import-sort": "12.1.1", | ||||
|     "esm": "3.2.25", | ||||
|  | ||||
| @ -1,52 +0,0 @@ | ||||
| POST {{triliumHost}}/etapi/create-note | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "parentNoteId": "root", | ||||
|   "title": "Hello", | ||||
|   "type": "text", | ||||
|   "content": "Hi there!" | ||||
| } | ||||
| 
 | ||||
| > {% client.global.set("createdNoteId", response.body.note.noteId); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/attachments | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "ownerId": "{{createdNoteId}}", | ||||
|   "role": "file", | ||||
|   "mime": "text/plain", | ||||
|   "title": "my attachment", | ||||
|   "content": "text" | ||||
| } | ||||
| 
 | ||||
| > {% client.global.set("createdAttachmentId", response.body.attachmentId); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| DELETE {{triliumHost}}/etapi/attachments/{{createdAttachmentId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 204, "Response status is not 204"); %} | ||||
| 
 | ||||
| ### repeat the DELETE request to test the idempotency | ||||
| 
 | ||||
| DELETE {{triliumHost}}/etapi/attachments/{{createdAttachmentId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 204, "Response status is not 204"); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/attachments/{{createdAttachmentId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 404, "Response status is not 404"); | ||||
|     client.assert(response.body.code === "ATTACHMENT_NOT_FOUND"); | ||||
| %} | ||||
| @ -1,52 +0,0 @@ | ||||
| POST {{triliumHost}}/etapi/create-note | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "parentNoteId": "root", | ||||
|   "title": "Hello", | ||||
|   "type": "text", | ||||
|   "content": "Hi there!" | ||||
| } | ||||
| 
 | ||||
| > {% client.global.set("createdNoteId", response.body.note.noteId); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/attributes | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "noteId": "{{createdNoteId}}", | ||||
|   "type": "label", | ||||
|   "name": "mylabel", | ||||
|   "value": "val", | ||||
|   "isInheritable": true | ||||
| } | ||||
| 
 | ||||
| > {% client.global.set("createdAttributeId", response.body.attributeId); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| DELETE {{triliumHost}}/etapi/attributes/{{createdAttributeId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 204, "Response status is not 204"); %} | ||||
| 
 | ||||
| ### repeat the DELETE request to test the idempotency | ||||
| 
 | ||||
| DELETE {{triliumHost}}/etapi/attributes/{{createdAttributeId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 204, "Response status is not 204"); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/attributes/{{createdAttributeId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 404, "Response status is not 404"); | ||||
|     client.assert(response.body.code === "ATTRIBUTE_NOT_FOUND"); | ||||
| %} | ||||
| @ -1,87 +0,0 @@ | ||||
| POST {{triliumHost}}/etapi/create-note | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "parentNoteId": "root", | ||||
|   "title": "Hello", | ||||
|   "type": "text", | ||||
|   "content": "Hi there!" | ||||
| } | ||||
| 
 | ||||
| > {% | ||||
|     client.global.set("createdNoteId", response.body.note.noteId); | ||||
|     client.global.set("createdBranchId", response.body.branch.branchId); | ||||
| %} | ||||
| 
 | ||||
| ### Clone to another location | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/branches | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "noteId": "{{createdNoteId}}", | ||||
|   "parentNoteId": "_hidden" | ||||
| } | ||||
| 
 | ||||
| > {% client.global.set("clonedBranchId", response.body.branchId); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/notes/{{createdNoteId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/branches/{{createdBranchId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/branches/{{clonedBranchId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| DELETE {{triliumHost}}/etapi/branches/{{createdBranchId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 204, "Response status is not 204"); %} | ||||
| 
 | ||||
| ### repeat the DELETE request to test the idempotency | ||||
| 
 | ||||
| DELETE {{triliumHost}}/etapi/branches/{{createdBranchId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 204, "Response status is not 204"); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/branches/{{createdBranchId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 404, "Response status is not 404"); | ||||
|     client.assert(response.body.code === "BRANCH_NOT_FOUND"); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/branches/{{clonedBranchId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/notes/{{createdNoteId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| @ -1,126 +0,0 @@ | ||||
| POST {{triliumHost}}/etapi/create-note | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "parentNoteId": "root", | ||||
|   "title": "Hello", | ||||
|   "type": "text", | ||||
|   "content": "Hi there!" | ||||
| } | ||||
| 
 | ||||
| > {% | ||||
|     client.global.set("createdNoteId", response.body.note.noteId); | ||||
|     client.global.set("createdBranchId", response.body.branch.branchId); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/attributes | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "noteId": "{{createdNoteId}}", | ||||
|   "type": "label", | ||||
|   "name": "mylabel", | ||||
|   "value": "val", | ||||
|   "isInheritable": true | ||||
| } | ||||
| 
 | ||||
| > {% client.global.set("createdAttributeId", response.body.attributeId); %} | ||||
| 
 | ||||
| ### Clone to another location | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/branches | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "noteId": "{{createdNoteId}}", | ||||
|   "parentNoteId": "_hidden" | ||||
| } | ||||
| 
 | ||||
| > {% client.global.set("clonedBranchId", response.body.branchId); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/notes/{{createdNoteId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/branches/{{createdBranchId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/branches/{{clonedBranchId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/attributes/{{createdAttributeId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| DELETE {{triliumHost}}/etapi/notes/{{createdNoteId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 204, "Response status is not 204"); %} | ||||
| 
 | ||||
| ### repeat the DELETE request to test the idempotency | ||||
| 
 | ||||
| DELETE {{triliumHost}}/etapi/notes/{{createdNoteId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 204, "Response status is not 204"); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/branches/{{createdBranchId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 404, "Response status is not 404"); | ||||
|     client.assert(response.body.code === "BRANCH_NOT_FOUND"); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/branches/{{clonedBranchId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 404, "Response status is not 404"); | ||||
|     client.assert(response.body.code == "BRANCH_NOT_FOUND"); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/notes/{{createdNoteId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 404, "Response status is not 404"); | ||||
|     client.assert(response.body.code === "NOTE_NOT_FOUND"); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/attributes/{{createdAttributeId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 404, "Response status is not 404"); | ||||
|     client.assert(response.body.code === "ATTRIBUTE_NOT_FOUND"); | ||||
| %} | ||||
| @ -1,72 +0,0 @@ | ||||
| GET {{triliumHost}}/etapi/inbox/2022-01-01 | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/calendar/days/2022-01-01 | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/calendar/days/2022-1 | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 400); | ||||
|     client.assert(response.body.code === "DATE_INVALID"); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/calendar/weeks/2022-01-01 | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/calendar/weeks/2022-1 | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 400); | ||||
|     client.assert(response.body.code === "DATE_INVALID"); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/calendar/months/2022-01 | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/calendar/months/2022-1 | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 400); | ||||
|     client.assert(response.body.code === "MONTH_INVALID"); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/calendar/years/2022 | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% client.assert(response.status === 200); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/calendar/years/202 | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 400); | ||||
|     client.assert(response.body.code === "YEAR_INVALID"); | ||||
| %} | ||||
| @ -1,116 +0,0 @@ | ||||
| POST {{triliumHost}}/etapi/create-note | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "parentNoteId": "root", | ||||
|   "title": "Hello parent", | ||||
|   "type": "text", | ||||
|   "content": "Hi there!" | ||||
| } | ||||
| 
 | ||||
| > {% | ||||
| client.assert(response.status === 201); | ||||
| client.global.set("parentNoteId", response.body.note.noteId); | ||||
| client.global.set("parentBranchId", response.body.branch.branchId); | ||||
| %} | ||||
| 
 | ||||
| ### Create inheritable parent attribute | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/attributes | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "noteId": "{{parentNoteId}}", | ||||
|   "type": "label", | ||||
|   "name": "mylabel", | ||||
|   "value": "", | ||||
|   "isInheritable": true, | ||||
|   "position": 10 | ||||
| } | ||||
| 
 | ||||
| > {% | ||||
| client.assert(response.status === 201); | ||||
| client.global.set("parentAttributeId", response.body.attributeId); | ||||
| %} | ||||
| 
 | ||||
| ### Create child note under root | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/create-note | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "parentNoteId": "root", | ||||
|   "title": "Hello child", | ||||
|   "type": "text", | ||||
|   "content": "Hi there!" | ||||
| } | ||||
| 
 | ||||
| > {% | ||||
| client.assert(response.status === 201); | ||||
| client.global.set("childNoteId", response.body.note.noteId); | ||||
| client.global.set("childBranchId", response.body.branch.branchId); | ||||
| %} | ||||
| 
 | ||||
| ### Create child attribute | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/attributes | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "noteId": "{{childNoteId}}", | ||||
|   "type": "label", | ||||
|   "name": "mylabel", | ||||
|   "value": "val", | ||||
|   "isInheritable": false, | ||||
|   "position": 10 | ||||
| } | ||||
| 
 | ||||
| > {% | ||||
| client.assert(response.status === 201); | ||||
| client.global.set("childAttributeId", response.body.attributeId); | ||||
| %} | ||||
| 
 | ||||
| ### Clone child to parent | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/branches | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "noteId": "{{childNoteId}}", | ||||
|   "parentNoteId": "{{parentNoteId}}" | ||||
| } | ||||
| 
 | ||||
| > {% | ||||
| client.assert(response.status === 201); | ||||
| client.assert(response.body.parentNoteId == client.global.get("parentNoteId")); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/notes/{{childNoteId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
| 
 | ||||
| function hasAttribute(list, attributeId) { | ||||
|   for (let i = 0; i < list.length; i++) { | ||||
|     if (list[i]["attributeId"] === attributeId) { | ||||
|       return true; | ||||
|     } | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| client.log(JSON.stringify(response.body.attributes)); | ||||
| 
 | ||||
| client.assert(response.status === 200); | ||||
| client.assert(response.body.noteId == client.global.get("childNoteId")); | ||||
| client.assert(response.body.attributes.length == 2); | ||||
| client.assert(hasAttribute(response.body.attributes, client.global.get("parentAttributeId"))); | ||||
| client.assert(hasAttribute(response.body.attributes, client.global.get("childAttributeId"))); | ||||
| %} | ||||
| @ -1,61 +0,0 @@ | ||||
| POST {{triliumHost}}/etapi/create-note | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "parentNoteId": "root", | ||||
|   "title": "GetInheritedAttributes Test Note", | ||||
|   "type": "text", | ||||
|   "content": "Hi there!" | ||||
| } | ||||
| 
 | ||||
| > {% | ||||
|     client.assert(response.status === 201); | ||||
|     client.global.set("parentNoteId", response.body.note.noteId); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/attributes | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "noteId": "{{parentNoteId}}", | ||||
|   "type": "label", | ||||
|   "name": "mylabel", | ||||
|   "value": "val", | ||||
|   "isInheritable": true | ||||
| } | ||||
| 
 | ||||
| > {% client.global.set("createdAttributeId", response.body.attributeId); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/create-note | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "parentNoteId": "{{parentNoteId}}", | ||||
|   "title": "Hello", | ||||
|   "type": "text", | ||||
|   "content": "Hi there!" | ||||
| } | ||||
| 
 | ||||
| > {% | ||||
| client.global.set("createdNoteId", response.body.note.noteId); | ||||
| client.global.set("createdBranchId", response.body.branch.branchId); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/notes/{{createdNoteId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {% | ||||
| client.assert(response.status === 200); | ||||
| client.assert(response.body.noteId == client.global.get("createdNoteId")); | ||||
| client.assert(response.body.attributes.length == 1); | ||||
| client.assert(response.body.attributes[0].attributeId == client.global.get("createdAttributeId")); | ||||
| %} | ||||
| @ -1,5 +0,0 @@ | ||||
| { | ||||
|   "dev": { | ||||
|     "triliumHost": "http://localhost:37740" | ||||
|   } | ||||
| } | ||||
| @ -1,80 +0,0 @@ | ||||
| POST {{triliumHost}}/etapi/create-note | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "parentNoteId": "root", | ||||
|   "title": "Hello", | ||||
|   "type": "text", | ||||
|   "content": "Hi there!" | ||||
| } | ||||
| 
 | ||||
| > {% | ||||
|     client.global.set("createdNoteId", response.body.note.noteId); | ||||
|     client.global.set("createdBranchId", response.body.branch.branchId); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| POST {{triliumHost}}/etapi/attributes | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "noteId": "{{createdNoteId}}", | ||||
|   "type": "label", | ||||
|   "name": "mylabel", | ||||
|   "value": "val", | ||||
|   "isInheritable": true | ||||
| } | ||||
| 
 | ||||
| > {% client.global.set("createdAttributeId", response.body.attributeId); %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| PATCH {{triliumHost}}/etapi/attributes/{{createdAttributeId}} | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "value": "CHANGED" | ||||
| } | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| GET {{triliumHost}}/etapi/attributes/{{createdAttributeId}} | ||||
| Authorization: {{authToken}} | ||||
| 
 | ||||
| > {%         | ||||
| client.assert(response.body.value === "CHANGED"); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| PATCH {{triliumHost}}/etapi/attributes/{{createdAttributeId}} | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "noteId": "root" | ||||
| } | ||||
| 
 | ||||
| > {%  | ||||
|     client.assert(response.status === 400);  | ||||
|     client.assert(response.body.code == "PROPERTY_NOT_ALLOWED"); | ||||
| %} | ||||
| 
 | ||||
| ### | ||||
| 
 | ||||
| PATCH {{triliumHost}}/etapi/attributes/{{createdAttributeId}} | ||||
| Authorization: {{authToken}} | ||||
| Content-Type: application/json | ||||
| 
 | ||||
| { | ||||
|   "value": null | ||||
| } | ||||
| 
 | ||||
| > {%  | ||||
|     client.assert(response.status === 400);  | ||||
|     client.assert(response.body.code == "PROPERTY_VALIDATION_ERROR"); | ||||
| %} | ||||
| @ -222,7 +222,6 @@ export function buildFloatingToolbar() { | ||||
|                 "|", | ||||
|                 "code", | ||||
|                 "link", | ||||
|                 "bookmark", | ||||
|                 "removeFormat", | ||||
|                 "internallink", | ||||
|                 "cuttonote" | ||||
| @ -244,7 +243,7 @@ export function buildFloatingToolbar() { | ||||
|             { | ||||
|                 label: "Insert", | ||||
|                 icon: "plus", | ||||
|                 items: ["internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak"] | ||||
|                 items: ["bookmark", "internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak"] | ||||
|             }, | ||||
|             "|", | ||||
|             "outdent", | ||||
|  | ||||
| @ -85,10 +85,10 @@ | ||||
|     "jsdom": "26.1.0", | ||||
|     "marked": "15.0.12", | ||||
|     "mime-types": "3.0.1", | ||||
|     "multer": "2.0.0", | ||||
|     "multer": "2.0.1", | ||||
|     "normalize-strings": "1.1.1", | ||||
|     "ollama": "0.5.16", | ||||
|     "openai": "5.0.2", | ||||
|     "openai": "5.1.0", | ||||
|     "rand-token": "1.0.1", | ||||
|     "safe-compare": "1.1.4", | ||||
|     "sanitize-filename": "1.6.3", | ||||
|  | ||||
| @ -21,6 +21,6 @@ describe("etapi/backup", () => { | ||||
|         const response = await supertest(app) | ||||
|             .put("/etapi/backup/etapi_test") | ||||
|             .auth(USER, token, { "type": "basic"}) | ||||
|             .expect(201); | ||||
|             .expect(204); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
							
								
								
									
										172
									
								
								apps/server/spec/etapi/delete-entities.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								apps/server/spec/etapi/delete-entities.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,172 @@ | ||||
| import { Application } from "express"; | ||||
| import { beforeAll, beforeEach, describe, expect, it } from "vitest"; | ||||
| import supertest from "supertest"; | ||||
| import { login } from "./utils.js"; | ||||
| import config from "../../src/services/config.js"; | ||||
| import { randomInt } from "crypto"; | ||||
| 
 | ||||
| let app: Application; | ||||
| let token: string; | ||||
| let createdNoteId: string; | ||||
| let createdBranchId: string; | ||||
| 
 | ||||
| const USER = "etapi"; | ||||
| 
 | ||||
| type EntityType = "attachments" | "attributes" | "branches" | "notes"; | ||||
| 
 | ||||
| describe("etapi/delete-entities", () => { | ||||
|     beforeAll(async () => { | ||||
|         config.General.noAuthentication = false; | ||||
|         const buildApp = (await (import("../../src/app.js"))).default; | ||||
|         app = await buildApp(); | ||||
|         token = await login(app); | ||||
|     }); | ||||
| 
 | ||||
|     beforeEach(async () => { | ||||
|         ({ createdNoteId, createdBranchId } = await createNote()); | ||||
|     }); | ||||
| 
 | ||||
|     it("deletes attachment", async () => { | ||||
|         const attachmentId = await createAttachment(); | ||||
|         await deleteEntity("attachments", attachmentId); | ||||
|         await expectNotFound("attachments", attachmentId); | ||||
|     }); | ||||
| 
 | ||||
|     it("deletes attribute", async () => { | ||||
|         const attributeId = await createAttribute(); | ||||
|         await deleteEntity("attributes", attributeId); | ||||
|         await expectNotFound("attributes", attributeId); | ||||
|     }); | ||||
| 
 | ||||
|     it("deletes cloned branch", async () => { | ||||
|         const clonedBranchId = await createClone(); | ||||
| 
 | ||||
|         await expectFound("branches", createdBranchId); | ||||
|         await expectFound("branches", clonedBranchId); | ||||
| 
 | ||||
|         await deleteEntity("branches", createdBranchId); | ||||
|         await expectNotFound("branches", createdBranchId); | ||||
| 
 | ||||
|         await expectFound("branches", clonedBranchId); | ||||
|         await expectFound("notes", createdNoteId); | ||||
|     }); | ||||
| 
 | ||||
|     it("deletes note with all branches", async () => { | ||||
|         const attributeId = await createAttribute(); | ||||
| 
 | ||||
|         const clonedBranchId = await createClone(); | ||||
| 
 | ||||
|         await expectFound("notes", createdNoteId); | ||||
|         await expectFound("branches", createdBranchId); | ||||
|         await expectFound("branches", clonedBranchId); | ||||
|         await expectFound("attributes", attributeId); | ||||
|         await deleteEntity("notes", createdNoteId); | ||||
| 
 | ||||
|         await expectNotFound("branches", createdBranchId); | ||||
|         await expectNotFound("branches", clonedBranchId); | ||||
|         await expectNotFound("notes", createdNoteId); | ||||
|         await expectNotFound("attributes", attributeId); | ||||
|     }); | ||||
| }); | ||||
| 
 | ||||
| async function createNote() { | ||||
|     const noteId = `forcedId${randomInt(1000)}`; | ||||
|     const response = await supertest(app) | ||||
|         .post("/etapi/create-note") | ||||
|         .auth(USER, token, { "type": "basic"}) | ||||
|         .send({ | ||||
|             "noteId": noteId, | ||||
|             "parentNoteId": "root", | ||||
|             "title": "Hello", | ||||
|             "type": "text", | ||||
|             "content": "Hi there!", | ||||
|             "dateCreated": "2023-08-21 23:38:51.123+0200", | ||||
|             "utcDateCreated": "2023-08-21 23:38:51.123Z" | ||||
|         }) | ||||
|         .expect(201); | ||||
|     expect(response.body.note.noteId).toStrictEqual(noteId); | ||||
| 
 | ||||
|     return { | ||||
|         createdNoteId: response.body.note.noteId, | ||||
|         createdBranchId: response.body.branch.branchId | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| async function createClone() { | ||||
|     const response = await supertest(app) | ||||
|         .post("/etapi/branches") | ||||
|         .auth(USER, token, { "type": "basic"}) | ||||
|         .send({ | ||||
|             noteId: createdNoteId, | ||||
|             parentNoteId: "_hidden" | ||||
|         }) | ||||
|         .expect(201); | ||||
|     expect(response.body.parentNoteId).toStrictEqual("_hidden"); | ||||
|     return response.body.branchId; | ||||
| } | ||||
| 
 | ||||
| async function createAttribute() { | ||||
|     const attributeId = `forcedId${randomInt(1000)}`; | ||||
|     const response = await supertest(app) | ||||
|         .post("/etapi/attributes") | ||||
|         .auth(USER, token, { "type": "basic"}) | ||||
|         .send({ | ||||
|             "attributeId": attributeId, | ||||
|             "noteId": createdNoteId, | ||||
|             "type": "label", | ||||
|             "name": "mylabel", | ||||
|             "value": "val", | ||||
|             "isInheritable": true | ||||
|         }) | ||||
|         .expect(201); | ||||
|     expect(response.body.attributeId).toStrictEqual(attributeId); | ||||
|     return response.body.attributeId; | ||||
| } | ||||
| 
 | ||||
| async function createAttachment() { | ||||
|     const response = await supertest(app) | ||||
|         .post("/etapi/attachments") | ||||
|         .auth(USER, token, { "type": "basic"}) | ||||
|         .send({ | ||||
|             "ownerId": createdNoteId, | ||||
|             "role": "file", | ||||
|             "mime": "plain/text", | ||||
|             "title": "my attachment", | ||||
|             "content": "my text" | ||||
|         }) | ||||
|         .expect(201); | ||||
|     return response.body.attachmentId; | ||||
| } | ||||
| 
 | ||||
| async function deleteEntity(entity: EntityType, id: string) { | ||||
|     // Delete twice to test idempotency.
 | ||||
|     for (let i=0; i < 2; i++) { | ||||
|         await supertest(app) | ||||
|             .delete(`/etapi/${entity}/${id}`) | ||||
|             .auth(USER, token, { "type": "basic"}) | ||||
|             .expect(204); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const MISSING_ENTITY_ERROR_CODES: Record<EntityType, string> = { | ||||
|     attachments: "ATTACHMENT_NOT_FOUND", | ||||
|     attributes: "ATTRIBUTE_NOT_FOUND", | ||||
|     branches: "BRANCH_NOT_FOUND", | ||||
|     notes: "NOTE_NOT_FOUND" | ||||
| } | ||||
| 
 | ||||
| async function expectNotFound(entity: EntityType, id: string) { | ||||
|     const response = await supertest(app) | ||||
|             .get(`/etapi/${entity}/${id}`) | ||||
|             .auth(USER, token, { "type": "basic"}) | ||||
|             .expect(404); | ||||
| 
 | ||||
|     expect(response.body.code).toStrictEqual(MISSING_ENTITY_ERROR_CODES[entity]); | ||||
| } | ||||
| 
 | ||||
| async function expectFound(entity: EntityType, id: string) { | ||||
|     await supertest(app) | ||||
|             .get(`/etapi/${entity}/${id}`) | ||||
|             .auth(USER, token, { "type": "basic"}) | ||||
|             .expect(200); | ||||
| } | ||||
							
								
								
									
										103
									
								
								apps/server/spec/etapi/get-date-notes.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								apps/server/spec/etapi/get-date-notes.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | ||||
| import { beforeAll, describe, expect, it } from "vitest"; | ||||
| import config from "../../src/services/config.js"; | ||||
| import { login } from "./utils.js"; | ||||
| import { Application } from "express"; | ||||
| import supertest from "supertest"; | ||||
| import date_notes from "../../src/services/date_notes.js"; | ||||
| import cls from "../../src/services/cls.js"; | ||||
| 
 | ||||
| let app: Application; | ||||
| let token: string; | ||||
| 
 | ||||
| const USER = "etapi"; | ||||
| 
 | ||||
| describe("etapi/get-date-notes", () => { | ||||
|     beforeAll(async () => { | ||||
|         config.General.noAuthentication = false; | ||||
|         const buildApp = (await (import("../../src/app.js"))).default; | ||||
|         app = await buildApp(); | ||||
|         token = await login(app); | ||||
|     }); | ||||
| 
 | ||||
|     it("obtains inbox", async () => { | ||||
|         await supertest(app) | ||||
|             .get("/etapi/inbox/2022-01-01") | ||||
|             .auth(USER, token, { "type": "basic"}) | ||||
|             .expect(200); | ||||
|     }); | ||||
| 
 | ||||
|     describe("days", () => { | ||||
|         it("obtains day from calendar", async () => { | ||||
|             await supertest(app) | ||||
|                 .get("/etapi/calendar/days/2022-01-01") | ||||
|                 .auth(USER, token, { "type": "basic"}) | ||||
|                 .expect(200); | ||||
|         }); | ||||
| 
 | ||||
|         it("detects invalid date", async () => { | ||||
|             const response = await supertest(app) | ||||
|                 .get("/etapi/calendar/days/2022-1") | ||||
|                 .auth(USER, token, { "type": "basic"}) | ||||
|                 .expect(400); | ||||
|             expect(response.body.code).toStrictEqual("DATE_INVALID"); | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|     describe("weeks", () => { | ||||
|         beforeAll(() => { | ||||
|             cls.init(() => { | ||||
|                 const rootCalendarNote = date_notes.getRootCalendarNote(); | ||||
|                 rootCalendarNote.setLabel("enableWeekNote"); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         it("obtains week calendar", async () => { | ||||
|             await supertest(app) | ||||
|                 .get("/etapi/calendar/weeks/2022-W01") | ||||
|                 .auth(USER, token, { "type": "basic"}) | ||||
|                 .expect(200); | ||||
|         }); | ||||
| 
 | ||||
|         it("detects invalid date", async () => { | ||||
|             const response = await supertest(app) | ||||
|                 .get("/etapi/calendar/weeks/2022-1") | ||||
|                 .auth(USER, token, { "type": "basic"}) | ||||
|                 .expect(400); | ||||
|             expect(response.body.code).toStrictEqual("WEEK_INVALID"); | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|     describe("months", () => { | ||||
|         it("obtains month calendar", async () => { | ||||
|             await supertest(app) | ||||
|                 .get("/etapi/calendar/months/2022-01") | ||||
|                 .auth(USER, token, { "type": "basic"}) | ||||
|                 .expect(200); | ||||
|         }); | ||||
| 
 | ||||
|         it("detects invalid month", async () => { | ||||
|             const response = await supertest(app) | ||||
|                 .get("/etapi/calendar/months/2022-1") | ||||
|                 .auth(USER, token, { "type": "basic"}) | ||||
|                 .expect(400); | ||||
|             expect(response.body.code).toStrictEqual("MONTH_INVALID"); | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|     describe("years", () => { | ||||
|         it("obtains year calendar", async () => { | ||||
|             await supertest(app) | ||||
|                 .get("/etapi/calendar/years/2022") | ||||
|                 .auth(USER, token, { "type": "basic"}) | ||||
|                 .expect(200); | ||||
|         }); | ||||
| 
 | ||||
|         it("detects invalid year", async () => { | ||||
|             const response = await supertest(app) | ||||
|                 .get("/etapi/calendar/years/202") | ||||
|                 .auth(USER, token, { "type": "basic"}) | ||||
|                 .expect(400); | ||||
|             expect(response.body.code).toStrictEqual("YEAR_INVALID"); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| @ -0,0 +1,98 @@ | ||||
| import { Application } from "express"; | ||||
| import { beforeAll, describe, expect, it } from "vitest"; | ||||
| import supertest from "supertest"; | ||||
| import { createNote, login } from "./utils.js"; | ||||
| import config from "../../src/services/config.js"; | ||||
| 
 | ||||
| let app: Application; | ||||
| let token: string; | ||||
| 
 | ||||
| let parentNoteId: string; | ||||
| 
 | ||||
| describe("etapi/get-inherited-attribute-cloned", () => { | ||||
|     beforeAll(async () => { | ||||
|         config.General.noAuthentication = false; | ||||
|         const buildApp = (await (import("../../src/app.js"))).default; | ||||
|         app = await buildApp(); | ||||
|         token = await login(app); | ||||
| 
 | ||||
|         parentNoteId = await createNote(app, token); | ||||
|     }); | ||||
| 
 | ||||
|     it("gets inherited attribute", async () => { | ||||
|         // Create an inheritable attribute on the parent note.
 | ||||
|         let response = await supertest(app) | ||||
|             .post("/etapi/attributes") | ||||
|             .auth("etapi", token, { "type": "basic"}) | ||||
|             .send({ | ||||
|                 "noteId": parentNoteId, | ||||
|                 "type": "label", | ||||
|                 "name": "mylabel", | ||||
|                 "value": "val", | ||||
|                 "isInheritable": true, | ||||
|                 "position": 10 | ||||
|             }) | ||||
|             .expect(201); | ||||
|         const parentAttributeId = response.body.attributeId; | ||||
|         expect(parentAttributeId).toBeTruthy(); | ||||
| 
 | ||||
|         // Create a subnote.
 | ||||
|         response = await supertest(app) | ||||
|             .post("/etapi/create-note") | ||||
|             .auth("etapi", token, { "type": "basic"}) | ||||
|             .send({ | ||||
|                 "parentNoteId": parentNoteId, | ||||
|                 "title": "Hello", | ||||
|                 "type": "text", | ||||
|                 "content": "Hi there!" | ||||
|             }) | ||||
|             .expect(201); | ||||
|         const childNoteId = response.body.note.noteId; | ||||
| 
 | ||||
|         // Create child attribute
 | ||||
|         response = await supertest(app) | ||||
|             .post("/etapi/attributes") | ||||
|             .auth("etapi", token, { "type": "basic"}) | ||||
|             .send({ | ||||
|                 "noteId": childNoteId, | ||||
|                 "type": "label", | ||||
|                 "name": "mylabel", | ||||
|                 "value": "val", | ||||
|                 "isInheritable": false, | ||||
|                 "position": 10 | ||||
|             }) | ||||
|             .expect(201); | ||||
|         const childAttributeId = response.body.attributeId; | ||||
|         expect(parentAttributeId).toBeTruthy(); | ||||
| 
 | ||||
|         // Clone child to parent
 | ||||
|         response = await supertest(app) | ||||
|             .post("/etapi/branches") | ||||
|             .auth("etapi", token, { "type": "basic"}) | ||||
|             .send({ | ||||
|                 noteId: childNoteId, | ||||
|                 parentNoteId: parentNoteId | ||||
|             }) | ||||
|             .expect(200); | ||||
|         parentNoteId = response.body.parentNoteId; | ||||
| 
 | ||||
|         // Check attribute IDs
 | ||||
|         response = await supertest(app) | ||||
|             .get(`/etapi/notes/${childNoteId}`) | ||||
|             .auth("etapi", token, { "type": "basic"}) | ||||
|             .expect(200); | ||||
|         expect(response.body.noteId).toStrictEqual(childNoteId); | ||||
|         expect(response.body.attributes).toHaveLength(2); | ||||
|         expect(hasAttribute(response.body.attributes, parentAttributeId)); | ||||
|         expect(hasAttribute(response.body.attributes, childAttributeId)); | ||||
|     }); | ||||
| 
 | ||||
|     function hasAttribute(list: object[], attributeId: string) { | ||||
|         for (let i = 0; i < list.length; i++) { | ||||
|                 if (list[i]["attributeId"] === attributeId) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| }); | ||||
							
								
								
									
										60
									
								
								apps/server/spec/etapi/get-inherited-attribute.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								apps/server/spec/etapi/get-inherited-attribute.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | ||||
| import { Application } from "express"; | ||||
| import { beforeAll, describe, expect, it } from "vitest"; | ||||
| import supertest from "supertest"; | ||||
| import { createNote, login } from "./utils.js"; | ||||
| import config from "../../src/services/config.js"; | ||||
| 
 | ||||
| let app: Application; | ||||
| let token: string; | ||||
| 
 | ||||
| let parentNoteId: string; | ||||
| 
 | ||||
| describe("etapi/get-inherited-attribute", () => { | ||||
|     beforeAll(async () => { | ||||
|         config.General.noAuthentication = false; | ||||
|         const buildApp = (await (import("../../src/app.js"))).default; | ||||
|         app = await buildApp(); | ||||
|         token = await login(app); | ||||
| 
 | ||||
|         parentNoteId = await createNote(app, token); | ||||
|     }); | ||||
| 
 | ||||
|     it("gets inherited attribute", async () => { | ||||
|         // Create an inheritable attribute on the parent note.
 | ||||
|         let response = await supertest(app) | ||||
|             .post("/etapi/attributes") | ||||
|             .auth("etapi", token, { "type": "basic"}) | ||||
|             .send({ | ||||
|                 "noteId": parentNoteId, | ||||
|                 "type": "label", | ||||
|                 "name": "mylabel", | ||||
|                 "value": "val", | ||||
|                 "isInheritable": true | ||||
|             }) | ||||
|             .expect(201); | ||||
|         const createdAttributeId = response.body.attributeId; | ||||
|         expect(createdAttributeId).toBeTruthy(); | ||||
| 
 | ||||
|         // Create a subnote.
 | ||||
|         response = await supertest(app) | ||||
|             .post("/etapi/create-note") | ||||
|             .auth("etapi", token, { "type": "basic"}) | ||||
|             .send({ | ||||
|                 "parentNoteId": parentNoteId, | ||||
|                 "title": "Hello", | ||||
|                 "type": "text", | ||||
|                 "content": "Hi there!" | ||||
|             }) | ||||
|             .expect(201); | ||||
|         const createdNoteId = response.body.note.noteId; | ||||
| 
 | ||||
|         // Check the attribute is inherited.
 | ||||
|         response = await supertest(app) | ||||
|             .get(`/etapi/notes/${createdNoteId}`) | ||||
|             .auth("etapi", token, { "type": "basic"}) | ||||
|             .expect(200); | ||||
|         expect(response.body.noteId).toStrictEqual(createdNoteId); | ||||
|         expect(response.body.attributes).toHaveLength(1); | ||||
|         expect(response.body.attributes[0].attributeId === createdAttributeId); | ||||
|     }); | ||||
| }); | ||||
| @ -21,6 +21,6 @@ describe("etapi/refresh-note-ordering/root", () => { | ||||
|         await supertest(app) | ||||
|             .post("/etapi/refresh-note-ordering/root") | ||||
|             .auth(USER, token, { "type": "basic"}) | ||||
|             .expect(200); | ||||
|             .expect(204); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
							
								
								
									
										77
									
								
								apps/server/spec/etapi/patch-attribute.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								apps/server/spec/etapi/patch-attribute.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| import { Application } from "express"; | ||||
| import { beforeAll, describe, expect, it } from "vitest"; | ||||
| import supertest from "supertest"; | ||||
| import { createNote, login } from "./utils.js"; | ||||
| import config from "../../src/services/config.js"; | ||||
| 
 | ||||
| let app: Application; | ||||
| let token: string; | ||||
| 
 | ||||
| const USER = "etapi"; | ||||
| let createdNoteId: string; | ||||
| let createdAttributeId: string; | ||||
| 
 | ||||
| describe("etapi/patch-attribute", () => { | ||||
|     beforeAll(async () => { | ||||
|         config.General.noAuthentication = false; | ||||
|         const buildApp = (await (import("../../src/app.js"))).default; | ||||
|         app = await buildApp(); | ||||
|         token = await login(app); | ||||
| 
 | ||||
|         createdNoteId = await createNote(app, token); | ||||
| 
 | ||||
|         // Create an attribute
 | ||||
|         const response = await supertest(app) | ||||
|             .post(`/etapi/attributes`) | ||||
|             .auth(USER, token, { "type": "basic"}) | ||||
|             .send({ | ||||
|                 "noteId": createdNoteId, | ||||
|                 "type": "label", | ||||
|                 "name": "mylabel", | ||||
|                 "value": "val", | ||||
|                 "isInheritable": true | ||||
|             }); | ||||
|         createdAttributeId = response.body.attributeId; | ||||
|         expect(createdAttributeId).toBeTruthy(); | ||||
|     }); | ||||
| 
 | ||||
|     it("changes name and value", async () => { | ||||
|         const state = { | ||||
|             value: "CHANGED" | ||||
|         }; | ||||
|         await supertest(app) | ||||
|             .patch(`/etapi/attributes/${createdAttributeId}`) | ||||
|             .auth(USER, token, { "type": "basic"}) | ||||
|             .send(state) | ||||
|             .expect(200); | ||||
| 
 | ||||
|         // Ensure it got changed.
 | ||||
|         const response = await supertest(app) | ||||
|             .get(`/etapi/attributes/${createdAttributeId}`) | ||||
|             .auth(USER, token, { "type": "basic"}); | ||||
|         expect(response.body).toMatchObject(state); | ||||
|     }); | ||||
| 
 | ||||
|     it("forbids setting disallowed property", async () => { | ||||
|         const response = await supertest(app) | ||||
|             .patch(`/etapi/attributes/${createdAttributeId}`) | ||||
|             .auth(USER, token, { "type": "basic"}) | ||||
|             .send({ | ||||
|                 noteId: "root" | ||||
|             }) | ||||
|             .expect(400); | ||||
|         expect(response.body.code).toStrictEqual("PROPERTY_NOT_ALLOWED"); | ||||
|     }); | ||||
| 
 | ||||
|     it("forbids setting wrong data type", async () => { | ||||
|         const response = await supertest(app) | ||||
|             .patch(`/etapi/attributes/${createdAttributeId}`) | ||||
|             .auth(USER, token, { "type": "basic"}) | ||||
|             .send({ | ||||
|                 value: null | ||||
|             }) | ||||
|             .expect(400); | ||||
|         expect(response.body.code).toStrictEqual("PROPERTY_VALIDATION_ERROR"); | ||||
|     }); | ||||
| 
 | ||||
| }); | ||||
| @ -34,7 +34,7 @@ describe("etapi/patch-note", () => { | ||||
|     }); | ||||
| 
 | ||||
|     it("obtains correct note information", async () => { | ||||
|         expectNoteToMatch({ | ||||
|         await expectNoteToMatch({ | ||||
|             title: "Hello", | ||||
|             type: "code", | ||||
|             mime: "application/json" | ||||
|  | ||||
| @ -13,6 +13,7 @@ | ||||
|     <link rel="shortcut icon" href="../favicon.ico"> | ||||
|     <% } %> | ||||
|     <script src="<%= appPath %>/share.js" type="module"></script> | ||||
|     <link href="<%= assetPath %>/src/share.css" rel="stylesheet"> | ||||
|     <% if (!note.isLabelTruthy("shareOmitDefaultCss")) { %> | ||||
|         <link href="<%= assetPath %>/stylesheets/share.css" rel="stylesheet"> | ||||
|     <% } %> | ||||
|  | ||||
| @ -4,10 +4,10 @@ import eu from "./etapi_utils.js"; | ||||
| import backupService from "../services/backup.js"; | ||||
| 
 | ||||
| function register(router: Router) { | ||||
|     eu.route(router, "put", "/etapi/backup/:backupName", async (req, res, next) => { | ||||
|         await backupService.backupNow(req.params.backupName); | ||||
| 
 | ||||
|         res.sendStatus(204); | ||||
|     eu.route(router, "put", "/etapi/backup/:backupName", (req, res, next) => { | ||||
|         backupService.backupNow(req.params.backupName) | ||||
|             .then(() => res.sendStatus(204)) | ||||
|             .catch(() => res.sendStatus(500)); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,7 @@ import etapiTokenService from "../services/etapi_tokens.js"; | ||||
| import config from "../services/config.js"; | ||||
| import type { NextFunction, Request, RequestHandler, Response, Router } from "express"; | ||||
| import type { ValidatorMap } from "./etapi-interface.js"; | ||||
| import type { ApiRequestHandler } from "../routes/route_api.js"; | ||||
| import type { ApiRequestHandler, SyncRouteRequestHandler } from "../routes/route_api.js"; | ||||
| const GENERIC_CODE = "GENERIC"; | ||||
| 
 | ||||
| type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head"; | ||||
| @ -73,11 +73,11 @@ function processRequest(req: Request, res: Response, routeHandler: ApiRequestHan | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function route(router: Router, method: HttpMethod, path: string, routeHandler: ApiRequestHandler) { | ||||
| function route(router: Router, method: HttpMethod, path: string, routeHandler: SyncRouteRequestHandler) { | ||||
|     router[method](path, checkEtapiAuth, (req: Request, res: Response, next: NextFunction) => processRequest(req, res, routeHandler, next, method, path)); | ||||
| } | ||||
| 
 | ||||
| function NOT_AUTHENTICATED_ROUTE(router: Router, method: HttpMethod, path: string, middleware: RequestHandler[], routeHandler: RequestHandler) { | ||||
| function NOT_AUTHENTICATED_ROUTE(router: Router, method: HttpMethod, path: string, middleware: RequestHandler[], routeHandler: SyncRouteRequestHandler) { | ||||
|     router[method](path, ...middleware, (req: Request, res: Response, next: NextFunction) => processRequest(req, res, routeHandler, next, method, path)); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -15,46 +15,46 @@ function isValidDate(date: string) { | ||||
| } | ||||
| 
 | ||||
| function register(router: Router) { | ||||
|     eu.route(router, "get", "/etapi/inbox/:date", async (req, res, next) => { | ||||
|     eu.route(router, "get", "/etapi/inbox/:date", (req, res, next) => { | ||||
|         const { date } = req.params; | ||||
| 
 | ||||
|         if (!isValidDate(date)) { | ||||
|             throw getDateInvalidError(date); | ||||
|         } | ||||
|         const note = await specialNotesService.getInboxNote(date); | ||||
|         const note = specialNotesService.getInboxNote(date); | ||||
|         res.json(mappers.mapNoteToPojo(note)); | ||||
|     }); | ||||
| 
 | ||||
|     eu.route(router, "get", "/etapi/calendar/days/:date", async (req, res, next) => { | ||||
|     eu.route(router, "get", "/etapi/calendar/days/:date", (req, res, next) => { | ||||
|         const { date } = req.params; | ||||
| 
 | ||||
|         if (!isValidDate(date)) { | ||||
|             throw getDateInvalidError(date); | ||||
|         } | ||||
| 
 | ||||
|         const note = await dateNotesService.getDayNote(date); | ||||
|         const note = dateNotesService.getDayNote(date); | ||||
|         res.json(mappers.mapNoteToPojo(note)); | ||||
|     }); | ||||
| 
 | ||||
|     eu.route(router, "get", "/etapi/calendar/week-first-day/:date", async (req, res, next) => { | ||||
|     eu.route(router, "get", "/etapi/calendar/week-first-day/:date", (req, res, next) => { | ||||
|         const { date } = req.params; | ||||
| 
 | ||||
|         if (!isValidDate(date)) { | ||||
|             throw getDateInvalidError(date); | ||||
|         } | ||||
| 
 | ||||
|         const note = await dateNotesService.getWeekFirstDayNote(date); | ||||
|         const note = dateNotesService.getWeekFirstDayNote(date); | ||||
|         res.json(mappers.mapNoteToPojo(note)); | ||||
|     }); | ||||
| 
 | ||||
|     eu.route(router, "get", "/etapi/calendar/weeks/:week", async (req, res, next) => { | ||||
|     eu.route(router, "get", "/etapi/calendar/weeks/:week", (req, res, next) => { | ||||
|         const { week } = req.params; | ||||
| 
 | ||||
|         if (!/[0-9]{4}-W[0-9]{2}/.test(week)) { | ||||
|             throw getWeekInvalidError(week); | ||||
|         } | ||||
| 
 | ||||
|         const note = await dateNotesService.getWeekNote(week); | ||||
|         const note = dateNotesService.getWeekNote(week); | ||||
| 
 | ||||
|         if (!note) { | ||||
|             throw getWeekNotFoundError(week); | ||||
| @ -63,14 +63,14 @@ function register(router: Router) { | ||||
|         res.json(mappers.mapNoteToPojo(note)); | ||||
|     }); | ||||
| 
 | ||||
|     eu.route(router, "get", "/etapi/calendar/months/:month", async (req, res, next) => { | ||||
|     eu.route(router, "get", "/etapi/calendar/months/:month", (req, res, next) => { | ||||
|         const { month } = req.params; | ||||
| 
 | ||||
|         if (!/[0-9]{4}-[0-9]{2}/.test(month)) { | ||||
|             throw getMonthInvalidError(month); | ||||
|         } | ||||
| 
 | ||||
|         const note = await dateNotesService.getMonthNote(month); | ||||
|         const note = dateNotesService.getMonthNote(month); | ||||
|         res.json(mappers.mapNoteToPojo(note)); | ||||
|     }); | ||||
| 
 | ||||
|  | ||||
| @ -9,6 +9,7 @@ import searchService from "./search/services/search.js"; | ||||
| import SearchContext from "./search/search_context.js"; | ||||
| import hiddenSubtree from "./hidden_subtree.js"; | ||||
| import { t } from "i18next"; | ||||
| import { BNote } from "./backend_script_entrypoint.js"; | ||||
| const { LBTPL_NOTE_LAUNCHER, LBTPL_CUSTOM_WIDGET, LBTPL_SPACER, LBTPL_SCRIPT } = hiddenSubtree; | ||||
| 
 | ||||
| function getInboxNote(date: string) { | ||||
| @ -17,7 +18,7 @@ function getInboxNote(date: string) { | ||||
|         throw new Error("Unable to find workspace note"); | ||||
|     } | ||||
| 
 | ||||
|     let inbox; | ||||
|     let inbox: BNote; | ||||
| 
 | ||||
|     if (!workspaceNote.isRoot()) { | ||||
|         inbox = workspaceNote.searchNoteInSubtree("#workspaceInbox"); | ||||
|  | ||||
| @ -11,7 +11,6 @@ export default defineConfig(() => ({ | ||||
|     setupFiles: ["./spec/setup.ts"], | ||||
|     environment: "node", | ||||
|     include: ['{src,spec}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], | ||||
|     reporters: ['default'], | ||||
|     coverage: { | ||||
|       reportsDirectory: './test-output/vitest/coverage', | ||||
|       provider: 'v8' as const, | ||||
|  | ||||
							
								
								
									
										57
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										57
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							| @ -18,6 +18,24 @@ | ||||
|         "type": "github" | ||||
|       } | ||||
|     }, | ||||
|     "flake-utils_2": { | ||||
|       "inputs": { | ||||
|         "systems": "systems_2" | ||||
|       }, | ||||
|       "locked": { | ||||
|         "lastModified": 1701680307, | ||||
|         "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", | ||||
|         "owner": "numtide", | ||||
|         "repo": "flake-utils", | ||||
|         "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
|         "owner": "numtide", | ||||
|         "repo": "flake-utils", | ||||
|         "type": "github" | ||||
|       } | ||||
|     }, | ||||
|     "nixpkgs": { | ||||
|       "locked": { | ||||
|         "lastModified": 1748437600, | ||||
| @ -34,10 +52,32 @@ | ||||
|         "type": "github" | ||||
|       } | ||||
|     }, | ||||
|     "pnpm2nix": { | ||||
|       "inputs": { | ||||
|         "flake-utils": "flake-utils_2", | ||||
|         "nixpkgs": [ | ||||
|           "nixpkgs" | ||||
|         ] | ||||
|       }, | ||||
|       "locked": { | ||||
|         "lastModified": 1748901165, | ||||
|         "narHash": "sha256-SctrxW5rVrROBLfh8p4kXfbF7NbJQDkse/Penu4PlEs=", | ||||
|         "owner": "FliegendeWurst", | ||||
|         "repo": "pnpm2nix-nzbr", | ||||
|         "rev": "cda68d63418896a58542f3310c1c757ae92b1f22", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
|         "owner": "FliegendeWurst", | ||||
|         "repo": "pnpm2nix-nzbr", | ||||
|         "type": "github" | ||||
|       } | ||||
|     }, | ||||
|     "root": { | ||||
|       "inputs": { | ||||
|         "flake-utils": "flake-utils", | ||||
|         "nixpkgs": "nixpkgs" | ||||
|         "nixpkgs": "nixpkgs", | ||||
|         "pnpm2nix": "pnpm2nix" | ||||
|       } | ||||
|     }, | ||||
|     "systems": { | ||||
| @ -54,6 +94,21 @@ | ||||
|         "repo": "default", | ||||
|         "type": "github" | ||||
|       } | ||||
|     }, | ||||
|     "systems_2": { | ||||
|       "locked": { | ||||
|         "lastModified": 1681028828, | ||||
|         "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", | ||||
|         "owner": "nix-systems", | ||||
|         "repo": "default", | ||||
|         "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
|         "owner": "nix-systems", | ||||
|         "repo": "default", | ||||
|         "type": "github" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "root": "root", | ||||
|  | ||||
							
								
								
									
										229
									
								
								flake.nix
									
									
									
									
									
								
							
							
						
						
									
										229
									
								
								flake.nix
									
									
									
									
									
								
							| @ -4,6 +4,10 @@ | ||||
|   inputs = { | ||||
|     nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; | ||||
|     flake-utils.url = "github:numtide/flake-utils"; | ||||
|     pnpm2nix = { | ||||
|       url = "github:FliegendeWurst/pnpm2nix-nzbr"; | ||||
|       inputs.nixpkgs.follows = "nixpkgs"; | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
|   outputs = | ||||
| @ -11,69 +15,171 @@ | ||||
|       self, | ||||
|       nixpkgs, | ||||
|       flake-utils, | ||||
|       pnpm2nix, | ||||
|     }: | ||||
|     flake-utils.lib.eachDefaultSystem ( | ||||
|       system: | ||||
|       let | ||||
|         packageJSON = builtins.fromJSON (builtins.readFile ./package.json); | ||||
|         pkgs = import nixpkgs { inherit system; }; | ||||
|         electron = pkgs.electron_35; | ||||
|         nodejs = pkgs.nodejs_22; | ||||
|         pnpm = pkgs.pnpm_10; | ||||
|         inherit (pkgs) | ||||
|           copyDesktopItems | ||||
|           darwin | ||||
|           lib | ||||
|           makeBinaryWrapper | ||||
|           makeDesktopItem | ||||
|           nodejs | ||||
|           pnpm | ||||
|           moreutils | ||||
|           removeReferencesTo | ||||
|           stdenv | ||||
|           wrapGAppsHook3 | ||||
|           xcodebuild | ||||
|           darwin | ||||
|           ; | ||||
|         desktop = stdenv.mkDerivation (finalAttrs: { | ||||
|           pname = "triliumnext-desktop"; | ||||
|           version = packageJSON.version; | ||||
|           src = lib.cleanSource ./.; | ||||
| 
 | ||||
|           nativeBuildInputs = | ||||
|             [ | ||||
|               pnpm.configHook | ||||
|               nodejs | ||||
|               nodejs.python | ||||
|               copyDesktopItems | ||||
|               makeBinaryWrapper | ||||
|               wrapGAppsHook3 | ||||
|             ] | ||||
|             ++ lib.optionals stdenv.hostPlatform.isDarwin [ | ||||
|               xcodebuild | ||||
|               darwin.cctools | ||||
|         fullCleanSourceFilter = | ||||
|           name: type: | ||||
|           (lib.cleanSourceFilter name type) | ||||
|           || ( | ||||
|             let | ||||
|               baseName = baseNameOf (toString name); | ||||
|             in | ||||
|             # No need to copy the flake. | ||||
|             # Don't copy local development instance of NX cache. | ||||
|             baseName == "flake.nix" || baseName == "flake.lock" || baseName == ".nx" | ||||
|           ); | ||||
|         fullCleanSource = | ||||
|           src: | ||||
|           lib.cleanSourceWith { | ||||
|             filter = fullCleanSourceFilter; | ||||
|             src = src; | ||||
|           }; | ||||
|         packageJson = builtins.fromJSON (builtins.readFile ./package.json); | ||||
| 
 | ||||
|         makeApp = | ||||
|           { | ||||
|             app, | ||||
|             buildTask, | ||||
|             mainProgram, | ||||
|             installCommands, | ||||
|             preBuildCommands ? "", | ||||
|           }: | ||||
|           pnpm2nix.packages.${system}.mkPnpmPackage rec { | ||||
|             pname = "triliumnext-${app}"; | ||||
|             version = packageJson.version + (lib.optionalString (self ? shortRev) "-${self.shortRev}"); | ||||
| 
 | ||||
|             src = fullCleanSource ./.; | ||||
|             packageJSON = ./package.json; | ||||
|             pnpmLockYaml = ./pnpm-lock.yaml; | ||||
| 
 | ||||
|             workspace = fullCleanSource ./.; | ||||
|             pnpmWorkspaceYaml = ./pnpm-workspace.yaml; | ||||
| 
 | ||||
|             inherit nodejs pnpm; | ||||
| 
 | ||||
|             extraNodeModuleSources = [ | ||||
|               rec { | ||||
|                 name = "patches"; | ||||
|                 value = ./patches; | ||||
|               } | ||||
|             ]; | ||||
| 
 | ||||
|           dontWrapGApps = true; | ||||
|             # remove pnpm version override | ||||
|             preConfigure = '' | ||||
|               cat package.json | grep -v 'packageManager' | sponge package.json | ||||
|             ''; | ||||
| 
 | ||||
|           preBuild = lib.optionalString stdenv.hostPlatform.isLinux '' | ||||
|             patchelf --set-interpreter $(cat $NIX_CC/nix-support/dynamic-linker) \ | ||||
|               node_modules/.pnpm/sass-embedded-linux-x64@*/node_modules/sass-embedded-linux-x64/dart-sass/src/dart | ||||
|           ''; | ||||
|             postConfigure = | ||||
|               '' | ||||
|                 chmod +x node_modules/.pnpm/electron@*/node_modules/electron/install.js | ||||
|                 patchShebangs --build node_modules | ||||
|               '' | ||||
|               + lib.optionalString stdenv.hostPlatform.isLinux '' | ||||
|                 patchelf --set-interpreter $(cat $NIX_CC/nix-support/dynamic-linker) \ | ||||
|                   node_modules/.pnpm/sass-embedded-linux-x64@*/node_modules/sass-embedded-linux-x64/dart-sass/src/dart | ||||
|               ''; | ||||
| 
 | ||||
|           buildPhase = '' | ||||
|             runHook preBuild | ||||
|             extraNativeBuildInputs = | ||||
|               [ | ||||
|                 makeBinaryWrapper | ||||
|                 moreutils # sponge | ||||
|                 nodejs.python | ||||
|                 removeReferencesTo | ||||
|               ] | ||||
|               ++ lib.optionals (app == "desktop") [ | ||||
|                 copyDesktopItems | ||||
|                 wrapGAppsHook3 | ||||
|               ] | ||||
|               ++ lib.optionals stdenv.hostPlatform.isDarwin [ | ||||
|                 xcodebuild | ||||
|                 darwin.cctools | ||||
|               ]; | ||||
|             dontWrapGApps = true; | ||||
| 
 | ||||
|             # Disable NX interaction | ||||
|             export NX_TUI=false | ||||
|             export NX_DAEMON=false | ||||
|             env.ELECTRON_SKIP_BINARY_DOWNLOAD = "1"; | ||||
| 
 | ||||
|             pnpm nx run desktop:build --outputStyle stream --verbose | ||||
|             preBuild = '' | ||||
|               ${preBuildCommands} | ||||
|             ''; | ||||
| 
 | ||||
|             # Rebuild dependencies | ||||
|             export npm_config_nodedir=${electron.headers} | ||||
|             pnpm nx run desktop:rebuild-deps --outputStyle stream --verbose | ||||
|             scriptFull = "pnpm nx ${buildTask} --outputStyle stream --verbose"; | ||||
| 
 | ||||
|             runHook postBuild | ||||
|           ''; | ||||
|             installPhase = '' | ||||
|               runHook preInstall | ||||
| 
 | ||||
|           installPhase = '' | ||||
|             runHook preInstall | ||||
|               ${installCommands} | ||||
| 
 | ||||
|               runHook postInstall | ||||
|             ''; | ||||
| 
 | ||||
|             components = [ | ||||
|               "packages/ckeditor5" | ||||
|               "packages/ckeditor5-admonition" | ||||
|               "packages/ckeditor5-footnotes" | ||||
|               "packages/ckeditor5-keyboard-marker" | ||||
|               "packages/ckeditor5-math" | ||||
|               "packages/ckeditor5-mermaid" | ||||
|               "packages/codemirror" | ||||
|               "packages/commons" | ||||
|               "packages/express-partial-content" | ||||
|               "packages/highlightjs" | ||||
|               "packages/turndown-plugin-gfm" | ||||
| 
 | ||||
|               "apps/client" | ||||
|               "apps/db-compare" | ||||
|               "apps/desktop" | ||||
|               "apps/dump-db" | ||||
|               "apps/edit-docs" | ||||
|               "apps/server" | ||||
|               "apps/server-e2e" | ||||
|             ]; | ||||
| 
 | ||||
|             desktopItems = lib.optionals (app == "desktop") [ | ||||
|               (makeDesktopItem { | ||||
|                 name = "TriliumNext Notes"; | ||||
|                 exec = meta.mainProgram; | ||||
|                 icon = "trilium"; | ||||
|                 comment = meta.description; | ||||
|                 desktopName = "TriliumNext Notes"; | ||||
|                 categories = [ "Office" ]; | ||||
|                 startupWMClass = "Trilium Notes Next"; | ||||
|               }) | ||||
|             ]; | ||||
| 
 | ||||
|             meta = { | ||||
|               description = "TriliumNext: ${app}"; | ||||
|               inherit mainProgram; | ||||
|             }; | ||||
|           }; | ||||
| 
 | ||||
|         desktop = makeApp { | ||||
|           app = "desktop"; | ||||
|           preBuildCommands = "export npm_config_nodedir=${electron.headers}"; | ||||
|           buildTask = "run desktop:rebuild-deps"; | ||||
|           mainProgram = "trilium"; | ||||
|           installCommands = '' | ||||
|             remove-references-to -t ${electron.headers} apps/desktop/dist/node_modules/better-sqlite3/build/config.gypi | ||||
|             remove-references-to -t ${nodejs.python} apps/desktop/dist/node_modules/better-sqlite3/build/config.gypi | ||||
| 
 | ||||
|             mkdir -p $out/{bin,share/icons/hicolor/512x512/apps,opt/trilium} | ||||
|             cp --archive apps/desktop/dist/* $out/opt/trilium | ||||
| @ -82,34 +188,37 @@ | ||||
|               "''${gappsWrapperArgs[@]}" \ | ||||
|               --set-default ELECTRON_IS_DEV 0 \ | ||||
|               --add-flags $out/opt/trilium/main.cjs | ||||
| 
 | ||||
|             runHook postInstall | ||||
|           ''; | ||||
|         }; | ||||
| 
 | ||||
|           desktopItems = [ | ||||
|             (makeDesktopItem { | ||||
|               name = "TriliumNext Notes"; | ||||
|               exec = finalAttrs.meta.mainProgram; | ||||
|               icon = "trilium"; | ||||
|               comment = finalAttrs.meta.description; | ||||
|               desktopName = "TriliumNext Notes"; | ||||
|               categories = [ "Office" ]; | ||||
|               startupWMClass = "Trilium Notes Next"; | ||||
|             }) | ||||
|           ]; | ||||
|         server = makeApp { | ||||
|           app = "server"; | ||||
|           preBuildCommands = "pushd apps/server; pnpm rebuild; popd"; | ||||
|           buildTask = "--project=server build"; | ||||
|           mainProgram = "trilium-server"; | ||||
|           installCommands = '' | ||||
|             remove-references-to -t ${nodejs.python} apps/server/dist/node_modules/better-sqlite3/build/config.gypi | ||||
|             remove-references-to -t ${pnpm} apps/server/dist/node_modules/better-sqlite3/build/config.gypi | ||||
| 
 | ||||
|           pnpmDeps = pnpm.fetchDeps { | ||||
|             inherit (finalAttrs) pname version src; | ||||
|             hash = "sha256-xC0u1h92wtthylOAw+IF9mpFi0c4xajJhUcA9pqzcAw="; | ||||
|           }; | ||||
|             pushd apps/server/dist | ||||
|             rm -rf node_modules/better-sqlite3/build/Release/obj \ | ||||
|                    node_modules/better-sqlite3/build/Release/obj.target \ | ||||
|                    node_modules/better-sqlite3/build/Release/sqlite3.a \ | ||||
|                    node_modules/better-sqlite3/build/{Makefile,better_sqlite3.target.mk,test_extension.target.mk,binding.Makefile} \ | ||||
|                    node_modules/better-sqlite3/deps/sqlite3 | ||||
|             popd | ||||
| 
 | ||||
|           meta = { | ||||
|             description = "Free and open-source, cross-platform hierarchical note taking application with focus on building large personal knowledge bases"; | ||||
|             mainProgram = "trilium"; | ||||
|           }; | ||||
|         }); | ||||
|             mkdir -p $out/{bin,opt/trilium-server} | ||||
|             cp --archive apps/server/dist/* $out/opt/trilium-server | ||||
|             makeWrapper ${lib.getExe nodejs} $out/bin/trilium-server \ | ||||
|               --add-flags $out/opt/trilium-server/main.cjs | ||||
|           ''; | ||||
|         }; | ||||
|       in | ||||
|       { | ||||
|         packages.desktop = desktop; | ||||
|         packages.server = server; | ||||
| 
 | ||||
|         packages.default = desktop; | ||||
|       } | ||||
|     ); | ||||
|  | ||||
							
								
								
									
										262
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										262
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -65,7 +65,7 @@ importers: | ||||
|         version: 21.1.2(@babel/traverse@7.27.0)(@playwright/test@1.52.0)(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17))(@zkochan/js-yaml@0.0.7)(eslint@9.28.0(jiti@2.4.2))(nx@21.1.2(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17)))(typescript@5.8.3) | ||||
|       '@nx/vite': | ||||
|         specifier: 21.1.2 | ||||
|         version: 21.1.2(@babel/traverse@7.27.0)(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17))(nx@21.1.2(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17)))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.0) | ||||
|         version: 21.1.2(@babel/traverse@7.27.0)(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17))(nx@21.1.2(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17)))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.1) | ||||
|       '@nx/web': | ||||
|         specifier: 21.1.2 | ||||
|         version: 21.1.2(@babel/traverse@7.27.0)(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17))(nx@21.1.2(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17))) | ||||
| @ -83,10 +83,10 @@ importers: | ||||
|         version: 22.15.29 | ||||
|       '@vitest/coverage-v8': | ||||
|         specifier: ^3.0.5 | ||||
|         version: 3.2.0(@vitest/browser@3.2.0)(vitest@3.2.0) | ||||
|         version: 3.2.1(vitest@3.2.1) | ||||
|       '@vitest/ui': | ||||
|         specifier: ^3.0.0 | ||||
|         version: 3.2.0(vitest@3.2.0) | ||||
|         version: 3.2.1(vitest@3.2.1) | ||||
|       chalk: | ||||
|         specifier: 5.4.1 | ||||
|         version: 5.4.1 | ||||
| @ -146,7 +146,7 @@ importers: | ||||
|         version: 4.5.4(@types/node@22.15.29)(rollup@4.40.0)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0)) | ||||
|       vitest: | ||||
|         specifier: ^3.0.0 | ||||
|         version: 3.2.0(@types/debug@4.1.12)(@types/node@22.15.29)(@vitest/browser@3.2.0)(@vitest/ui@3.2.0)(happy-dom@17.6.1)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
|         version: 3.2.1(@types/debug@4.1.12)(@types/node@22.15.29)(@vitest/ui@3.2.1)(happy-dom@17.6.1)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
| 
 | ||||
|   apps/client: | ||||
|     dependencies: | ||||
| @ -699,8 +699,8 @@ importers: | ||||
|         specifier: 3.0.1 | ||||
|         version: 3.0.1 | ||||
|       multer: | ||||
|         specifier: 2.0.0 | ||||
|         version: 2.0.0 | ||||
|         specifier: 2.0.1 | ||||
|         version: 2.0.1 | ||||
|       normalize-strings: | ||||
|         specifier: 1.1.1 | ||||
|         version: 1.1.1 | ||||
| @ -708,8 +708,8 @@ importers: | ||||
|         specifier: 0.5.16 | ||||
|         version: 0.5.16 | ||||
|       openai: | ||||
|         specifier: 5.0.2 | ||||
|         version: 5.0.2(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.4) | ||||
|         specifier: 5.1.0 | ||||
|         version: 5.1.0(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.4) | ||||
|       rand-token: | ||||
|         specifier: 1.0.1 | ||||
|         version: 1.0.1 | ||||
| @ -1426,8 +1426,8 @@ packages: | ||||
|     engines: {node: '>=6.0.0'} | ||||
|     hasBin: true | ||||
| 
 | ||||
|   '@babel/parser@7.27.4': | ||||
|     resolution: {integrity: sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==} | ||||
|   '@babel/parser@7.27.5': | ||||
|     resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} | ||||
|     engines: {node: '>=6.0.0'} | ||||
|     hasBin: true | ||||
| 
 | ||||
| @ -4864,11 +4864,11 @@ packages: | ||||
|     peerDependencies: | ||||
|       vitest: 3.2.0 | ||||
| 
 | ||||
|   '@vitest/coverage-v8@3.2.0': | ||||
|     resolution: {integrity: sha512-HjgvaokAiHxRMI5ioXl4WmgAi4zQtKtnltOOlmpzUqApdcTTZrZJAastbbRGydtiqwtYLFaIb6Jpo3PzowZ0cg==} | ||||
|   '@vitest/coverage-v8@3.2.1': | ||||
|     resolution: {integrity: sha512-6dy0uF/0BE3jpUW9bFzg0V2S4F7XVaZHL/7qma1XANvHPQGoJuc3wtx911zSoAgUnpfvcLVK1vancNJ95d+uxQ==} | ||||
|     peerDependencies: | ||||
|       '@vitest/browser': 3.2.0 | ||||
|       vitest: 3.2.0 | ||||
|       '@vitest/browser': 3.2.1 | ||||
|       vitest: 3.2.1 | ||||
|     peerDependenciesMeta: | ||||
|       '@vitest/browser': | ||||
|         optional: true | ||||
| @ -4876,6 +4876,9 @@ packages: | ||||
|   '@vitest/expect@3.2.0': | ||||
|     resolution: {integrity: sha512-0v4YVbhDKX3SKoy0PHWXpKhj44w+3zZkIoVES9Ex2pq+u6+Bijijbi2ua5kE+h3qT6LBWFTNZSCOEU37H8Y5sA==} | ||||
| 
 | ||||
|   '@vitest/expect@3.2.1': | ||||
|     resolution: {integrity: sha512-FqS/BnDOzV6+IpxrTg5GQRyLOCtcJqkwMwcS8qGCI2IyRVDwPAtutztaf1CjtPHlZlWtl1yUPCd7HM0cNiDOYw==} | ||||
| 
 | ||||
|   '@vitest/mocker@3.2.0': | ||||
|     resolution: {integrity: sha512-HFcW0lAMx3eN9vQqis63H0Pscv0QcVMo1Kv8BNysZbxcmHu3ZUYv59DS6BGYiGQ8F5lUkmsfMMlPm4DJFJdf/A==} | ||||
|     peerDependencies: | ||||
| @ -4887,26 +4890,57 @@ packages: | ||||
|       vite: | ||||
|         optional: true | ||||
| 
 | ||||
|   '@vitest/mocker@3.2.1': | ||||
|     resolution: {integrity: sha512-OXxMJnx1lkB+Vl65Re5BrsZEHc90s5NMjD23ZQ9NlU7f7nZiETGoX4NeKZSmsKjseuMq2uOYXdLOeoM0pJU+qw==} | ||||
|     peerDependencies: | ||||
|       msw: ^2.4.9 | ||||
|       vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 | ||||
|     peerDependenciesMeta: | ||||
|       msw: | ||||
|         optional: true | ||||
|       vite: | ||||
|         optional: true | ||||
| 
 | ||||
|   '@vitest/pretty-format@3.2.0': | ||||
|     resolution: {integrity: sha512-gUUhaUmPBHFkrqnOokmfMGRBMHhgpICud9nrz/xpNV3/4OXCn35oG+Pl8rYYsKaTNd/FAIrqRHnwpDpmYxCYZw==} | ||||
| 
 | ||||
|   '@vitest/pretty-format@3.2.1': | ||||
|     resolution: {integrity: sha512-xBh1X2GPlOGBupp6E1RcUQWIxw0w/hRLd3XyBS6H+dMdKTAqHDNsIR2AnJwPA3yYe9DFy3VUKTe3VRTrAiQ01g==} | ||||
| 
 | ||||
|   '@vitest/runner@3.2.0': | ||||
|     resolution: {integrity: sha512-bXdmnHxuB7fXJdh+8vvnlwi/m1zvu+I06i1dICVcDQFhyV4iKw2RExC/acavtDn93m/dRuawUObKsrNE1gJacA==} | ||||
| 
 | ||||
|   '@vitest/runner@3.2.1': | ||||
|     resolution: {integrity: sha512-kygXhNTu/wkMYbwYpS3z/9tBe0O8qpdBuC3dD/AW9sWa0LE/DAZEjnHtWA9sIad7lpD4nFW1yQ+zN7mEKNH3yA==} | ||||
| 
 | ||||
|   '@vitest/snapshot@3.2.0': | ||||
|     resolution: {integrity: sha512-z7P/EneBRMe7hdvWhcHoXjhA6at0Q4ipcoZo6SqgxLyQQ8KSMMCmvw1cSt7FHib3ozt0wnRHc37ivuUMbxzG/A==} | ||||
| 
 | ||||
|   '@vitest/snapshot@3.2.1': | ||||
|     resolution: {integrity: sha512-5xko/ZpW2Yc65NVK9Gpfg2y4BFvcF+At7yRT5AHUpTg9JvZ4xZoyuRY4ASlmNcBZjMslV08VRLDrBOmUe2YX3g==} | ||||
| 
 | ||||
|   '@vitest/spy@3.2.0': | ||||
|     resolution: {integrity: sha512-s3+TkCNUIEOX99S0JwNDfsHRaZDDZZR/n8F0mop0PmsEbQGKZikCGpTGZ6JRiHuONKew3Fb5//EPwCP+pUX9cw==} | ||||
| 
 | ||||
|   '@vitest/spy@3.2.1': | ||||
|     resolution: {integrity: sha512-Nbfib34Z2rfcJGSetMxjDCznn4pCYPZOtQYox2kzebIJcgH75yheIKd5QYSFmR8DIZf2M8fwOm66qSDIfRFFfQ==} | ||||
| 
 | ||||
|   '@vitest/ui@3.2.0': | ||||
|     resolution: {integrity: sha512-cYFZZSl1usgzsHoGF66GHfYXlEwc06ggapS1TaSLMKCzhTPWBPI9b/t1RvKIsLSjdKUakpSPf33jQMvRjMvvlQ==} | ||||
|     peerDependencies: | ||||
|       vitest: 3.2.0 | ||||
| 
 | ||||
|   '@vitest/ui@3.2.1': | ||||
|     resolution: {integrity: sha512-xT93aOcPn2wn8vvw4T6rZAK9WjGEHdYrEjN3OJ1zcDpl2UInxvcD9fYI10nmPAERNEK6jUVcSCIPAIfNuaRX6Q==} | ||||
|     peerDependencies: | ||||
|       vitest: 3.2.1 | ||||
| 
 | ||||
|   '@vitest/utils@3.2.0': | ||||
|     resolution: {integrity: sha512-gXXOe7Fj6toCsZKVQouTRLJftJwmvbhH5lKOBR6rlP950zUq9AitTUjnFoXS/CqjBC2aoejAztLPzzuva++XBw==} | ||||
| 
 | ||||
|   '@vitest/utils@3.2.1': | ||||
|     resolution: {integrity: sha512-KkHlGhePEKZSub5ViknBcN5KEF+u7dSUr9NW8QsVICusUojrgrOnnY3DEWWO877ax2Pyopuk2qHmt+gkNKnBVw==} | ||||
| 
 | ||||
|   '@volar/language-core@2.4.13': | ||||
|     resolution: {integrity: sha512-MnQJ7eKchJx5Oz+YdbqyFUk8BN6jasdJv31n/7r6/WwlOOv7qzvot6B66887l2ST3bUW4Mewml54euzpJWA6bg==} | ||||
| 
 | ||||
| @ -5951,6 +5985,10 @@ packages: | ||||
|     resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} | ||||
|     engines: {'0': node >= 0.8} | ||||
| 
 | ||||
|   concat-stream@2.0.0: | ||||
|     resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} | ||||
|     engines: {'0': node >= 6.0} | ||||
| 
 | ||||
|   confbox@0.1.8: | ||||
|     resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} | ||||
| 
 | ||||
| @ -9262,8 +9300,8 @@ packages: | ||||
|   muggle-string@0.4.1: | ||||
|     resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} | ||||
| 
 | ||||
|   multer@2.0.0: | ||||
|     resolution: {integrity: sha512-bS8rPZurbAuHGAnApbM9d4h1wSoYqrOqkE+6a64KLMK9yWU7gJXBDDVklKQ3TPi9DRb85cRs6yXaC0+cjxRtRg==} | ||||
|   multer@2.0.1: | ||||
|     resolution: {integrity: sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==} | ||||
|     engines: {node: '>= 10.16.0'} | ||||
| 
 | ||||
|   multicast-dns@7.2.5: | ||||
| @ -9544,8 +9582,8 @@ packages: | ||||
|     resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} | ||||
|     engines: {node: '>=12'} | ||||
| 
 | ||||
|   openai@5.0.2: | ||||
|     resolution: {integrity: sha512-NN7LAAImgBmd4RIe6WyRpLmwCbn+HQ1iaXeIG7K9DM3Auy/G2waKFhrDfRgaEeY0UUPnm6nohaCsqcS+zO8+2g==} | ||||
|   openai@5.1.0: | ||||
|     resolution: {integrity: sha512-YQBgPJykHrDOlngB/8QpOsFNg36yofBatpeDWg1zejl9R59/ELuN7AMPSU95ZIdChbKc/o5vg1UnBJ1OEB0IJA==} | ||||
|     hasBin: true | ||||
|     peerDependencies: | ||||
|       ws: ^8.18.0 | ||||
| @ -12386,6 +12424,11 @@ packages: | ||||
|     engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} | ||||
|     hasBin: true | ||||
| 
 | ||||
|   vite-node@3.2.1: | ||||
|     resolution: {integrity: sha512-V4EyKQPxquurNJPtQJRZo8hKOoKNBRIhxcDbQFPFig0JdoWcUhwRgK8yoCXXrfYVPKS6XwirGHPszLnR8FbjCA==} | ||||
|     engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} | ||||
|     hasBin: true | ||||
| 
 | ||||
|   vite-plugin-dts@4.5.4: | ||||
|     resolution: {integrity: sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==} | ||||
|     peerDependencies: | ||||
| @ -12475,6 +12518,34 @@ packages: | ||||
|       jsdom: | ||||
|         optional: true | ||||
| 
 | ||||
|   vitest@3.2.1: | ||||
|     resolution: {integrity: sha512-VZ40MBnlE1/V5uTgdqY3DmjUgZtIzsYq758JGlyQrv5syIsaYcabkfPkEuWML49Ph0D/SoqpVFd0dyVTr551oA==} | ||||
|     engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} | ||||
|     hasBin: true | ||||
|     peerDependencies: | ||||
|       '@edge-runtime/vm': '*' | ||||
|       '@types/debug': ^4.1.12 | ||||
|       '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 | ||||
|       '@vitest/browser': 3.2.1 | ||||
|       '@vitest/ui': 3.2.1 | ||||
|       happy-dom: '*' | ||||
|       jsdom: '*' | ||||
|     peerDependenciesMeta: | ||||
|       '@edge-runtime/vm': | ||||
|         optional: true | ||||
|       '@types/debug': | ||||
|         optional: true | ||||
|       '@types/node': | ||||
|         optional: true | ||||
|       '@vitest/browser': | ||||
|         optional: true | ||||
|       '@vitest/ui': | ||||
|         optional: true | ||||
|       happy-dom: | ||||
|         optional: true | ||||
|       jsdom: | ||||
|         optional: true | ||||
| 
 | ||||
|   vscode-jsonrpc@8.2.0: | ||||
|     resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} | ||||
|     engines: {node: '>=14.0.0'} | ||||
| @ -13128,7 +13199,7 @@ snapshots: | ||||
|     dependencies: | ||||
|       '@babel/types': 7.27.1 | ||||
| 
 | ||||
|   '@babel/parser@7.27.4': | ||||
|   '@babel/parser@7.27.5': | ||||
|     dependencies: | ||||
|       '@babel/types': 7.27.3 | ||||
| 
 | ||||
| @ -16377,7 +16448,7 @@ snapshots: | ||||
|       - typescript | ||||
|       - verdaccio | ||||
| 
 | ||||
|   '@nx/vite@21.1.2(@babel/traverse@7.27.0)(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17))(nx@21.1.2(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17)))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.0)': | ||||
|   '@nx/vite@21.1.2(@babel/traverse@7.27.0)(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17))(nx@21.1.2(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17)))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.1)': | ||||
|     dependencies: | ||||
|       '@nx/devkit': 21.1.2(nx@21.1.2(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17))) | ||||
|       '@nx/js': 21.1.2(@babel/traverse@7.27.0)(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17))(nx@21.1.2(@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.29(@swc/helpers@0.5.17))) | ||||
| @ -16389,7 +16460,7 @@ snapshots: | ||||
|       semver: 7.7.2 | ||||
|       tsconfig-paths: 4.2.0 | ||||
|       vite: 6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
|       vitest: 3.2.0(@types/debug@4.1.12)(@types/node@22.15.29)(@vitest/browser@3.2.0)(@vitest/ui@3.2.0)(happy-dom@17.6.1)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
|       vitest: 3.2.1(@types/debug@4.1.12)(@types/node@22.15.29)(@vitest/ui@3.2.1)(happy-dom@17.6.1)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
|     transitivePeerDependencies: | ||||
|       - '@babel/traverse' | ||||
|       - '@swc-node/register' | ||||
| @ -17241,7 +17312,7 @@ snapshots: | ||||
| 
 | ||||
|   '@types/babel__core@7.20.5': | ||||
|     dependencies: | ||||
|       '@babel/parser': 7.27.4 | ||||
|       '@babel/parser': 7.27.5 | ||||
|       '@babel/types': 7.27.3 | ||||
|       '@types/babel__generator': 7.27.0 | ||||
|       '@types/babel__template': 7.4.4 | ||||
| @ -17253,7 +17324,7 @@ snapshots: | ||||
| 
 | ||||
|   '@types/babel__template@7.4.4': | ||||
|     dependencies: | ||||
|       '@babel/parser': 7.27.4 | ||||
|       '@babel/parser': 7.27.5 | ||||
|       '@babel/types': 7.27.3 | ||||
| 
 | ||||
|   '@types/babel__traverse@7.20.7': | ||||
| @ -17921,7 +17992,7 @@ snapshots: | ||||
|     transitivePeerDependencies: | ||||
|       - supports-color | ||||
| 
 | ||||
|   '@vitest/coverage-v8@3.2.0(@vitest/browser@3.2.0)(vitest@3.2.0)': | ||||
|   '@vitest/coverage-v8@3.2.1(vitest@3.2.1)': | ||||
|     dependencies: | ||||
|       '@ampproject/remapping': 2.3.0 | ||||
|       '@bcoe/v8-coverage': 1.0.2 | ||||
| @ -17936,9 +18007,7 @@ snapshots: | ||||
|       std-env: 3.9.0 | ||||
|       test-exclude: 7.0.1 | ||||
|       tinyrainbow: 2.0.0 | ||||
|       vitest: 3.2.0(@types/debug@4.1.12)(@types/node@22.15.29)(@vitest/browser@3.2.0)(@vitest/ui@3.2.0)(happy-dom@17.6.1)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
|     optionalDependencies: | ||||
|       '@vitest/browser': 3.2.0(bufferutil@4.0.9)(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(playwright@1.52.0)(utf-8-validate@6.0.5)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.0)(webdriverio@9.15.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)) | ||||
|       vitest: 3.2.1(@types/debug@4.1.12)(@types/node@22.15.29)(@vitest/ui@3.2.1)(happy-dom@17.6.1)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
|     transitivePeerDependencies: | ||||
|       - supports-color | ||||
| 
 | ||||
| @ -17950,6 +18019,14 @@ snapshots: | ||||
|       chai: 5.2.0 | ||||
|       tinyrainbow: 2.0.0 | ||||
| 
 | ||||
|   '@vitest/expect@3.2.1': | ||||
|     dependencies: | ||||
|       '@types/chai': 5.2.2 | ||||
|       '@vitest/spy': 3.2.1 | ||||
|       '@vitest/utils': 3.2.1 | ||||
|       chai: 5.2.0 | ||||
|       tinyrainbow: 2.0.0 | ||||
| 
 | ||||
|   '@vitest/mocker@3.2.0(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0))': | ||||
|     dependencies: | ||||
|       '@vitest/spy': 3.2.0 | ||||
| @ -17959,25 +18036,53 @@ snapshots: | ||||
|       msw: 2.7.5(@types/node@22.15.29)(typescript@5.8.3) | ||||
|       vite: 6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
| 
 | ||||
|   '@vitest/mocker@3.2.1(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0))': | ||||
|     dependencies: | ||||
|       '@vitest/spy': 3.2.1 | ||||
|       estree-walker: 3.0.3 | ||||
|       magic-string: 0.30.17 | ||||
|     optionalDependencies: | ||||
|       msw: 2.7.5(@types/node@22.15.29)(typescript@5.8.3) | ||||
|       vite: 6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
| 
 | ||||
|   '@vitest/pretty-format@3.2.0': | ||||
|     dependencies: | ||||
|       tinyrainbow: 2.0.0 | ||||
| 
 | ||||
|   '@vitest/pretty-format@3.2.1': | ||||
|     dependencies: | ||||
|       tinyrainbow: 2.0.0 | ||||
| 
 | ||||
|   '@vitest/runner@3.2.0': | ||||
|     dependencies: | ||||
|       '@vitest/utils': 3.2.0 | ||||
|       pathe: 2.0.3 | ||||
| 
 | ||||
|   '@vitest/runner@3.2.1': | ||||
|     dependencies: | ||||
|       '@vitest/utils': 3.2.1 | ||||
|       pathe: 2.0.3 | ||||
| 
 | ||||
|   '@vitest/snapshot@3.2.0': | ||||
|     dependencies: | ||||
|       '@vitest/pretty-format': 3.2.0 | ||||
|       magic-string: 0.30.17 | ||||
|       pathe: 2.0.3 | ||||
| 
 | ||||
|   '@vitest/snapshot@3.2.1': | ||||
|     dependencies: | ||||
|       '@vitest/pretty-format': 3.2.1 | ||||
|       magic-string: 0.30.17 | ||||
|       pathe: 2.0.3 | ||||
| 
 | ||||
|   '@vitest/spy@3.2.0': | ||||
|     dependencies: | ||||
|       tinyspy: 4.0.3 | ||||
| 
 | ||||
|   '@vitest/spy@3.2.1': | ||||
|     dependencies: | ||||
|       tinyspy: 4.0.3 | ||||
| 
 | ||||
|   '@vitest/ui@3.2.0(vitest@3.2.0)': | ||||
|     dependencies: | ||||
|       '@vitest/utils': 3.2.0 | ||||
| @ -17988,6 +18093,18 @@ snapshots: | ||||
|       tinyglobby: 0.2.14 | ||||
|       tinyrainbow: 2.0.0 | ||||
|       vitest: 3.2.0(@types/debug@4.1.12)(@types/node@22.15.29)(@vitest/browser@3.2.0)(@vitest/ui@3.2.0)(happy-dom@17.6.1)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
|     optional: true | ||||
| 
 | ||||
|   '@vitest/ui@3.2.1(vitest@3.2.1)': | ||||
|     dependencies: | ||||
|       '@vitest/utils': 3.2.1 | ||||
|       fflate: 0.8.2 | ||||
|       flatted: 3.3.3 | ||||
|       pathe: 2.0.3 | ||||
|       sirv: 3.0.1 | ||||
|       tinyglobby: 0.2.14 | ||||
|       tinyrainbow: 2.0.0 | ||||
|       vitest: 3.2.1(@types/debug@4.1.12)(@types/node@22.15.29)(@vitest/ui@3.2.1)(happy-dom@17.6.1)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
| 
 | ||||
|   '@vitest/utils@3.2.0': | ||||
|     dependencies: | ||||
| @ -17995,6 +18112,12 @@ snapshots: | ||||
|       loupe: 3.1.3 | ||||
|       tinyrainbow: 2.0.0 | ||||
| 
 | ||||
|   '@vitest/utils@3.2.1': | ||||
|     dependencies: | ||||
|       '@vitest/pretty-format': 3.2.1 | ||||
|       loupe: 3.1.3 | ||||
|       tinyrainbow: 2.0.0 | ||||
| 
 | ||||
|   '@volar/language-core@2.4.13': | ||||
|     dependencies: | ||||
|       '@volar/source-map': 2.4.13 | ||||
| @ -18009,7 +18132,7 @@ snapshots: | ||||
| 
 | ||||
|   '@vue/compiler-core@3.5.14': | ||||
|     dependencies: | ||||
|       '@babel/parser': 7.27.4 | ||||
|       '@babel/parser': 7.27.5 | ||||
|       '@vue/shared': 3.5.14 | ||||
|       entities: 4.5.0 | ||||
|       estree-walker: 2.0.2 | ||||
| @ -19290,6 +19413,13 @@ snapshots: | ||||
|       readable-stream: 2.3.8 | ||||
|       typedarray: 0.0.6 | ||||
| 
 | ||||
|   concat-stream@2.0.0: | ||||
|     dependencies: | ||||
|       buffer-from: 1.1.2 | ||||
|       inherits: 2.0.4 | ||||
|       readable-stream: 3.6.2 | ||||
|       typedarray: 0.0.6 | ||||
| 
 | ||||
|   confbox@0.1.8: {} | ||||
| 
 | ||||
|   confbox@0.2.2: {} | ||||
| @ -22034,7 +22164,7 @@ snapshots: | ||||
|   istanbul-lib-instrument@5.2.1: | ||||
|     dependencies: | ||||
|       '@babel/core': 7.26.10 | ||||
|       '@babel/parser': 7.27.4 | ||||
|       '@babel/parser': 7.27.5 | ||||
|       '@istanbuljs/schema': 0.1.3 | ||||
|       istanbul-lib-coverage: 3.2.2 | ||||
|       semver: 6.3.1 | ||||
| @ -22839,7 +22969,7 @@ snapshots: | ||||
| 
 | ||||
|   magicast@0.3.5: | ||||
|     dependencies: | ||||
|       '@babel/parser': 7.27.4 | ||||
|       '@babel/parser': 7.27.5 | ||||
|       '@babel/types': 7.27.3 | ||||
|       source-map-js: 1.2.1 | ||||
| 
 | ||||
| @ -23428,11 +23558,11 @@ snapshots: | ||||
| 
 | ||||
|   muggle-string@0.4.1: {} | ||||
| 
 | ||||
|   multer@2.0.0: | ||||
|   multer@2.0.1: | ||||
|     dependencies: | ||||
|       append-field: 1.0.0 | ||||
|       busboy: 1.6.0 | ||||
|       concat-stream: 1.6.2 | ||||
|       concat-stream: 2.0.0 | ||||
|       mkdirp: 0.5.6 | ||||
|       object-assign: 4.1.1 | ||||
|       type-is: 1.6.18 | ||||
| @ -23761,7 +23891,7 @@ snapshots: | ||||
|       is-docker: 2.2.1 | ||||
|       is-wsl: 2.2.0 | ||||
| 
 | ||||
|   openai@5.0.2(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.4): | ||||
|   openai@5.1.0(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.4): | ||||
|     optionalDependencies: | ||||
|       ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5) | ||||
|       zod: 3.24.4 | ||||
| @ -26868,6 +26998,27 @@ snapshots: | ||||
|       - tsx | ||||
|       - yaml | ||||
| 
 | ||||
|   vite-node@3.2.1(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0): | ||||
|     dependencies: | ||||
|       cac: 6.7.14 | ||||
|       debug: 4.4.1(supports-color@6.0.0) | ||||
|       es-module-lexer: 1.7.0 | ||||
|       pathe: 2.0.3 | ||||
|       vite: 6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
|     transitivePeerDependencies: | ||||
|       - '@types/node' | ||||
|       - jiti | ||||
|       - less | ||||
|       - lightningcss | ||||
|       - sass | ||||
|       - sass-embedded | ||||
|       - stylus | ||||
|       - sugarss | ||||
|       - supports-color | ||||
|       - terser | ||||
|       - tsx | ||||
|       - yaml | ||||
| 
 | ||||
|   vite-plugin-dts@4.5.4(@types/node@22.15.29)(rollup@4.40.0)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0)): | ||||
|     dependencies: | ||||
|       '@microsoft/api-extractor': 7.52.8(@types/node@22.15.29) | ||||
| @ -26969,6 +27120,51 @@ snapshots: | ||||
|       - tsx | ||||
|       - yaml | ||||
| 
 | ||||
|   vitest@3.2.1(@types/debug@4.1.12)(@types/node@22.15.29)(@vitest/ui@3.2.1)(happy-dom@17.6.1)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0): | ||||
|     dependencies: | ||||
|       '@types/chai': 5.2.2 | ||||
|       '@vitest/expect': 3.2.1 | ||||
|       '@vitest/mocker': 3.2.1(msw@2.7.5(@types/node@22.15.29)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0)) | ||||
|       '@vitest/pretty-format': 3.2.1 | ||||
|       '@vitest/runner': 3.2.1 | ||||
|       '@vitest/snapshot': 3.2.1 | ||||
|       '@vitest/spy': 3.2.1 | ||||
|       '@vitest/utils': 3.2.1 | ||||
|       chai: 5.2.0 | ||||
|       debug: 4.4.1(supports-color@6.0.0) | ||||
|       expect-type: 1.2.1 | ||||
|       magic-string: 0.30.17 | ||||
|       pathe: 2.0.3 | ||||
|       picomatch: 4.0.2 | ||||
|       std-env: 3.9.0 | ||||
|       tinybench: 2.9.0 | ||||
|       tinyexec: 0.3.2 | ||||
|       tinyglobby: 0.2.14 | ||||
|       tinypool: 1.1.0 | ||||
|       tinyrainbow: 2.0.0 | ||||
|       vite: 6.3.5(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
|       vite-node: 3.2.1(@types/node@22.15.29)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.0)(tsx@4.19.4)(yaml@2.8.0) | ||||
|       why-is-node-running: 2.3.0 | ||||
|     optionalDependencies: | ||||
|       '@types/debug': 4.1.12 | ||||
|       '@types/node': 22.15.29 | ||||
|       '@vitest/ui': 3.2.1(vitest@3.2.1) | ||||
|       happy-dom: 17.6.1 | ||||
|       jsdom: 26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) | ||||
|     transitivePeerDependencies: | ||||
|       - jiti | ||||
|       - less | ||||
|       - lightningcss | ||||
|       - msw | ||||
|       - sass | ||||
|       - sass-embedded | ||||
|       - stylus | ||||
|       - sugarss | ||||
|       - supports-color | ||||
|       - terser | ||||
|       - tsx | ||||
|       - yaml | ||||
| 
 | ||||
|   vscode-jsonrpc@8.2.0: {} | ||||
| 
 | ||||
|   vscode-languageserver-protocol@3.17.5: | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Elian Doran
						Elian Doran