From fc896129e086d2bcd732d30b57cdc99f9efffdbd Mon Sep 17 00:00:00 2001 From: callumalpass Date: Sat, 3 Oct 2026 15:03:00 +1000 Subject: [PATCH 1/3] List every earlier starter as a seed upgrade baseline Pack beta.4 named only the beta.3 starters as `upgrade_from`, so a collection installed from an earlier pack was merged against the wrong document: upgrading an edited beta.1 seed treated beta.1's `match` rules, which beta.3 removed, as the collection's own edit and restored them. mdbase-spec 05A (mdbase-dev/mdbase-spec#59) lets a seed list every starter it supports and has engines choose the baseline from the lock's seed origin. Pack beta.5 ships the same version-2 starters and lists, newest first, each distinct starter Reader has shipped, with its `version`: - reader-source: beta.3 (82869c1), beta.2 (54f46e7), beta.1 (e8a3fca) - reader-annotation: beta.3 (1fb7e75), beta.1 (9b6b0fc) beta.2 changed only reader-source, and the beta.3 starters were also served as beta.1 between #20 and #26. The new baselines are the exact released bytes from 5c55c7d and fde2582; the released beta.2 pack is added as a fixture. An unedited starter from any of them is replaced with the exact version-2 bytes. An edited seed is merged against the starter it was installed from; for beta.1 and beta.2 that removes the top-level `match` rule, which 05A leaves to manual review, so the upgrade fails closed instead of restoring it. A seed whose lock predates recorded origins is kept with a reason. Verification installs packs with @callumalpass/mdbase 0.3.0-rc.9, which supports baseline lists. Until a Connect release includes mdbase-dev/mdbase-connect#567, @mdbase-dev/connect-dev rejects them, so connect-protocol 0.1.0-beta.124 is patched with that PR's schema change; drop the patch when moving to that release. --- apps/extension/src/generated/mdbase-app.json | 39 ++- .../1.0.0-beta.1/types/reader-annotation.md | 74 +++++ .../1.0.0-beta.1/types/reader-source.md | 100 +++++++ .../1.0.0-beta.2/types/reader-source.md | 110 +++++++ .../reader/public/.well-known/mdbase-app.json | 39 ++- .../scripts/fixtures/reader-pack-beta2.json | 69 +++++ apps/reader/scripts/reader-manifest.mjs | 47 ++- apps/reader/scripts/reader-type-pack.test.mjs | 275 +++++++++++++++--- apps/reader/src/generated/mdbase-app.json | 39 ++- package.json | 2 +- ...dev__connect-protocol@0.1.0-beta.124.patch | 38 +++ pnpm-lock.yaml | 21 +- pnpm-workspace.yaml | 6 + 13 files changed, 764 insertions(+), 95 deletions(-) create mode 100644 apps/reader/mdbase/baselines/1.0.0-beta.1/types/reader-annotation.md create mode 100644 apps/reader/mdbase/baselines/1.0.0-beta.1/types/reader-source.md create mode 100644 apps/reader/mdbase/baselines/1.0.0-beta.2/types/reader-source.md create mode 100644 apps/reader/scripts/fixtures/reader-pack-beta2.json create mode 100644 patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch diff --git a/apps/extension/src/generated/mdbase-app.json b/apps/extension/src/generated/mdbase-app.json index aff4834..6a9b23c 100644 --- a/apps/extension/src/generated/mdbase-app.json +++ b/apps/extension/src/generated/mdbase-app.json @@ -45,7 +45,7 @@ "manifest": { "kind": "mdbase.type-pack", "id": "dev.mdbase.reader", - "version": "1.0.0-beta.4", + "version": "1.0.0-beta.5", "name": "mdbase Reader", "description": "Reader source and annotation contracts with starter implementations.", "resources": [ @@ -69,10 +69,23 @@ "source": "types/reader-source.md", "target": "_types/reader-source.md", "digest": "sha256:7d8d2a9fd0e9aa448d7b35e0c0587ee52222d01a739f66d770b92f721bc2a597", - "upgrade_from": { - "digest": "sha256:82869c12234662297cc273aab993732d35827b8a2ba8ad74bec18975cadf0c1a", - "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n description: { type: string }\n language: { type: string }\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.1\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n description: description\n language: language\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" - } + "upgrade_from": [ + { + "digest": "sha256:82869c12234662297cc273aab993732d35827b8a2ba8ad74bec18975cadf0c1a", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n description: { type: string }\n language: { type: string }\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.1\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n description: description\n language: language\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" + }, + { + "digest": "sha256:54f46e7c3bb70e1cea5a37afa458406a0acf2be7f195c8893160782d87254990", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n original_url: { type: string }\n description: { type: string }\n language: { type: string }\n site: { type: string }\n image: { type: string }\n capture:\n type: object\n additionalProperties: true\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\nmatch:\n path_glob: sources/**/*.md\n fields_present: [id, title, kind]\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.2\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n original_url: original_url\n description: description\n language: language\n site: site\n image: image\n capture: capture\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" + }, + { + "digest": "sha256:e8a3fca643d335e684810c2e83bc9e9c3fc702ecf5a23d29283615902e0cfa55", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n description: { type: string }\n language: { type: string }\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\nmatch:\n path_glob: sources/**/*.md\n fields_present: [id, title, kind]\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.1\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n description: description\n language: language\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" + } + ] }, { "kind": "type", @@ -80,10 +93,18 @@ "source": "types/reader-annotation.md", "target": "_types/reader-annotation.md", "digest": "sha256:19e174941e390cb446ec63ea91a43fe26eba9918d70cd9f86ad693cca485628b", - "upgrade_from": { - "digest": "sha256:1fb7e758a4aacd139a30f15d7937ca18e95a6d6bec447a5b2cd8e6fb7907a901", - "document": "---\nkind: mdbase.type\nname: reader-annotation\nversion: 1\ndescription: An independently addressable mdbase Reader annotation.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, source, annotation_type, created_at]\n properties:\n type: { const: reader-annotation }\n id: { type: string, minLength: 1 }\n source: { type: string, minLength: 1 }\n document:\n type: object\n additionalProperties: false\n required: [file_id, file, revision]\n properties:\n file_id: { type: string, minLength: 1 }\n file: { type: string, minLength: 1 }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n annotation_type: { type: string, minLength: 1 }\n motivation: { type: string }\n color: { type: string }\n locator:\n type: object\n additionalProperties: true\n target:\n type: object\n additionalProperties: true\n tags:\n type: array\n items: { type: string }\n created_at: { type: string, format: date-time }\n modified_at: { type: string, format: date-time }\n created_by: { type: string }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n source:\n target_type: reader-source\n validate_exists: true\n document.file:\n target_type: any\n validate_exists: true\nimplements:\n - contract: dev.mdbase.reader.annotation\n version: 1.0.0-beta.1\n fields:\n id: id\n source: source\n document: document\n annotation_type: annotation_type\n motivation: motivation\n color: color\n locator: locator\n target: target\n tags: tags\n created_at: created_at\n modified_at: modified_at\n created_by: created_by\n---\n\n# Reader annotation\n\nThe body contains a readable quotation, an optional note, or both.\n" - } + "upgrade_from": [ + { + "digest": "sha256:1fb7e758a4aacd139a30f15d7937ca18e95a6d6bec447a5b2cd8e6fb7907a901", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-annotation\nversion: 1\ndescription: An independently addressable mdbase Reader annotation.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, source, annotation_type, created_at]\n properties:\n type: { const: reader-annotation }\n id: { type: string, minLength: 1 }\n source: { type: string, minLength: 1 }\n document:\n type: object\n additionalProperties: false\n required: [file_id, file, revision]\n properties:\n file_id: { type: string, minLength: 1 }\n file: { type: string, minLength: 1 }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n annotation_type: { type: string, minLength: 1 }\n motivation: { type: string }\n color: { type: string }\n locator:\n type: object\n additionalProperties: true\n target:\n type: object\n additionalProperties: true\n tags:\n type: array\n items: { type: string }\n created_at: { type: string, format: date-time }\n modified_at: { type: string, format: date-time }\n created_by: { type: string }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n source:\n target_type: reader-source\n validate_exists: true\n document.file:\n target_type: any\n validate_exists: true\nimplements:\n - contract: dev.mdbase.reader.annotation\n version: 1.0.0-beta.1\n fields:\n id: id\n source: source\n document: document\n annotation_type: annotation_type\n motivation: motivation\n color: color\n locator: locator\n target: target\n tags: tags\n created_at: created_at\n modified_at: modified_at\n created_by: created_by\n---\n\n# Reader annotation\n\nThe body contains a readable quotation, an optional note, or both.\n" + }, + { + "digest": "sha256:9b6b0fc7e1d883b9a56c743c3e5525eb80946c76cae34e9158fc574bb87a4cbe", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-annotation\nversion: 1\ndescription: An independently addressable mdbase Reader annotation.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, source, annotation_type, created_at]\n properties:\n type: { const: reader-annotation }\n id: { type: string, minLength: 1 }\n source: { type: string, minLength: 1 }\n document:\n type: object\n additionalProperties: false\n required: [file_id, file, revision]\n properties:\n file_id: { type: string, minLength: 1 }\n file: { type: string, minLength: 1 }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n annotation_type: { type: string, minLength: 1 }\n motivation: { type: string }\n color: { type: string }\n locator:\n type: object\n additionalProperties: true\n target:\n type: object\n additionalProperties: true\n tags:\n type: array\n items: { type: string }\n created_at: { type: string, format: date-time }\n modified_at: { type: string, format: date-time }\n created_by: { type: string }\nmatch:\n path_glob: annotations/**/*.md\n fields_present: [id, source, annotation_type]\ncollection:\n unique:\n - field: id\n scope: type\n links:\n source:\n target_type: reader-source\n validate_exists: true\n document.file:\n target_type: any\n validate_exists: true\nimplements:\n - contract: dev.mdbase.reader.annotation\n version: 1.0.0-beta.1\n fields:\n id: id\n source: source\n document: document\n annotation_type: annotation_type\n motivation: motivation\n color: color\n locator: locator\n target: target\n tags: tags\n created_at: created_at\n modified_at: modified_at\n created_by: created_by\n---\n\n# Reader annotation\n\nThe body contains a readable quotation, an optional note, or both.\n" + } + ] } ] }, diff --git a/apps/reader/mdbase/baselines/1.0.0-beta.1/types/reader-annotation.md b/apps/reader/mdbase/baselines/1.0.0-beta.1/types/reader-annotation.md new file mode 100644 index 0000000..0b54ce0 --- /dev/null +++ b/apps/reader/mdbase/baselines/1.0.0-beta.1/types/reader-annotation.md @@ -0,0 +1,74 @@ +--- +kind: mdbase.type +name: reader-annotation +version: 1 +description: An independently addressable mdbase Reader annotation. +schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + type: object + additionalProperties: true + required: [type, id, source, annotation_type, created_at] + properties: + type: { const: reader-annotation } + id: { type: string, minLength: 1 } + source: { type: string, minLength: 1 } + document: + type: object + additionalProperties: false + required: [file_id, file, revision] + properties: + file_id: { type: string, minLength: 1 } + file: { type: string, minLength: 1 } + revision: { type: string, pattern: "^sha256:[0-9a-f]{64}$" } + annotation_type: { type: string, minLength: 1 } + motivation: { type: string } + color: { type: string } + locator: + type: object + additionalProperties: true + target: + type: object + additionalProperties: true + tags: + type: array + items: { type: string } + created_at: { type: string, format: date-time } + modified_at: { type: string, format: date-time } + created_by: { type: string } +match: + path_glob: annotations/**/*.md + fields_present: [id, source, annotation_type] +collection: + unique: + - field: id + scope: type + links: + source: + target_type: reader-source + validate_exists: true + document.file: + target_type: any + validate_exists: true +implements: + - contract: dev.mdbase.reader.annotation + version: 1.0.0-beta.1 + fields: + id: id + source: source + document: document + annotation_type: annotation_type + motivation: motivation + color: color + locator: locator + target: target + tags: tags + created_at: created_at + modified_at: modified_at + created_by: created_by +--- + +# Reader annotation + +The body contains a readable quotation, an optional note, or both. diff --git a/apps/reader/mdbase/baselines/1.0.0-beta.1/types/reader-source.md b/apps/reader/mdbase/baselines/1.0.0-beta.1/types/reader-source.md new file mode 100644 index 0000000..683c326 --- /dev/null +++ b/apps/reader/mdbase/baselines/1.0.0-beta.1/types/reader-source.md @@ -0,0 +1,100 @@ +--- +kind: mdbase.type +name: reader-source +version: 1 +description: A source and user-authored literature note for mdbase Reader. +schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + type: object + additionalProperties: true + required: [type, id, title, kind, saved_at] + properties: + type: { const: reader-source } + id: { type: string, minLength: 1 } + title: { type: string, minLength: 1 } + kind: { type: string, minLength: 1 } + authors: + type: array + items: { type: string } + published: + oneOf: + - type: string + - type: number + url: { type: string } + description: { type: string } + language: { type: string } + saved_at: { type: string, format: date-time } + documents: + type: array + items: + type: object + additionalProperties: true + required: [file] + properties: + file_id: { type: string } + file: { type: string, minLength: 1 } + role: { type: string } + revision: { type: string, pattern: "^sha256:[0-9a-f]{64}$" } + format: { type: string } + media_type: { type: string } + label: { type: string } + reading: + type: object + additionalProperties: true + properties: + status: { type: string } + progress: { type: number, minimum: 0, maximum: 1 } + tags: + type: array + items: + type: [string, number, boolean] + relations: + type: array + items: + type: object + additionalProperties: true + required: [relation, target] + properties: + relation: { type: string } + target: { type: string } + csl: { type: object } +match: + path_glob: sources/**/*.md + fields_present: [id, title, kind] +collection: + unique: + - field: id + scope: type + links: + documents[].file: + target_type: any + validate_exists: true + relations[].target: + target_type: reader-source + validate_exists: false +implements: + - contract: dev.mdbase.reader.source + version: 1.0.0-beta.1 + fields: + id: id + title: title + kind: kind + authors: authors + published: published + url: url + description: description + language: language + saved_at: saved_at + documents: documents + reading: reading + tags: tags + relations: relations + csl: csl +--- + +# Reader source + +The source body is the user's literature note. Reader never regenerates it +from annotations. diff --git a/apps/reader/mdbase/baselines/1.0.0-beta.2/types/reader-source.md b/apps/reader/mdbase/baselines/1.0.0-beta.2/types/reader-source.md new file mode 100644 index 0000000..af30d3c --- /dev/null +++ b/apps/reader/mdbase/baselines/1.0.0-beta.2/types/reader-source.md @@ -0,0 +1,110 @@ +--- +kind: mdbase.type +name: reader-source +version: 1 +description: A source and user-authored literature note for mdbase Reader. +schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + type: object + additionalProperties: true + required: [type, id, title, kind, saved_at] + properties: + type: { const: reader-source } + id: { type: string, minLength: 1 } + title: { type: string, minLength: 1 } + kind: { type: string, minLength: 1 } + authors: + type: array + items: { type: string } + published: + oneOf: + - type: string + - type: number + url: { type: string } + original_url: { type: string } + description: { type: string } + language: { type: string } + site: { type: string } + image: { type: string } + capture: + type: object + additionalProperties: true + saved_at: { type: string, format: date-time } + documents: + type: array + items: + type: object + additionalProperties: true + required: [file] + properties: + file_id: { type: string } + file: { type: string, minLength: 1 } + role: { type: string } + revision: { type: string, pattern: "^sha256:[0-9a-f]{64}$" } + format: { type: string } + media_type: { type: string } + label: { type: string } + reading: + type: object + additionalProperties: true + properties: + status: { type: string } + progress: { type: number, minimum: 0, maximum: 1 } + tags: + type: array + items: + type: [string, number, boolean] + relations: + type: array + items: + type: object + additionalProperties: true + required: [relation, target] + properties: + relation: { type: string } + target: { type: string } + csl: { type: object } +match: + path_glob: sources/**/*.md + fields_present: [id, title, kind] +collection: + unique: + - field: id + scope: type + links: + documents[].file: + target_type: any + validate_exists: true + relations[].target: + target_type: reader-source + validate_exists: false +implements: + - contract: dev.mdbase.reader.source + version: 1.0.0-beta.2 + fields: + id: id + title: title + kind: kind + authors: authors + published: published + url: url + original_url: original_url + description: description + language: language + site: site + image: image + capture: capture + saved_at: saved_at + documents: documents + reading: reading + tags: tags + relations: relations + csl: csl +--- + +# Reader source + +The source body is the user's literature note. Reader never regenerates it +from annotations. diff --git a/apps/reader/public/.well-known/mdbase-app.json b/apps/reader/public/.well-known/mdbase-app.json index e59119e..3b398a2 100644 --- a/apps/reader/public/.well-known/mdbase-app.json +++ b/apps/reader/public/.well-known/mdbase-app.json @@ -48,7 +48,7 @@ "manifest": { "kind": "mdbase.type-pack", "id": "dev.mdbase.reader", - "version": "1.0.0-beta.4", + "version": "1.0.0-beta.5", "name": "mdbase Reader", "description": "Reader source and annotation contracts with starter implementations.", "resources": [ @@ -72,10 +72,23 @@ "source": "types/reader-source.md", "target": "_types/reader-source.md", "digest": "sha256:7d8d2a9fd0e9aa448d7b35e0c0587ee52222d01a739f66d770b92f721bc2a597", - "upgrade_from": { - "digest": "sha256:82869c12234662297cc273aab993732d35827b8a2ba8ad74bec18975cadf0c1a", - "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n description: { type: string }\n language: { type: string }\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.1\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n description: description\n language: language\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" - } + "upgrade_from": [ + { + "digest": "sha256:82869c12234662297cc273aab993732d35827b8a2ba8ad74bec18975cadf0c1a", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n description: { type: string }\n language: { type: string }\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.1\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n description: description\n language: language\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" + }, + { + "digest": "sha256:54f46e7c3bb70e1cea5a37afa458406a0acf2be7f195c8893160782d87254990", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n original_url: { type: string }\n description: { type: string }\n language: { type: string }\n site: { type: string }\n image: { type: string }\n capture:\n type: object\n additionalProperties: true\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\nmatch:\n path_glob: sources/**/*.md\n fields_present: [id, title, kind]\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.2\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n original_url: original_url\n description: description\n language: language\n site: site\n image: image\n capture: capture\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" + }, + { + "digest": "sha256:e8a3fca643d335e684810c2e83bc9e9c3fc702ecf5a23d29283615902e0cfa55", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n description: { type: string }\n language: { type: string }\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\nmatch:\n path_glob: sources/**/*.md\n fields_present: [id, title, kind]\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.1\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n description: description\n language: language\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" + } + ] }, { "kind": "type", @@ -83,10 +96,18 @@ "source": "types/reader-annotation.md", "target": "_types/reader-annotation.md", "digest": "sha256:19e174941e390cb446ec63ea91a43fe26eba9918d70cd9f86ad693cca485628b", - "upgrade_from": { - "digest": "sha256:1fb7e758a4aacd139a30f15d7937ca18e95a6d6bec447a5b2cd8e6fb7907a901", - "document": "---\nkind: mdbase.type\nname: reader-annotation\nversion: 1\ndescription: An independently addressable mdbase Reader annotation.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, source, annotation_type, created_at]\n properties:\n type: { const: reader-annotation }\n id: { type: string, minLength: 1 }\n source: { type: string, minLength: 1 }\n document:\n type: object\n additionalProperties: false\n required: [file_id, file, revision]\n properties:\n file_id: { type: string, minLength: 1 }\n file: { type: string, minLength: 1 }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n annotation_type: { type: string, minLength: 1 }\n motivation: { type: string }\n color: { type: string }\n locator:\n type: object\n additionalProperties: true\n target:\n type: object\n additionalProperties: true\n tags:\n type: array\n items: { type: string }\n created_at: { type: string, format: date-time }\n modified_at: { type: string, format: date-time }\n created_by: { type: string }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n source:\n target_type: reader-source\n validate_exists: true\n document.file:\n target_type: any\n validate_exists: true\nimplements:\n - contract: dev.mdbase.reader.annotation\n version: 1.0.0-beta.1\n fields:\n id: id\n source: source\n document: document\n annotation_type: annotation_type\n motivation: motivation\n color: color\n locator: locator\n target: target\n tags: tags\n created_at: created_at\n modified_at: modified_at\n created_by: created_by\n---\n\n# Reader annotation\n\nThe body contains a readable quotation, an optional note, or both.\n" - } + "upgrade_from": [ + { + "digest": "sha256:1fb7e758a4aacd139a30f15d7937ca18e95a6d6bec447a5b2cd8e6fb7907a901", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-annotation\nversion: 1\ndescription: An independently addressable mdbase Reader annotation.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, source, annotation_type, created_at]\n properties:\n type: { const: reader-annotation }\n id: { type: string, minLength: 1 }\n source: { type: string, minLength: 1 }\n document:\n type: object\n additionalProperties: false\n required: [file_id, file, revision]\n properties:\n file_id: { type: string, minLength: 1 }\n file: { type: string, minLength: 1 }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n annotation_type: { type: string, minLength: 1 }\n motivation: { type: string }\n color: { type: string }\n locator:\n type: object\n additionalProperties: true\n target:\n type: object\n additionalProperties: true\n tags:\n type: array\n items: { type: string }\n created_at: { type: string, format: date-time }\n modified_at: { type: string, format: date-time }\n created_by: { type: string }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n source:\n target_type: reader-source\n validate_exists: true\n document.file:\n target_type: any\n validate_exists: true\nimplements:\n - contract: dev.mdbase.reader.annotation\n version: 1.0.0-beta.1\n fields:\n id: id\n source: source\n document: document\n annotation_type: annotation_type\n motivation: motivation\n color: color\n locator: locator\n target: target\n tags: tags\n created_at: created_at\n modified_at: modified_at\n created_by: created_by\n---\n\n# Reader annotation\n\nThe body contains a readable quotation, an optional note, or both.\n" + }, + { + "digest": "sha256:9b6b0fc7e1d883b9a56c743c3e5525eb80946c76cae34e9158fc574bb87a4cbe", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-annotation\nversion: 1\ndescription: An independently addressable mdbase Reader annotation.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, source, annotation_type, created_at]\n properties:\n type: { const: reader-annotation }\n id: { type: string, minLength: 1 }\n source: { type: string, minLength: 1 }\n document:\n type: object\n additionalProperties: false\n required: [file_id, file, revision]\n properties:\n file_id: { type: string, minLength: 1 }\n file: { type: string, minLength: 1 }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n annotation_type: { type: string, minLength: 1 }\n motivation: { type: string }\n color: { type: string }\n locator:\n type: object\n additionalProperties: true\n target:\n type: object\n additionalProperties: true\n tags:\n type: array\n items: { type: string }\n created_at: { type: string, format: date-time }\n modified_at: { type: string, format: date-time }\n created_by: { type: string }\nmatch:\n path_glob: annotations/**/*.md\n fields_present: [id, source, annotation_type]\ncollection:\n unique:\n - field: id\n scope: type\n links:\n source:\n target_type: reader-source\n validate_exists: true\n document.file:\n target_type: any\n validate_exists: true\nimplements:\n - contract: dev.mdbase.reader.annotation\n version: 1.0.0-beta.1\n fields:\n id: id\n source: source\n document: document\n annotation_type: annotation_type\n motivation: motivation\n color: color\n locator: locator\n target: target\n tags: tags\n created_at: created_at\n modified_at: modified_at\n created_by: created_by\n---\n\n# Reader annotation\n\nThe body contains a readable quotation, an optional note, or both.\n" + } + ] } ] }, diff --git a/apps/reader/scripts/fixtures/reader-pack-beta2.json b/apps/reader/scripts/fixtures/reader-pack-beta2.json new file mode 100644 index 0000000..a1c79c3 --- /dev/null +++ b/apps/reader/scripts/fixtures/reader-pack-beta2.json @@ -0,0 +1,69 @@ +{ + "provides": [ + { + "id": "dev.mdbase.reader.source", + "version": "1.0.0-beta.2", + "digest": "sha256:2739fe726154e3219170ab02bb33c469a67e4349be580f3b16bae5f161564381" + }, + { + "id": "dev.mdbase.reader.annotation", + "version": "1.0.0-beta.1", + "digest": "sha256:9593376fed19903b07013ce0167b1d25ff0517364c826ddb8ecf3b4b7d83ef7c" + } + ], + "manifest": { + "kind": "mdbase.type-pack", + "id": "dev.mdbase.reader", + "version": "1.0.0-beta.2", + "name": "mdbase Reader", + "description": "Reader source and annotation contracts with starter implementations.", + "resources": [ + { + "kind": "contract", + "mode": "managed", + "source": "contracts/dev.mdbase.reader.source.md", + "target": "_contracts/dev.mdbase.reader.source.md", + "digest": "sha256:f1ce31663665e6c3b7e5cb52910893242ef073f669b074195c7536866cbd584a" + }, + { + "kind": "contract", + "mode": "managed", + "source": "contracts/dev.mdbase.reader.annotation.md", + "target": "_contracts/dev.mdbase.reader.annotation.md", + "digest": "sha256:28e045ca097a8ca0e9758fdd7562fbb4443b38e6089c6c9be4427cbe60e3c8bf" + }, + { + "kind": "type", + "mode": "seed", + "source": "types/reader-source.md", + "target": "_types/reader-source.md", + "digest": "sha256:54f46e7c3bb70e1cea5a37afa458406a0acf2be7f195c8893160782d87254990" + }, + { + "kind": "type", + "mode": "seed", + "source": "types/reader-annotation.md", + "target": "_types/reader-annotation.md", + "digest": "sha256:9b6b0fc7e1d883b9a56c743c3e5525eb80946c76cae34e9158fc574bb87a4cbe" + } + ] + }, + "resources": [ + { + "source": "contracts/dev.mdbase.reader.source.md", + "document": "---\nkind: mdbase.contract\ncontract_type: record\nid: dev.mdbase.reader.source\nversion: 1.0.0-beta.2\nname: mdbase Reader source\ndescription: A saved source, its readable representations, and its literature note.\nrecord_schema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: false\n required: [id, title, kind, saved_at]\n properties:\n id:\n type: string\n minLength: 1\n title:\n type: string\n minLength: 1\n kind:\n type: string\n minLength: 1\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url:\n type: string\n original_url:\n type: string\n description:\n type: string\n language:\n type: string\n site:\n type: string\n image:\n type: string\n capture:\n type: object\n additionalProperties: true\n properties:\n method: { type: string }\n application: { type: string }\n captured_at: { type: string, format: date-time }\n submitted_url: { type: string }\n canonical_url: { type: string }\n saved_at:\n type: string\n format: date-time\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl:\n type: object\n---\n\n# mdbase Reader source\n\nThe portable Reader view of a saved source. The Markdown body remains the\nuser-authored literature note and is deliberately outside the frontmatter\nprojection.\n" + }, + { + "source": "contracts/dev.mdbase.reader.annotation.md", + "document": "---\nkind: mdbase.contract\ncontract_type: record\nid: dev.mdbase.reader.annotation\nversion: 1.0.0-beta.1\nname: mdbase Reader annotation\ndescription: An independently addressable annotation attached to a Reader source.\nrecord_schema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: false\n required: [id, source, annotation_type, created_at]\n properties:\n id: { type: string, minLength: 1 }\n source: { type: string, minLength: 1 }\n document:\n type: object\n additionalProperties: false\n required: [file_id, file, revision]\n properties:\n file_id: { type: string, minLength: 1 }\n file: { type: string, minLength: 1 }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n annotation_type: { type: string, minLength: 1 }\n motivation: { type: string }\n color: { type: string }\n locator:\n type: object\n additionalProperties: true\n target:\n type: object\n additionalProperties: true\n tags:\n type: array\n items: { type: string }\n created_at: { type: string, format: date-time }\n modified_at: { type: string, format: date-time }\n created_by: { type: string }\n---\n\n# mdbase Reader annotation\n\nThe portable Reader view of an annotation. Its Markdown body contains the\nreadable quotation and the user's note.\n" + }, + { + "source": "types/reader-source.md", + "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n original_url: { type: string }\n description: { type: string }\n language: { type: string }\n site: { type: string }\n image: { type: string }\n capture:\n type: object\n additionalProperties: true\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\nmatch:\n path_glob: sources/**/*.md\n fields_present: [id, title, kind]\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.2\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n original_url: original_url\n description: description\n language: language\n site: site\n image: image\n capture: capture\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" + }, + { + "source": "types/reader-annotation.md", + "document": "---\nkind: mdbase.type\nname: reader-annotation\nversion: 1\ndescription: An independently addressable mdbase Reader annotation.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, source, annotation_type, created_at]\n properties:\n type: { const: reader-annotation }\n id: { type: string, minLength: 1 }\n source: { type: string, minLength: 1 }\n document:\n type: object\n additionalProperties: false\n required: [file_id, file, revision]\n properties:\n file_id: { type: string, minLength: 1 }\n file: { type: string, minLength: 1 }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n annotation_type: { type: string, minLength: 1 }\n motivation: { type: string }\n color: { type: string }\n locator:\n type: object\n additionalProperties: true\n target:\n type: object\n additionalProperties: true\n tags:\n type: array\n items: { type: string }\n created_at: { type: string, format: date-time }\n modified_at: { type: string, format: date-time }\n created_by: { type: string }\nmatch:\n path_glob: annotations/**/*.md\n fields_present: [id, source, annotation_type]\ncollection:\n unique:\n - field: id\n scope: type\n links:\n source:\n target_type: reader-source\n validate_exists: true\n document.file:\n target_type: any\n validate_exists: true\nimplements:\n - contract: dev.mdbase.reader.annotation\n version: 1.0.0-beta.1\n fields:\n id: id\n source: source\n document: document\n annotation_type: annotation_type\n motivation: motivation\n color: color\n locator: locator\n target: target\n tags: tags\n created_at: created_at\n modified_at: modified_at\n created_by: created_by\n---\n\n# Reader annotation\n\nThe body contains a readable quotation, an optional note, or both.\n" + } + ] +} diff --git a/apps/reader/scripts/reader-manifest.mjs b/apps/reader/scripts/reader-manifest.mjs index 51719d6..882da6c 100644 --- a/apps/reader/scripts/reader-manifest.mjs +++ b/apps/reader/scripts/reader-manifest.mjs @@ -8,18 +8,23 @@ import { parse as parseYaml } from "yaml"; // Pack releases are immutable independently of the data contracts they provide. // beta.2 was used historically; do not reuse it even though the contracts remain beta.1. // beta.4 ships version 2 of the starter types, which no longer pin or require `type`. -export const READER_TYPE_PACK_VERSION = "1.0.0-beta.4"; +// beta.5 ships the same starters and lists every earlier starter as an upgrade baseline. +export const READER_TYPE_PACK_VERSION = "1.0.0-beta.5"; const projectRoot = resolve(import.meta.dirname, ".."); // Reader saves library views as mdbase.view records; this is the published // mdbase-contracts provision, embedded byte-for-byte. const viewPackPath = resolve(projectRoot, "mdbase", "packs", "mdbase.view-1.0.1.json"); -// Seed types belong to the collection once installed. Version 1 (pack beta.3) declared -// `type: { const: }` and required `type`, which fails in collections whose +// Seed types belong to the collection once installed. Version 1 (packs beta.1 to beta.3) +// declared `type: { const: }` and required `type`, which fails in collections whose // settings.explicit_type_keys record the type elsewhere (such as `[mdbase_type]`, where -// `type` holds CSL data). Each seed therefore names the exact previous bytes it replaces, -// so Connect can offer a reviewed three-way upgrade that keeps collection edits. -// Baselines under mdbase/baselines/ are released bytes: never edit them. +// `type` holds CSL data). Each seed therefore lists the exact bytes of every starter Reader +// has shipped for it, newest first, so Connect can offer a reviewed upgrade from any of +// them: an unedited starter is replaced, and an edited one is merged three-way against the +// starter it was installed from, keeping collection edits. +// Baselines under mdbase/baselines// are released bytes: never edit them. +// A pack version that shipped a starter identical to an earlier one has no copy of it: +// beta.2 changed only reader-source, and the beta.3 starters also shipped late in beta.1. const resources = [ { kind: "contract", @@ -38,14 +43,21 @@ const resources = [ mode: "seed", source: "types/reader-source.md", target: "_types/reader-source.md", - upgradeFrom: "baselines/1.0.0-beta.3/types/reader-source.md", + upgradeFrom: [ + "baselines/1.0.0-beta.3/types/reader-source.md", + "baselines/1.0.0-beta.2/types/reader-source.md", + "baselines/1.0.0-beta.1/types/reader-source.md", + ], }, { kind: "type", mode: "seed", source: "types/reader-annotation.md", target: "_types/reader-annotation.md", - upgradeFrom: "baselines/1.0.0-beta.3/types/reader-annotation.md", + upgradeFrom: [ + "baselines/1.0.0-beta.3/types/reader-annotation.md", + "baselines/1.0.0-beta.1/types/reader-annotation.md", + ], }, ]; @@ -58,16 +70,21 @@ export async function buildReaderManifest({ const packResources = await Promise.all( resources.map(async (resource) => { const document = await readFile(resolve(projectRoot, "mdbase", resource.source), "utf8"); - const baseline = resource.upgradeFrom - ? await readFile(resolve(projectRoot, "mdbase", resource.upgradeFrom), "utf8") - : undefined; + const baselines = await Promise.all( + (resource.upgradeFrom ?? []).map(async (path) => { + const baseline = await readFile(resolve(projectRoot, "mdbase", path), "utf8"); + return { + digest: digest(baseline), + version: parseFrontmatter(baseline).version, + document: baseline, + }; + }), + ); return { ...resource, digest: digest(document), document, - ...(baseline === undefined - ? {} - : { upgrade_from: { digest: digest(baseline), document: baseline } }), + ...(baselines.length === 0 ? {} : { upgrade_from: baselines }), ...(resource.kind === "contract" ? { contractDigest: dataContractDigest(parseFrontmatter(document)) } : {}), @@ -135,7 +152,7 @@ function digest(document) { function parseFrontmatter(document) { const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u.exec(document); if (!match?.[1]) { - throw new Error("Reader contract resource has no YAML frontmatter."); + throw new Error("Reader pack resource has no YAML frontmatter."); } return parseYaml(match[1]); } diff --git a/apps/reader/scripts/reader-type-pack.test.mjs b/apps/reader/scripts/reader-type-pack.test.mjs index 0c19d19..fbdb8ec 100644 --- a/apps/reader/scripts/reader-type-pack.test.mjs +++ b/apps/reader/scripts/reader-type-pack.test.mjs @@ -15,6 +15,11 @@ import { buildReaderManifest } from "./reader-manifest.mjs"; const legacy = JSON.parse( await readFile(new URL("./fixtures/reader-pack-beta1.json", import.meta.url), "utf8"), ); +// Exact released pack from fde25820c64dfd44ee6c10842c2da8daac3733f8: its reader-source +// starter implemented source contract 1.0.0-beta.2, which 52eb850 reverted to beta.1. +const legacyBeta2 = JSON.parse( + await readFile(new URL("./fixtures/reader-pack-beta2.json", import.meta.url), "utf8"), +); const application = await buildReaderManifest(); const current = application.provisions.type_packs.find( (pack) => pack.manifest.id === "dev.mdbase.reader", @@ -26,11 +31,12 @@ const options = { installedBy: application.id }; const provision = (pack) => ({ manifest: pack.manifest, resources: pack.resources }); // Published identities must not change. Add a new version/digest instead of updating this pin. -// Pins are the installer's desired pack digest, which covers `upgrade_from`; beta.4's pin -// matches the mdbase CLI's `packs assess`. +// Pins are the installer's desired pack digest, which covers `upgrade_from`; the beta.4 and +// beta.5 pins match the mdbase CLI's `packs assess`. const releasedDigests = { "1.0.0-beta.3": "sha256:712aa3d2ad5ca8719505b27c8c72f39fb9643660edbbeee3321e01ab53ef6938", "1.0.0-beta.4": "sha256:f66f0b5e96df834c2aeab9aaddb4dc6eb6533111892a121ccb2b89ec15660dac", + "1.0.0-beta.5": "sha256:a8c684491b91ad290a8bd63a3709a949ea58679c69fc600aabba2422da95055b", }; // The vendored mdbase-contracts view pack, pinned the same way. 1.0.1 ships the version-2 // view starter (no pinned `type`, open top level) with `upgrade_from` the 1.0.0 starter; @@ -38,12 +44,19 @@ const releasedDigests = { const releasedViewPackDigests = { "1.0.1": "sha256:b32222bce5f16b01b87c34736b0e70bfad3583b5678557fa6c42ee706f7ef1d4", }; -// Exact bytes of the version-1 starter types released in beta.3, which beta.4 upgrades from. -const beta3SeedDigests = { - "types/reader-source.md": - "sha256:82869c12234662297cc273aab993732d35827b8a2ba8ad74bec18975cadf0c1a", - "types/reader-annotation.md": - "sha256:1fb7e758a4aacd139a30f15d7937ca18e95a6d6bec447a5b2cd8e6fb7907a901", +// Every distinct starter Reader shipped before version 2, by the first pack that shipped it, +// newest first: the order beta.5 lists them as `upgrade_from` baselines. beta.2 changed only +// reader-source, and the beta.3 starters were also served as beta.1 between #20 and #26. +const earlierStarters = { + "types/reader-source.md": { + "1.0.0-beta.3": "sha256:82869c12234662297cc273aab993732d35827b8a2ba8ad74bec18975cadf0c1a", + "1.0.0-beta.2": "sha256:54f46e7c3bb70e1cea5a37afa458406a0acf2be7f195c8893160782d87254990", + "1.0.0-beta.1": "sha256:e8a3fca643d335e684810c2e83bc9e9c3fc702ecf5a23d29283615902e0cfa55", + }, + "types/reader-annotation.md": { + "1.0.0-beta.3": "sha256:1fb7e758a4aacd139a30f15d7937ca18e95a6d6bec447a5b2cd8e6fb7907a901", + "1.0.0-beta.1": "sha256:9b6b0fc7e1d883b9a56c743c3e5525eb80946c76cae34e9158fc574bb87a4cbe", + }, }; function sha256(value) { @@ -54,20 +67,48 @@ function frontmatter(document) { return parseYaml(/^---\n([\s\S]*?)\n---\n/u.exec(document)[1]); } -// beta.3 as released: beta.4's manifest with each seed's `upgrade_from` as its document. +function baseline(resource, version) { + const digest = earlierStarters[resource.source][version]; + return resource.upgrade_from.find((candidate) => candidate.digest === digest); +} + +// beta.3 as released: the current manifest with each seed's beta.3 baseline as its document. function beta3() { const pack = structuredClone(current); pack.manifest.version = "1.0.0-beta.3"; for (const resource of pack.manifest.resources) { if (!resource.upgrade_from) continue; - resource.digest = resource.upgrade_from.digest; - pack.resources.find(({ source }) => source === resource.source).document = - resource.upgrade_from.document; + const { digest, document } = baseline(resource, "1.0.0-beta.3"); + resource.digest = digest; + pack.resources.find(({ source }) => source === resource.source).document = document; delete resource.upgrade_from; } return pack; } +// beta.4 as released: the current starters with the beta.3 starter as their single baseline. +function beta4() { + const pack = structuredClone(current); + pack.manifest.version = "1.0.0-beta.4"; + for (const resource of pack.manifest.resources) { + if (!resource.upgrade_from) continue; + const { digest, document } = baseline(resource, "1.0.0-beta.3"); + resource.upgrade_from = { digest, document }; + } + return pack; +} + +const earlierPacks = { + "1.0.0-beta.1": () => legacy, + "1.0.0-beta.2": () => legacyBeta2, + "1.0.0-beta.3": beta3, +}; + +// The starter a pack shipped for a seed, by its digest in that pack. +function starterVersion(source, digest) { + return Object.entries(earlierStarters[source]).find(([, pinned]) => pinned === digest)?.[0]; +} + async function collection(t) { const root = await mkdtemp(join(tmpdir(), "reader-pack-upgrade-")); t.after(() => rm(root, { recursive: true, force: true })); @@ -116,7 +157,7 @@ async function assertPreserved(contents) { } test("Reader's new pack release preserves contract identities and capability protocol", async (t) => { - assert.equal(current.manifest.version, "1.0.0-beta.4"); + assert.equal(current.manifest.version, "1.0.0-beta.5"); assert.deepEqual(current.provides, legacy.provides); assert.equal(application.requirements.capabilities.contract_version, 2); assert.equal(viewPack.manifest.version, "1.0.1"); @@ -142,21 +183,162 @@ test("the released pack digest stays immutable and is independent of deployment assert.deepEqual(staging.provisions.type_packs[0], current); }); -test("seed upgrades start from the exact beta.3 starter types", async (t) => { +test("seed upgrades list every earlier starter type, newest first, as exact bytes", async (t) => { const seeds = current.manifest.resources.filter((resource) => resource.mode === "seed"); - assert.deepEqual(seeds.map((seed) => seed.source).sort(), Object.keys(beta3SeedDigests).sort()); + assert.deepEqual(seeds.map((seed) => seed.source).sort(), Object.keys(earlierStarters).sort()); + const desired = new Map(current.resources.map(({ source, document }) => [source, document])); for (const seed of seeds) { assert.equal(seed.kind, "type"); - assert.equal(seed.upgrade_from.digest, beta3SeedDigests[seed.source]); - assert.equal(sha256(seed.upgrade_from.document), seed.upgrade_from.digest); - assert.equal(frontmatter(seed.upgrade_from.document).version, 1); + assert.ok(Array.isArray(seed.upgrade_from), seed.source); + assert.deepEqual( + seed.upgrade_from.map(({ digest }) => digest), + Object.values(earlierStarters[seed.source]), + ); + const type = frontmatter(desired.get(seed.source)); + for (const { digest, document, version } of seed.upgrade_from) { + assert.equal(sha256(document), digest); + assert.notEqual(digest, seed.digest); + const starter = frontmatter(document); + assert.equal(starter.kind, type.kind); + assert.equal(starter.name, type.name); + assert.equal(version, starter.version); + assert.equal(version, 1); + } } for (const resource of current.manifest.resources.filter(({ mode }) => mode !== "seed")) { assert.equal(resource.upgrade_from, undefined); } - // Rebuilding beta.3 from the baselines reproduces its released pack identity exactly. - const assessment = await assess(await collection(t), beta3()); - assert.equal(assessment.desired.digest, releasedDigests["1.0.0-beta.3"]); + // The released beta.1 and beta.2 packs shipped exactly these baselines. + for (const [version, pack] of Object.entries(earlierPacks)) { + const documents = new Map(pack().resources.map(({ source, document }) => [source, document])); + for (const resource of pack().manifest.resources.filter(({ mode }) => mode === "seed")) { + const starter = baseline( + current.manifest.resources.find(({ source }) => source === resource.source), + starterVersion(resource.source, resource.digest), + ); + assert.equal( + starter?.document, + documents.get(resource.source), + `${version} ${resource.source}`, + ); + } + } + // Rebuilding beta.3 and beta.4 from the baselines reproduces their released identities. + for (const [version, pack] of [ + ["1.0.0-beta.3", beta3()], + ["1.0.0-beta.4", beta4()], + ]) { + const assessment = await assess(await collection(t), pack); + assert.equal(assessment.desired.digest, releasedDigests[version], version); + } +}); + +for (const [version, pack] of Object.entries(earlierPacks)) { + test(`replaces unedited ${version} starters with the exact version-2 seeds`, async (t) => { + const root = await collection(t); + const installed = pack(); + await apply(root, installed, await assess(root, installed)); + const upgrade = await assess(root, current); + assert.equal(upgrade.status, "upgrade"); + assert.equal(upgrade.applicable, true); + const installedDigests = new Map( + installed.manifest.resources.map(({ source, digest }) => [source, digest]), + ); + for (const resource of upgrade.resources.filter(({ mode }) => mode === "seed")) { + assert.equal(resource.action, "update", resource.source); + // Each seed is replaced from the starter its pack shipped, which may predate the pack. + const starter = installedDigests.get(resource.source); + assert.ok(starterVersion(resource.source, starter), resource.source); + assert.deepEqual(resource.upgrade_baseline, { digest: starter, version: 1 }); + } + await apply(root, current, upgrade); + for (const resource of current.resources.filter(({ source }) => source.startsWith("types/"))) { + const target = current.manifest.resources.find(({ source }) => source === resource.source); + assert.equal(await readFile(join(root, target.target), "utf8"), resource.document); + } + assert.equal((await assess(root, current)).status, "current"); + }); +} + +// beta.1's starters had top-level `match` rules that later starters removed. A seed edited +// since beta.1 is merged against beta.1, so that removal is the publisher's: 05A requires a +// top-level removal to be reviewed by hand, so the upgrade fails closed instead of merging +// against beta.3 and silently restoring the rule as if it were the collection's edit. +test("fails closed on an edited beta.1 starter instead of restoring its match rule", async (t) => { + const root = await collection(t); + await apply(root, legacy, await assess(root, legacy)); + const sourceType = join(root, "_types", "reader-source.md"); + const original = await readFile(sourceType, "utf8"); + assert.equal(frontmatter(original).match.path_glob, "sources/**/*.md"); + const edited = original.replace("\ndescription: ", "\ndescription: Collection edit. "); + await writeFile(sourceType, edited); + const upgrade = await assess(root, current); + assert.equal(upgrade.status, "conflict"); + assert.equal(upgrade.applicable, false); + const seeds = new Map(upgrade.resources.map((resource) => [resource.source, resource])); + assert.equal(seeds.get("types/reader-source.md").action, "conflict"); + assert.match(seeds.get("types/reader-source.md").reason, /top-level type setting/u); + // The unedited annotation starter is still replaced from its own beta.1 baseline. + assert.equal(seeds.get("types/reader-annotation.md").action, "update"); + assert.deepEqual(seeds.get("types/reader-annotation.md").upgrade_baseline, { + digest: earlierStarters["types/reader-annotation.md"]["1.0.0-beta.1"], + version: 1, + }); + assert.equal(await readFile(sourceType, "utf8"), edited); +}); + +test("merges an edited beta.1 starter against the beta.1 baseline", async (t) => { + const root = await collection(t); + await apply(root, legacy, await assess(root, legacy)); + const sourceType = join(root, "_types", "reader-source.md"); + // The collection edited its description and, like every later starter, dropped `match`. + const edited = (await readFile(sourceType, "utf8")) + .replace("\ndescription: ", "\ndescription: Collection edit. ") + .replace(/\nmatch:\n(?: {2}.*\n)+/u, "\n"); + assert.equal(Object.hasOwn(frontmatter(edited), "match"), false); + await writeFile(sourceType, edited); + const upgrade = await assess(root, current); + assert.equal(upgrade.applicable, true); + const seed = upgrade.resources.find(({ source }) => source === "types/reader-source.md"); + assert.equal(seed.action, "update"); + assert.deepEqual(seed.upgrade_baseline, { + digest: earlierStarters["types/reader-source.md"]["1.0.0-beta.1"], + version: 1, + }); + await apply(root, current, upgrade); + const type = frontmatter(await readFile(sourceType, "utf8")); + assert.equal(type.version, 2); + assert.match(type.description, /^Collection edit\. /u); + assert.equal(Object.hasOwn(type, "match"), false); + assert.equal(Object.hasOwn(type.schema.value.properties, "type"), false); + assert.equal(type.schema.value.required.includes("type"), false); + assert.equal((await assess(root, current)).status, "current"); +}); + +test("keeps an edited seed whose lock predates recorded origins instead of guessing a baseline", async (t) => { + const root = await collection(t); + await apply(root, legacy, await assess(root, legacy)); + const lockPath = join(root, "mdbase.lock.yaml"); + const lock = JSON.parse(await readFile(lockPath, "utf8")); + // Locks written before mdbase-spec 05A recorded seed origins have no origin_digest. + for (const resource of lock.packs[0].resources) { + if (resource.mode !== "seed") continue; + assert.ok(resource.origin_digest, resource.source); + delete resource.origin_digest; + } + await writeFile(lockPath, `${JSON.stringify(lock, null, 2)}\n`); + const sourceType = join(root, "_types", "reader-source.md"); + const edited = `${await readFile(sourceType, "utf8")}\nCollection note.\n`; + await writeFile(sourceType, edited); + const upgrade = await assess(root, current); + assert.equal(upgrade.applicable, true); + const seeds = new Map(upgrade.resources.map((resource) => [resource.source, resource])); + assert.equal(seeds.get("types/reader-source.md").action, "preserve"); + assert.ok(seeds.get("types/reader-source.md").reason); + // Unedited bytes still prove their origin. + assert.equal(seeds.get("types/reader-annotation.md").action, "update"); + await apply(root, current, upgrade); + assert.equal(await readFile(sourceType, "utf8"), edited); }); for (const edited of [false, true]) { @@ -177,9 +359,15 @@ for (const edited of [false, true]) { assert.equal(upgrade.applicable, true); for (const resource of upgrade.resources) { assert.equal(resource.action, resource.mode === "seed" ? "update" : "unchanged"); + if (resource.mode === "seed") { + assert.deepEqual(resource.upgrade_baseline, { + digest: earlierStarters[resource.source]["1.0.0-beta.3"], + version: 1, + }); + } } const applied = await apply(root, current, upgrade); - assert.equal(applied.receipt.version, "1.0.0-beta.4"); + assert.equal(applied.receipt.version, current.manifest.version); const desired = new Map(current.resources.map(({ source, document }) => [source, document])); for (const resource of current.manifest.resources.filter(({ mode }) => mode === "seed")) { const document = await readFile(join(root, resource.target), "utf8"); @@ -311,7 +499,6 @@ for (const typeKeys of ["[type]", "[mdbase_type]"]) { test("reproduces the immutable beta.1 digest conflict without touching installed files", async (t) => { const root = await collection(t); await apply(root, legacy, await assess(root, legacy)); - const contents = await preserveExamples(root, legacy); const reusedVersion = structuredClone(current); reusedVersion.manifest.version = legacy.manifest.version; const conflict = await assess(root, reusedVersion); @@ -322,45 +509,45 @@ test("reproduces the immutable beta.1 digest conflict without touching installed resource.reason?.includes("different immutable pack digest"), ), ); + const contents = await preserveExamples(root, legacy); + const edited = await assess(root, reusedVersion); + assert.equal(edited.status, "conflict"); + assert.equal(edited.applicable, false); await assertPreserved(contents); }); -// original-beta.1's seeds are version-1 starters the collection edited, so beta.4 merges its -// version-2 changes into them (three-way, from the beta.3 baseline) and keeps the edits. +// original-beta.1's seeds are version-1 starters the collection edited. They are merged +// against the beta.1 starters they were installed from (not beta.3's, which would restore +// beta.1's removed `match` rules as if they were collection edits), and that removal needs +// manual review, so the upgrade fails closed and touches nothing. // changed-beta.1 already shipped the version-2 starters, so the edited seeds are kept as is. for (const variant of ["original-beta.1", "changed-beta.1"]) { - test(`upgrades ${variant} to beta.4 preserving contracts, customized seed types and notes`, async (t) => { + test(`upgrades ${variant} to the current pack preserving contracts, customized seed types and notes`, async (t) => { const root = await collection(t); const original = variant === "original-beta.1"; const installed = structuredClone(original ? legacy : current); installed.manifest.version = "1.0.0-beta.1"; await apply(root, installed, await assess(root, installed)); const contents = await preserveExamples(root, installed); - const seeds = installed.manifest.resources - .filter(({ mode }) => mode === "seed") - .map(({ target }) => join(root, target)); - if (original) for (const seed of seeds) contents.delete(seed); const upgrade = await assess(root, current); + if (original) { + assert.equal(upgrade.status, "conflict"); + assert.equal(upgrade.applicable, false); + for (const resource of upgrade.resources) { + assert.equal(resource.action, resource.mode === "seed" ? "conflict" : "unchanged"); + if (resource.mode === "seed") assert.match(resource.reason, /top-level type setting/u); + } + await assertPreserved(contents); + return; + } assert.equal(upgrade.status, "upgrade"); assert.equal(upgrade.applicable, true); for (const resource of upgrade.resources) { - const seedAction = original ? "update" : "preserve"; - assert.equal(resource.action, resource.mode === "seed" ? seedAction : "unchanged"); + assert.equal(resource.action, resource.mode === "seed" ? "preserve" : "unchanged"); } const applied = await apply(root, current, upgrade); - assert.equal(applied.receipt.version, "1.0.0-beta.4"); + assert.equal(applied.receipt.version, current.manifest.version); await assertPreserved(contents); - if (original) { - for (const seed of seeds) { - const document = await readFile(seed, "utf8"); - assert.equal(frontmatter(document).version, 2, seed); - assert.match( - document, - /\nCollection-owned customisation: keep this exact type document\.\n$/u, - ); - contents.set(seed, document); - } - } const again = await assess(root, current); assert.equal(again.status, "current"); assert.equal(again.applicable, true); diff --git a/apps/reader/src/generated/mdbase-app.json b/apps/reader/src/generated/mdbase-app.json index e59119e..3b398a2 100644 --- a/apps/reader/src/generated/mdbase-app.json +++ b/apps/reader/src/generated/mdbase-app.json @@ -48,7 +48,7 @@ "manifest": { "kind": "mdbase.type-pack", "id": "dev.mdbase.reader", - "version": "1.0.0-beta.4", + "version": "1.0.0-beta.5", "name": "mdbase Reader", "description": "Reader source and annotation contracts with starter implementations.", "resources": [ @@ -72,10 +72,23 @@ "source": "types/reader-source.md", "target": "_types/reader-source.md", "digest": "sha256:7d8d2a9fd0e9aa448d7b35e0c0587ee52222d01a739f66d770b92f721bc2a597", - "upgrade_from": { - "digest": "sha256:82869c12234662297cc273aab993732d35827b8a2ba8ad74bec18975cadf0c1a", - "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n description: { type: string }\n language: { type: string }\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.1\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n description: description\n language: language\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" - } + "upgrade_from": [ + { + "digest": "sha256:82869c12234662297cc273aab993732d35827b8a2ba8ad74bec18975cadf0c1a", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n description: { type: string }\n language: { type: string }\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.1\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n description: description\n language: language\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" + }, + { + "digest": "sha256:54f46e7c3bb70e1cea5a37afa458406a0acf2be7f195c8893160782d87254990", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n original_url: { type: string }\n description: { type: string }\n language: { type: string }\n site: { type: string }\n image: { type: string }\n capture:\n type: object\n additionalProperties: true\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\nmatch:\n path_glob: sources/**/*.md\n fields_present: [id, title, kind]\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.2\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n original_url: original_url\n description: description\n language: language\n site: site\n image: image\n capture: capture\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" + }, + { + "digest": "sha256:e8a3fca643d335e684810c2e83bc9e9c3fc702ecf5a23d29283615902e0cfa55", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-source\nversion: 1\ndescription: A source and user-authored literature note for mdbase Reader.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, title, kind, saved_at]\n properties:\n type: { const: reader-source }\n id: { type: string, minLength: 1 }\n title: { type: string, minLength: 1 }\n kind: { type: string, minLength: 1 }\n authors:\n type: array\n items: { type: string }\n published:\n oneOf:\n - type: string\n - type: number\n url: { type: string }\n description: { type: string }\n language: { type: string }\n saved_at: { type: string, format: date-time }\n documents:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [file]\n properties:\n file_id: { type: string }\n file: { type: string, minLength: 1 }\n role: { type: string }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n format: { type: string }\n media_type: { type: string }\n label: { type: string }\n reading:\n type: object\n additionalProperties: true\n properties:\n status: { type: string }\n progress: { type: number, minimum: 0, maximum: 1 }\n tags:\n type: array\n items:\n type: [string, number, boolean]\n relations:\n type: array\n items:\n type: object\n additionalProperties: true\n required: [relation, target]\n properties:\n relation: { type: string }\n target: { type: string }\n csl: { type: object }\nmatch:\n path_glob: sources/**/*.md\n fields_present: [id, title, kind]\ncollection:\n unique:\n - field: id\n scope: type\n links:\n documents[].file:\n target_type: any\n validate_exists: true\n relations[].target:\n target_type: reader-source\n validate_exists: false\nimplements:\n - contract: dev.mdbase.reader.source\n version: 1.0.0-beta.1\n fields:\n id: id\n title: title\n kind: kind\n authors: authors\n published: published\n url: url\n description: description\n language: language\n saved_at: saved_at\n documents: documents\n reading: reading\n tags: tags\n relations: relations\n csl: csl\n---\n\n# Reader source\n\nThe source body is the user's literature note. Reader never regenerates it\nfrom annotations.\n" + } + ] }, { "kind": "type", @@ -83,10 +96,18 @@ "source": "types/reader-annotation.md", "target": "_types/reader-annotation.md", "digest": "sha256:19e174941e390cb446ec63ea91a43fe26eba9918d70cd9f86ad693cca485628b", - "upgrade_from": { - "digest": "sha256:1fb7e758a4aacd139a30f15d7937ca18e95a6d6bec447a5b2cd8e6fb7907a901", - "document": "---\nkind: mdbase.type\nname: reader-annotation\nversion: 1\ndescription: An independently addressable mdbase Reader annotation.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, source, annotation_type, created_at]\n properties:\n type: { const: reader-annotation }\n id: { type: string, minLength: 1 }\n source: { type: string, minLength: 1 }\n document:\n type: object\n additionalProperties: false\n required: [file_id, file, revision]\n properties:\n file_id: { type: string, minLength: 1 }\n file: { type: string, minLength: 1 }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n annotation_type: { type: string, minLength: 1 }\n motivation: { type: string }\n color: { type: string }\n locator:\n type: object\n additionalProperties: true\n target:\n type: object\n additionalProperties: true\n tags:\n type: array\n items: { type: string }\n created_at: { type: string, format: date-time }\n modified_at: { type: string, format: date-time }\n created_by: { type: string }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n source:\n target_type: reader-source\n validate_exists: true\n document.file:\n target_type: any\n validate_exists: true\nimplements:\n - contract: dev.mdbase.reader.annotation\n version: 1.0.0-beta.1\n fields:\n id: id\n source: source\n document: document\n annotation_type: annotation_type\n motivation: motivation\n color: color\n locator: locator\n target: target\n tags: tags\n created_at: created_at\n modified_at: modified_at\n created_by: created_by\n---\n\n# Reader annotation\n\nThe body contains a readable quotation, an optional note, or both.\n" - } + "upgrade_from": [ + { + "digest": "sha256:1fb7e758a4aacd139a30f15d7937ca18e95a6d6bec447a5b2cd8e6fb7907a901", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-annotation\nversion: 1\ndescription: An independently addressable mdbase Reader annotation.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, source, annotation_type, created_at]\n properties:\n type: { const: reader-annotation }\n id: { type: string, minLength: 1 }\n source: { type: string, minLength: 1 }\n document:\n type: object\n additionalProperties: false\n required: [file_id, file, revision]\n properties:\n file_id: { type: string, minLength: 1 }\n file: { type: string, minLength: 1 }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n annotation_type: { type: string, minLength: 1 }\n motivation: { type: string }\n color: { type: string }\n locator:\n type: object\n additionalProperties: true\n target:\n type: object\n additionalProperties: true\n tags:\n type: array\n items: { type: string }\n created_at: { type: string, format: date-time }\n modified_at: { type: string, format: date-time }\n created_by: { type: string }\ncollection:\n unique:\n - field: id\n scope: type\n links:\n source:\n target_type: reader-source\n validate_exists: true\n document.file:\n target_type: any\n validate_exists: true\nimplements:\n - contract: dev.mdbase.reader.annotation\n version: 1.0.0-beta.1\n fields:\n id: id\n source: source\n document: document\n annotation_type: annotation_type\n motivation: motivation\n color: color\n locator: locator\n target: target\n tags: tags\n created_at: created_at\n modified_at: modified_at\n created_by: created_by\n---\n\n# Reader annotation\n\nThe body contains a readable quotation, an optional note, or both.\n" + }, + { + "digest": "sha256:9b6b0fc7e1d883b9a56c743c3e5525eb80946c76cae34e9158fc574bb87a4cbe", + "version": 1, + "document": "---\nkind: mdbase.type\nname: reader-annotation\nversion: 1\ndescription: An independently addressable mdbase Reader annotation.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n required: [type, id, source, annotation_type, created_at]\n properties:\n type: { const: reader-annotation }\n id: { type: string, minLength: 1 }\n source: { type: string, minLength: 1 }\n document:\n type: object\n additionalProperties: false\n required: [file_id, file, revision]\n properties:\n file_id: { type: string, minLength: 1 }\n file: { type: string, minLength: 1 }\n revision: { type: string, pattern: \"^sha256:[0-9a-f]{64}$\" }\n annotation_type: { type: string, minLength: 1 }\n motivation: { type: string }\n color: { type: string }\n locator:\n type: object\n additionalProperties: true\n target:\n type: object\n additionalProperties: true\n tags:\n type: array\n items: { type: string }\n created_at: { type: string, format: date-time }\n modified_at: { type: string, format: date-time }\n created_by: { type: string }\nmatch:\n path_glob: annotations/**/*.md\n fields_present: [id, source, annotation_type]\ncollection:\n unique:\n - field: id\n scope: type\n links:\n source:\n target_type: reader-source\n validate_exists: true\n document.file:\n target_type: any\n validate_exists: true\nimplements:\n - contract: dev.mdbase.reader.annotation\n version: 1.0.0-beta.1\n fields:\n id: id\n source: source\n document: document\n annotation_type: annotation_type\n motivation: motivation\n color: color\n locator: locator\n target: target\n tags: tags\n created_at: created_at\n modified_at: modified_at\n created_by: created_by\n---\n\n# Reader annotation\n\nThe body contains a readable quotation, an optional note, or both.\n" + } + ] } ] }, diff --git a/package.json b/package.json index 972da7c..14679ea 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "typecheck": "pnpm -r --if-present typecheck" }, "devDependencies": { - "@callumalpass/mdbase": "0.3.0-rc.8", + "@callumalpass/mdbase": "0.3.0-rc.9", "@eslint/js": "^9.39.2", "@mdbase-dev/connect-dev": "0.1.0-beta.124", "@types/node": "^22.19.11", diff --git a/patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch b/patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch new file mode 100644 index 0000000..ea6fbfe --- /dev/null +++ b/patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch @@ -0,0 +1,38 @@ +diff --git a/schemas/mdbase-app.schema.json b/schemas/mdbase-app.schema.json +index 997e49c6684e8008f40429eeb9331e8f84268404..8b40da6bc478c9e6d06fc401c7f0b46b45b04905 100644 +--- a/schemas/mdbase-app.schema.json ++++ b/schemas/mdbase-app.schema.json +@@ -319,19 +319,26 @@ + "kind": { "enum": ["contract", "type", "schema"] }, + "mode": { "enum": ["managed", "seed"] }, + "upgrade_from": { +- "type": "object", +- "additionalProperties": false, +- "required": ["digest", "document"], +- "properties": { +- "digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }, +- "document": { "type": "string", "maxLength": 262144 } +- } ++ "oneOf": [ ++ { "$ref": "#/$defs/typePackUpgradeBaseline" }, ++ { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/typePackUpgradeBaseline" } } ++ ] + }, + "source": { "type": "string", "minLength": 1, "maxLength": 240 }, + "target": { "type": "string", "minLength": 1, "maxLength": 240 }, + "digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" } + } + }, ++ "typePackUpgradeBaseline": { ++ "type": "object", ++ "additionalProperties": false, ++ "required": ["digest", "document"], ++ "properties": { ++ "digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }, ++ "document": { "type": "string", "maxLength": 262144 }, ++ "version": { "type": "integer", "minimum": 1 } ++ } ++ }, + "typePackSourceResource": { + "type": "object", + "additionalProperties": false, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5578887..538b075 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,13 +4,18 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + '@mdbase-dev/connect-protocol@0.1.0-beta.124': + hash: 00b5569ccd4801e1ff5ab009dce17eb7cf80695924159175f05bbf7e1ec45349 + path: patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch + importers: .: devDependencies: '@callumalpass/mdbase': - specifier: 0.3.0-rc.8 - version: 0.3.0-rc.8 + specifier: 0.3.0-rc.9 + version: 0.3.0-rc.9 '@eslint/js': specifier: ^9.39.2 version: 9.39.5 @@ -505,8 +510,8 @@ packages: resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} - '@callumalpass/mdbase@0.3.0-rc.8': - resolution: {integrity: sha512-fw8S64FaMt9YT/0WKb8mURWRn6wPca4PA7FpNPnDWquj2lm47CoBp+g25yrlJNUqPyCVgSs0lOSx6G/8u9uWJw==} + '@callumalpass/mdbase@0.3.0-rc.9': + resolution: {integrity: sha512-bXYhTqRqd928j9osTFGQTmzKG59W1pelmBV+UaEF1WOL+E5WEQVl4sBKvvdGvBVD9VMAq7IF1zJTkr3gqNb4Mw==} hasBin: true '@capacitor/browser@8.0.4': @@ -3462,7 +3467,7 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@callumalpass/mdbase@0.3.0-rc.8': + '@callumalpass/mdbase@0.3.0-rc.9': dependencies: '@marcbachmann/cel-js': 8.0.0 ajv: 8.20.0 @@ -4285,18 +4290,18 @@ snapshots: '@mdbase-dev/connect-dev@0.1.0-beta.124': dependencies: '@mdbase-dev/connect': 0.1.0-beta.124 - '@mdbase-dev/connect-protocol': 0.1.0-beta.124 + '@mdbase-dev/connect-protocol': 0.1.0-beta.124(patch_hash=00b5569ccd4801e1ff5ab009dce17eb7cf80695924159175f05bbf7e1ec45349) ajv: 8.20.0 yaml: 2.9.1 - '@mdbase-dev/connect-protocol@0.1.0-beta.124': + '@mdbase-dev/connect-protocol@0.1.0-beta.124(patch_hash=00b5569ccd4801e1ff5ab009dce17eb7cf80695924159175f05bbf7e1ec45349)': dependencies: ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) '@mdbase-dev/connect@0.1.0-beta.124': dependencies: - '@mdbase-dev/connect-protocol': 0.1.0-beta.124 + '@mdbase-dev/connect-protocol': 0.1.0-beta.124(patch_hash=00b5569ccd4801e1ff5ab009dce17eb7cf80695924159175f05bbf7e1ec45349) '@mdbase-dev/ui@0.1.0-beta.124(@codemirror/view@6.43.8)(react@19.2.8)': dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f4232ad..f290585 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,3 +6,9 @@ packages: onlyBuiltDependencies: - electron - esbuild + +# Backports mdbase-dev/mdbase-connect#567's app-manifest schema, which accepts a list of +# seed `upgrade_from` baselines, so validate-manifest accepts Reader's pack. Remove it when +# @mdbase-dev/connect-dev moves to the Connect release that includes #567. +patchedDependencies: + "@mdbase-dev/connect-protocol@0.1.0-beta.124": patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch From 5c490bd7c1b1f04938f8ebb06efc0db1514c4e15 Mon Sep 17 00:00:00 2001 From: callumalpass Date: Sat, 3 Oct 2026 18:06:03 +1000 Subject: [PATCH 2/3] Move to Connect SDK 0.1.0-beta.125 and drop the manifest-schema patch --- apps/extension/package.json | 2 +- apps/extension/src/generated/mdbase-app.json | 2 +- apps/reader/package.json | 2 +- package.json | 2 +- packages/connect/package.json | 2 +- packages/markdown-editor/package.json | 2 +- packages/ui/package.json | 2 +- ...dev__connect-protocol@0.1.0-beta.124.patch | 38 ------------ pnpm-lock.yaml | 60 +++++++++---------- pnpm-workspace.yaml | 6 -- 10 files changed, 35 insertions(+), 83 deletions(-) delete mode 100644 patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch diff --git a/apps/extension/package.json b/apps/extension/package.json index 86f3174..127ba2f 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -12,7 +12,7 @@ "typecheck": "tsc -p tsconfig.json" }, "dependencies": { - "@mdbase-dev/ui": "0.1.0-beta.124", + "@mdbase-dev/ui": "0.1.0-beta.125", "@mdbase-reader/connect": "workspace:*", "@mdbase-reader/core": "workspace:*", "@mdbase-reader/platform": "workspace:*", diff --git a/apps/extension/src/generated/mdbase-app.json b/apps/extension/src/generated/mdbase-app.json index 6a9b23c..df6ae5a 100644 --- a/apps/extension/src/generated/mdbase-app.json +++ b/apps/extension/src/generated/mdbase-app.json @@ -187,5 +187,5 @@ ] }, "distribution": "portable", - "project_url": "https://reader.mdbase.dev/" + "project_url": "https://lab.mdbase-reader.pages.dev/" } diff --git a/apps/reader/package.json b/apps/reader/package.json index 4b55e54..7281ea5 100644 --- a/apps/reader/package.json +++ b/apps/reader/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@citation-js/plugin-csl": "0.8.2", - "@mdbase-dev/ui": "0.1.0-beta.124", + "@mdbase-dev/ui": "0.1.0-beta.125", "@mdbase-reader/connect": "workspace:*", "@mdbase-reader/core": "workspace:*", "@mdbase-reader/markdown-editor": "workspace:*", diff --git a/package.json b/package.json index 14679ea..9c4f38b 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "devDependencies": { "@callumalpass/mdbase": "0.3.0-rc.9", "@eslint/js": "^9.39.2", - "@mdbase-dev/connect-dev": "0.1.0-beta.124", + "@mdbase-dev/connect-dev": "0.1.0-beta.125", "@types/node": "^22.19.11", "eslint": "^9.39.2", "eslint-import-resolver-typescript": "^4.4.5", diff --git a/packages/connect/package.json b/packages/connect/package.json index 3270168..f8f79e4 100644 --- a/packages/connect/package.json +++ b/packages/connect/package.json @@ -11,7 +11,7 @@ "typecheck": "tsc -p tsconfig.json" }, "dependencies": { - "@mdbase-dev/connect": "0.1.0-beta.124", + "@mdbase-dev/connect": "0.1.0-beta.125", "@mdbase-reader/core": "workspace:*", "@mdbase-reader/migration": "workspace:*", "yaml": "^2.9.0" diff --git a/packages/markdown-editor/package.json b/packages/markdown-editor/package.json index 95a67df..3f6222b 100644 --- a/packages/markdown-editor/package.json +++ b/packages/markdown-editor/package.json @@ -21,7 +21,7 @@ "@codemirror/state": "^6.5.2", "@codemirror/view": "^6.43.8", "@lezer/highlight": "^1.2.3", - "@mdbase-dev/ui": "0.1.0-beta.124", + "@mdbase-dev/ui": "0.1.0-beta.125", "@mdbase-reader/core": "workspace:*", "react": "^19.2.8" }, diff --git a/packages/ui/package.json b/packages/ui/package.json index 4e8a581..d5e608e 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -14,7 +14,7 @@ "dependencies": { "@fontsource/atkinson-hyperlegible": "^5.3.0", "@fontsource/azeret-mono": "^5.3.0", - "@mdbase-dev/ui": "0.1.0-beta.124", + "@mdbase-dev/ui": "0.1.0-beta.125", "@phosphor-icons/react": "^2.1.10", "react": "^19.2.8" }, diff --git a/patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch b/patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch deleted file mode 100644 index ea6fbfe..0000000 --- a/patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch +++ /dev/null @@ -1,38 +0,0 @@ -diff --git a/schemas/mdbase-app.schema.json b/schemas/mdbase-app.schema.json -index 997e49c6684e8008f40429eeb9331e8f84268404..8b40da6bc478c9e6d06fc401c7f0b46b45b04905 100644 ---- a/schemas/mdbase-app.schema.json -+++ b/schemas/mdbase-app.schema.json -@@ -319,19 +319,26 @@ - "kind": { "enum": ["contract", "type", "schema"] }, - "mode": { "enum": ["managed", "seed"] }, - "upgrade_from": { -- "type": "object", -- "additionalProperties": false, -- "required": ["digest", "document"], -- "properties": { -- "digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }, -- "document": { "type": "string", "maxLength": 262144 } -- } -+ "oneOf": [ -+ { "$ref": "#/$defs/typePackUpgradeBaseline" }, -+ { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/typePackUpgradeBaseline" } } -+ ] - }, - "source": { "type": "string", "minLength": 1, "maxLength": 240 }, - "target": { "type": "string", "minLength": 1, "maxLength": 240 }, - "digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" } - } - }, -+ "typePackUpgradeBaseline": { -+ "type": "object", -+ "additionalProperties": false, -+ "required": ["digest", "document"], -+ "properties": { -+ "digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }, -+ "document": { "type": "string", "maxLength": 262144 }, -+ "version": { "type": "integer", "minimum": 1 } -+ } -+ }, - "typePackSourceResource": { - "type": "object", - "additionalProperties": false, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 538b075..e10d11d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - '@mdbase-dev/connect-protocol@0.1.0-beta.124': - hash: 00b5569ccd4801e1ff5ab009dce17eb7cf80695924159175f05bbf7e1ec45349 - path: patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch - importers: .: @@ -20,8 +15,8 @@ importers: specifier: ^9.39.2 version: 9.39.5 '@mdbase-dev/connect-dev': - specifier: 0.1.0-beta.124 - version: 0.1.0-beta.124 + specifier: 0.1.0-beta.125 + version: 0.1.0-beta.125 '@types/node': specifier: ^22.19.11 version: 22.20.1 @@ -93,8 +88,8 @@ importers: apps/extension: dependencies: '@mdbase-dev/ui': - specifier: 0.1.0-beta.124 - version: 0.1.0-beta.124(@codemirror/view@6.43.8)(react@19.2.8) + specifier: 0.1.0-beta.125 + version: 0.1.0-beta.125(@codemirror/view@6.43.8)(react@19.2.8) '@mdbase-reader/connect': specifier: workspace:* version: link:../../packages/connect @@ -139,8 +134,8 @@ importers: specifier: 0.8.2 version: 0.8.2(@citation-js/core@0.8.2) '@mdbase-dev/ui': - specifier: 0.1.0-beta.124 - version: 0.1.0-beta.124(@codemirror/view@6.43.8)(react@19.2.8) + specifier: 0.1.0-beta.125 + version: 0.1.0-beta.125(@codemirror/view@6.43.8)(react@19.2.8) '@mdbase-reader/connect': specifier: workspace:* version: link:../../packages/connect @@ -229,8 +224,8 @@ importers: packages/connect: dependencies: '@mdbase-dev/connect': - specifier: 0.1.0-beta.124 - version: 0.1.0-beta.124 + specifier: 0.1.0-beta.125 + version: 0.1.0-beta.125 '@mdbase-reader/core': specifier: workspace:* version: link:../core @@ -276,8 +271,8 @@ importers: specifier: ^1.2.3 version: 1.2.3 '@mdbase-dev/ui': - specifier: 0.1.0-beta.124 - version: 0.1.0-beta.124(@codemirror/view@6.43.8)(react@19.2.8) + specifier: 0.1.0-beta.125 + version: 0.1.0-beta.125(@codemirror/view@6.43.8)(react@19.2.8) '@mdbase-reader/core': specifier: workspace:* version: link:../core @@ -416,8 +411,8 @@ importers: specifier: ^5.3.0 version: 5.3.0 '@mdbase-dev/ui': - specifier: 0.1.0-beta.124 - version: 0.1.0-beta.124(@codemirror/view@6.43.8)(react@19.2.8) + specifier: 0.1.0-beta.125 + version: 0.1.0-beta.125(@codemirror/view@6.43.8)(react@19.2.8) '@phosphor-icons/react': specifier: ^2.1.10 version: 2.1.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -1130,18 +1125,18 @@ packages: '@marijn/find-cluster-break@1.0.3': resolution: {integrity: sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==} - '@mdbase-dev/connect-dev@0.1.0-beta.124': - resolution: {integrity: sha512-QWvJg6SnNShu+yRz0e4Ac30mak9W9Hz9hQqdKtGjTG92oWopaH+bOEqOAiKci21wjmm8MivkKkGLwp2zrJSlxg==} + '@mdbase-dev/connect-dev@0.1.0-beta.125': + resolution: {integrity: sha512-fBYTD3+Fcl+I1FBvMiZ9tAGlvnaab+NRZ+J6pcuu3PdesZfbyanFDB5QRdpuXV+HVo8lbaXG145qQCpV2Wo4ZQ==} hasBin: true - '@mdbase-dev/connect-protocol@0.1.0-beta.124': - resolution: {integrity: sha512-p1jA+AqpqfhnV+foOXtoQg8oVK9oSe+W40MwzbW3x7kEeQvxRRxtmGDeVaqBlob3Fn+4Owx0G1KSRu3BzlnphA==} + '@mdbase-dev/connect-protocol@0.1.0-beta.125': + resolution: {integrity: sha512-q6aEZ1+dVAXvoN84FgDLMhqCnCdlsXhp0K5M+Wcp2x2ICUIoE/lA+eDg4AJqDqdWZ3Nq2mkiczvat6HG2y8EeQ==} - '@mdbase-dev/connect@0.1.0-beta.124': - resolution: {integrity: sha512-lV53SpYZjMqYD+tD5NDONsQAIPRHoFDm9OzF+SoG0hibqX5cPMu47LD9igZQIb63XFqvy+MOzjFz4SyGtlRWSg==} + '@mdbase-dev/connect@0.1.0-beta.125': + resolution: {integrity: sha512-vEH4NQfu3dBQgMw0YQxG7LDtOOLxDyxET4DQhrWuHqtgr9qoch/H0QLzel9tFSqkyH+QWxuvW3Bet6ae1+qVyQ==} - '@mdbase-dev/ui@0.1.0-beta.124': - resolution: {integrity: sha512-75xiQ9PhKjVyk9R+xaE0APpNn5MprfrkXwXhVQM5eT3uTCCZ0YwFCTeIAFsjEB6MNItp4pujoyebSIPQKwXg7w==} + '@mdbase-dev/ui@0.1.0-beta.125': + resolution: {integrity: sha512-K2UAAzwW4yuPJxx65LofVDQXKLgs4lOgrAB61YMuiNwV/jOc1yuNhGugy7Fx++VwxBx5bZyG+Pk7tMQlOat+tg==} peerDependencies: '@codemirror/view': ^6.0.0 react: ^19.2.0 @@ -4287,23 +4282,24 @@ snapshots: '@marijn/find-cluster-break@1.0.3': {} - '@mdbase-dev/connect-dev@0.1.0-beta.124': + '@mdbase-dev/connect-dev@0.1.0-beta.125': dependencies: - '@mdbase-dev/connect': 0.1.0-beta.124 - '@mdbase-dev/connect-protocol': 0.1.0-beta.124(patch_hash=00b5569ccd4801e1ff5ab009dce17eb7cf80695924159175f05bbf7e1ec45349) + '@mdbase-dev/connect': 0.1.0-beta.125 + '@mdbase-dev/connect-protocol': 0.1.0-beta.125 ajv: 8.20.0 yaml: 2.9.1 - '@mdbase-dev/connect-protocol@0.1.0-beta.124(patch_hash=00b5569ccd4801e1ff5ab009dce17eb7cf80695924159175f05bbf7e1ec45349)': + '@mdbase-dev/connect-protocol@0.1.0-beta.125': dependencies: ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) + yaml: 2.9.1 - '@mdbase-dev/connect@0.1.0-beta.124': + '@mdbase-dev/connect@0.1.0-beta.125': dependencies: - '@mdbase-dev/connect-protocol': 0.1.0-beta.124(patch_hash=00b5569ccd4801e1ff5ab009dce17eb7cf80695924159175f05bbf7e1ec45349) + '@mdbase-dev/connect-protocol': 0.1.0-beta.125 - '@mdbase-dev/ui@0.1.0-beta.124(@codemirror/view@6.43.8)(react@19.2.8)': + '@mdbase-dev/ui@0.1.0-beta.125(@codemirror/view@6.43.8)(react@19.2.8)': dependencies: '@fontsource/atkinson-hyperlegible': 5.3.0 '@fontsource/azeret-mono': 5.3.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f290585..f4232ad 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,9 +6,3 @@ packages: onlyBuiltDependencies: - electron - esbuild - -# Backports mdbase-dev/mdbase-connect#567's app-manifest schema, which accepts a list of -# seed `upgrade_from` baselines, so validate-manifest accepts Reader's pack. Remove it when -# @mdbase-dev/connect-dev moves to the Connect release that includes #567. -patchedDependencies: - "@mdbase-dev/connect-protocol@0.1.0-beta.124": patches/@mdbase-dev__connect-protocol@0.1.0-beta.124.patch From c236d1c0e02de08bab9f70082dcac10ded6032e9 Mon Sep 17 00:00:00 2001 From: callumalpass Date: Sat, 3 Oct 2026 18:13:12 +1000 Subject: [PATCH 3/3] Restore the production extension manifest --- apps/extension/src/generated/mdbase-app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/extension/src/generated/mdbase-app.json b/apps/extension/src/generated/mdbase-app.json index df6ae5a..6a9b23c 100644 --- a/apps/extension/src/generated/mdbase-app.json +++ b/apps/extension/src/generated/mdbase-app.json @@ -187,5 +187,5 @@ ] }, "distribution": "portable", - "project_url": "https://lab.mdbase-reader.pages.dev/" + "project_url": "https://reader.mdbase.dev/" }