@paulhectork/aiiinotate 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +206 -0
  3. package/cli/export.js +118 -0
  4. package/cli/import.js +111 -0
  5. package/cli/index.js +45 -0
  6. package/cli/migrate.js +131 -0
  7. package/cli/serve.js +29 -0
  8. package/cli/utils/fastifyClient.js +86 -0
  9. package/cli/utils/io.js +225 -0
  10. package/cli/utils/mongoClient.js +21 -0
  11. package/cli/utils/progressbar.js +86 -0
  12. package/cli/xywhToInt.js +99 -0
  13. package/config/.env.template +48 -0
  14. package/docker/Dockerfile +39 -0
  15. package/docker/README.md +5 -0
  16. package/docker/docker-compose.yaml +49 -0
  17. package/docker/docker.sh +43 -0
  18. package/docker/docker_aiiinotate_import.sh +62 -0
  19. package/docs/api.md +381 -0
  20. package/docs/cli.md +132 -0
  21. package/docs/dev_documentation/dev_architecture.md +88 -0
  22. package/docs/dev_documentation/dev_db.md +58 -0
  23. package/docs/dev_documentation/dev_iiif_compatibility.md +43 -0
  24. package/docs/dev_documentation/dev_notes_quirks_and_troubleshooting.md +143 -0
  25. package/docs/docker.md +98 -0
  26. package/docs/includes/report_benchmark_aiiinotate_2026-05-28-02:50:48_7steps.png +0 -0
  27. package/docs/scalability.md +34 -0
  28. package/docs/specifications/0_w3c_open_annotations.md +332 -0
  29. package/docs/specifications/1_w3c_web_annotations.md +577 -0
  30. package/docs/specifications/2_iiif_apis.md +428 -0
  31. package/docs/specifications/3_iiif_annotations.md +103 -0
  32. package/docs/specifications/4_search_api.md +135 -0
  33. package/docs/specifications/5_sas.md +119 -0
  34. package/docs/specifications/6_mirador.md +119 -0
  35. package/docs/specifications/7_aikon.md +137 -0
  36. package/docs/specifications/include/presentation_2.0.webp +0 -0
  37. package/docs/specifications/include/presentation_2.0_white.png +0 -0
  38. package/docs/specifications/include/presentation_3.0.png +0 -0
  39. package/docs/specifications/include/presentation_3.0_resize.png +0 -0
  40. package/eslint.config.js +30 -0
  41. package/migrations/baseConfig.js +57 -0
  42. package/migrations/manageIndex.js +55 -0
  43. package/migrations/migrate-mongo-config-main.js +8 -0
  44. package/migrations/migrate-mongo-config-test.js +8 -0
  45. package/migrations/migrationScripts/20250825185706-collections.js +48 -0
  46. package/migrations/migrationScripts/20250826194832-annotations2-schema.js +42 -0
  47. package/migrations/migrationScripts/20250904080710-annotations2-indexes.js +69 -0
  48. package/migrations/migrationScripts/20251002141951-manifests2-schema.js +43 -0
  49. package/migrations/migrationScripts/20251006212110-manifests2-indexes.js +35 -0
  50. package/migrations/migrationTemplate.js +25 -0
  51. package/package.json +82 -0
  52. package/scripts/get_version.py +12 -0
  53. package/scripts/run.sh +36 -0
  54. package/scripts/setup_mongodb.sh +70 -0
  55. package/scripts/setup_node.sh +15 -0
  56. package/scripts/update_version.py +30 -0
  57. package/scripts/utils.sh +65 -0
  58. package/src/app.js +116 -0
  59. package/src/constants.js +73 -0
  60. package/src/data/annotations/annotations2.js +681 -0
  61. package/src/data/annotations/annotations3.js +28 -0
  62. package/src/data/annotations/routes.js +335 -0
  63. package/src/data/annotations/routes.test.js +271 -0
  64. package/src/data/collectionAbstract.js +283 -0
  65. package/src/data/index.js +29 -0
  66. package/src/data/manifests/manifests2.js +378 -0
  67. package/src/data/manifests/manifests2.test.js +53 -0
  68. package/src/data/manifests/manifests3.js +23 -0
  69. package/src/data/manifests/routes.js +122 -0
  70. package/src/data/manifests/routes.test.js +70 -0
  71. package/src/data/routes.js +181 -0
  72. package/src/data/routes.test.js +166 -0
  73. package/src/db/index.js +50 -0
  74. package/src/fixtures/annotations.js +41 -0
  75. package/src/fixtures/data/annotationList_aikon_wit9_man11_anno165_all.jsonld +827 -0
  76. package/src/fixtures/data/annotationList_vhs_wit250_man250_anno250_all.jsonld +37514 -0
  77. package/src/fixtures/data/annotationList_vhs_wit253_man253_anno253_all.jsonld +20111 -0
  78. package/src/fixtures/data/annotations2Invalid.jsonld +39 -0
  79. package/src/fixtures/data/annotations2SvgValid.jsonld +81 -0
  80. package/src/fixtures/data/annotations2Valid.jsonld +39 -0
  81. package/src/fixtures/data/bnf_invalid_manifest.json +2806 -0
  82. package/src/fixtures/data/bnf_valid_manifest.json +2817 -0
  83. package/src/fixtures/data/vhs_wit253_man253_anno253_anno-24.json +1 -0
  84. package/src/fixtures/generate.js +181 -0
  85. package/src/fixtures/index.js +69 -0
  86. package/src/fixtures/manifests.js +14 -0
  87. package/src/fixtures/utils.js +37 -0
  88. package/src/schemas/index.js +100 -0
  89. package/src/schemas/schemasBase.js +19 -0
  90. package/src/schemas/schemasPresentation2.js +410 -0
  91. package/src/schemas/schemasPresentation3.js +33 -0
  92. package/src/schemas/schemasResolver.js +71 -0
  93. package/src/schemas/schemasRoutes.js +318 -0
  94. package/src/server.js +25 -0
  95. package/src/types.js +97 -0
  96. package/src/utils/iiif2Utils.js +332 -0
  97. package/src/utils/iiif2Utils.test.js +146 -0
  98. package/src/utils/iiif3Utils.js +0 -0
  99. package/src/utils/iiifUtils.js +18 -0
  100. package/src/utils/logger.js +119 -0
  101. package/src/utils/routeUtils.js +137 -0
  102. package/src/utils/svg.js +417 -0
  103. package/src/utils/testUtils.js +289 -0
  104. package/src/utils/utils.js +403 -0
@@ -0,0 +1 @@
1
+ {"@type": "sc:AnnotationList", "resources": [{"@id": "http://iscd.huma-num.fr/sas/annotation/wit253_man253_anno253_c24_b2204564e1d74023bc38cbd2906e137b", "@type": "oa:Annotation", "dcterms:created": "2025-09-09T11:42:43", "dcterms:modified": "2025-09-09T11:42:43", "resource": [{"@type": "dctypes:Text", "https://iscd.huma-num.fr/sas/full_text": "", "format": "text/html", "chars": "<p></p>"}], "on": [{"@type": "oa:SpecificResource", "within": {"@id": "https://iscd.huma-num.fr/vhs/iiif/v2/wit253_man253_anno253/manifest.json", "@type": "sc:Manifest"}, "selector": {"@type": "oa:Choice", "default": {"@type": "oa:FragmentSelector", "value": "xywh=212,381,1021,1610"}, "item": {"@type": "oa:SvgSelector", "value": "<svg xmlns=\"http://www.w3.org/2000/svg\"><path xmlns='http://www.w3.org/2000/svg' d='M212 381 h 510 v 0 h 510 v 805 v 805 h -510 h -510 v -805Z' id='rectangle_wit253_man253_anno253_c24_b2204564e1d74023bc38cbd2906e137b' data-paper-data='{&quot;strokeWidth&quot;:1,&quot;rotation&quot;:0,&quot;annotation&quot;:null,&quot;nonHoverStrokeColor&quot;:[&quot;Color&quot;,0,1,0],&quot;editable&quot;:true,&quot;deleteIcon&quot;:null,&quot;rotationIcon&quot;:null,&quot;group&quot;:null}' fill-opacity='0' fill='#00ff00' fill-rule='nonzero' stroke='#00ff00' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter' stroke-miterlimit='10' stroke-dashoffset='0' style='mix-blend-mode: normal'/></svg>"}}, "full": "https://iscd.huma-num.fr/vhs/iiif/v2/wit253_man253_anno253/canvas/c24.json"}], "motivation": ["oa:commenting", "oa:tagging"], "@context": "http://iiif.io/api/presentation/2/context.json"}]}
@@ -0,0 +1,181 @@
1
+ import { v4 as uuid4 } from "uuid";
2
+
3
+ import { getRandomItem } from "#utils/utils.js";
4
+ import { PUBLIC_URL } from "#constants";
5
+
6
+ /**
7
+ * generate an array of length `length` filled with values returned by `itemFunc`
8
+ * @param {number} length
9
+ * @param {Function} itemFunc
10
+ * @returns {Array}
11
+ */
12
+ const fillArray = (length, itemFunc) =>
13
+ Array.from({ length: length }, itemFunc)
14
+
15
+ /** @returns {string} - matches "int,int,int.int" */
16
+ const makeXywh = () =>
17
+ fillArray(4, () => Math.floor(Math.random() * 1000))
18
+ .sort()
19
+ .join(",");
20
+
21
+ const makeRandomNumber = (maxNum=1000) => Math.floor(Math.random() * maxNum);
22
+
23
+ const makeManifestShortId = () =>
24
+ `wit${makeRandomNumber()}_pdf${makeRandomNumber()}_anno${makeRandomNumber()}`;
25
+
26
+ const makeBaseUrl = (manifestShortId) =>
27
+ `${PUBLIC_URL}/data/2/${manifestShortId}`;
28
+
29
+ const makeAnnotationId = (manifestShortId) =>
30
+ `${makeBaseUrl(manifestShortId)}/annotation/a_${uuid4()}`;
31
+
32
+ const makeManifestId = (manifestShortId) =>
33
+ `${makeBaseUrl(manifestShortId)}/manifest.json`;
34
+
35
+ const makeCanvasId = (manifestShortId) =>
36
+ `${makeBaseUrl(manifestShortId)}/canvas/c_${uuid4()}`;
37
+
38
+ const makeAnnotationListId = (manifestShortId) =>
39
+ `${makeBaseUrl(manifestShortId)}/list/l_${uuid4()}`;
40
+
41
+ const makeIiif2Manifest = (manifestShortId, canvasArray) => ({
42
+ "@context": "http://iiif.io/api/presentation/2/context.json",
43
+ "@id": makeManifestId(manifestShortId),
44
+ "label": "test aiiinotate manifest",
45
+ "attribution": "",
46
+ "seeAlso": [
47
+ "http://oai.bnf.fr/oai2/OAIHandler?verb=GetRecord&metadataPrefix=oai_dc&identifier=oai:bnf.fr:gallica/ark:/12148/btv1b8490076p"
48
+ ],
49
+ "description": "this is a test IIIF manifest for aiiinotate testing purposes",
50
+ "metadata": [],
51
+ "sequences": [
52
+ {
53
+ "canvases": canvasArray,
54
+ "label": "Current Page Order",
55
+ "@type": "sc:Sequence",
56
+ "@id": `${makeManifestId(manifestShortId)}/sequence/default`
57
+ }
58
+ ],
59
+ "thumbnail": {
60
+ "@id": "https://gallica.bnf.fr/ark:/12148/btv1b8490076p.thumbnail"
61
+ },
62
+ "@type": "sc:Manifest",
63
+ })
64
+
65
+ const makeIiif2Canvas = (canvasId) => ({
66
+ "@context": "http://iiif.io/api/presentation/2/context.json",
67
+ "@id": canvasId,
68
+ "label": "plat supérieur",
69
+ "height": 6044,
70
+ "width": 4768,
71
+ "images": [
72
+ {
73
+ "motivation": "sc:painting",
74
+ "on": canvasId,
75
+ "resource": {
76
+ "format": "image/jpeg",
77
+ "service": {
78
+ "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level2",
79
+ "@context": "http://iiif.io/api/image/1/context.json",
80
+ "@id": `${canvasId}_img.png`
81
+ },
82
+ "height": 6044,
83
+ "width": 4768,
84
+ "@id": `${canvasId}_img.png/full/full/0/native.jpg`,
85
+ "@type": "dctypes:Image"
86
+ },
87
+ "@type": "oa:Annotation"
88
+ }
89
+ ],
90
+ "@type": "sc:Canvas"
91
+ })
92
+
93
+ const makeIiif2AnnotationList = (manifestShortId, annotationArray) => ({
94
+ "@context": "http://iiif.io/api/presentation/2/context.json",
95
+ "@type": "sc:AnnotationList",
96
+ "@id": makeAnnotationListId(manifestShortId),
97
+ "resources": annotationArray
98
+ })
99
+
100
+ const makeIiif2Annotation = (annotationId, canvasId) => ({
101
+ "@context": "http://iiif.io/api/presentation/2/context.json",
102
+ "@id": annotationId,
103
+ "@type": "oa:Annotation",
104
+ "resource": {
105
+ "@type": "dctypes:Text",
106
+ "format": "text/html",
107
+ "chars": ""
108
+ },
109
+ "on": `${canvasId}#xywh=${makeXywh()}`,
110
+ "motivation": [
111
+ "oa:tagging",
112
+ "oa:commenting"
113
+ ],
114
+ "label": ""
115
+ })
116
+
117
+ /**
118
+ * @param {string} manifestShortId
119
+ * @param {string[]} canvasIdArray
120
+ * @returns {object}
121
+ */
122
+ const generateIiif2AnnotationList = (manifestShortId, canvasIdArray) =>
123
+ makeIiif2AnnotationList(
124
+ canvasIdArray.map((canvasId) =>
125
+ makeIiif2Annotation(
126
+ makeAnnotationId(manifestShortId),
127
+ canvasId
128
+ )
129
+ )
130
+ );
131
+
132
+
133
+ /**
134
+ * @param {number} nCanvas - integer, number of canvases in the manifest
135
+ * @returns {object}
136
+ */
137
+ const generateIiif2Manifest = (nCanvas) => {
138
+ const manifestShortId = makeManifestShortId();
139
+ return makeIiif2Manifest(
140
+ manifestShortId,
141
+ fillArray(nCanvas, () => makeCanvasId(manifestShortId))
142
+ .map((canvasId) => makeIiif2Canvas(canvasId))
143
+ )
144
+ }
145
+
146
+ /**
147
+ * return an array of [manifest, annotationList].
148
+ * manifest has `nCanvas` canvases, `annotationList` has `nAnnotations` annotation on the canvases of the generated manifest.
149
+ * @param {number} nCanvas
150
+ * @param {number} nAnnotations
151
+ * @returns
152
+ */
153
+ const generateIiif2ManifestAndAnnotationsList = (nCanvas, nAnnotations) => {
154
+ const
155
+ manifestShortId = makeManifestShortId(),
156
+ manifest = makeIiif2Manifest(
157
+ manifestShortId,
158
+ fillArray(nCanvas, () => makeCanvasId(manifestShortId))
159
+ .map((canvasId) => makeIiif2Canvas(canvasId))
160
+ ),
161
+ canvasArray = fillArray(
162
+ nAnnotations,
163
+ () => getRandomItem(manifest.sequences[0].canvases)
164
+ ).map((canvas) =>
165
+ makeIiif2Annotation(
166
+ makeAnnotationId(manifestShortId),
167
+ canvas["@id"]
168
+ )
169
+ ),
170
+ annotationList = makeIiif2AnnotationList(
171
+ manifestShortId, canvasArray
172
+ );
173
+
174
+ return [ manifest, annotationList ];
175
+ }
176
+
177
+ export {
178
+ generateIiif2Manifest,
179
+ generateIiif2AnnotationList,
180
+ generateIiif2ManifestAndAnnotationsList
181
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * the `fixtures` plugin makes test files available to the entire fastify app, mostly for testing purposes.
3
+ */
4
+ import fastifyPlugin from "fastify-plugin";
5
+
6
+ import { annotations2Invalid, annotations2Valid, annotations2SvgValid, annotationListUri, annotationListUriArray, annotationList, annotationListArray, annotationListUriInvalid, annotationListUriArrayInvalid } from "#src/fixtures/annotations.js";
7
+ import { manifest2Valid, manifest2ValidUri, manifest2Invalid, manifest2InvalidUri } from "#fixtures/manifests.js";
8
+ import { generateIiif2Manifest, generateIiif2AnnotationList, generateIiif2ManifestAndAnnotationsList } from "#fixtures/generate.js";
9
+ import { readFileToObject } from "#fixtures/utils.js";
10
+
11
+ /** @typedef {import("#types").FastifyInstanceType} FastifyInstanceType */
12
+
13
+
14
+ /**
15
+ * NOTE: `done` musn't be used with async plugins. it raises an error `FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER`
16
+ * @param {FastifyInstanceType} fastify Encapsulated Fastify Instance
17
+ * @param {object} options
18
+ */
19
+ async function fixtures(fastify, options) {
20
+
21
+ /** route to return a file in `dataDir` */
22
+ fastify.get(
23
+ "/fixtures/:fileName",
24
+ {
25
+ schema: {
26
+ params: {
27
+ type: "object",
28
+ properties: { fileName: { type: "string" } }
29
+ }
30
+ },
31
+ response: {
32
+ 200: {
33
+ type: "string"
34
+ },
35
+ 500: {
36
+ type: "object",
37
+ properties: {
38
+ error: { type: "string" }
39
+ }
40
+ }
41
+ }
42
+ },
43
+ async (request, reply) => {
44
+ const { fileName } = request.params;
45
+ return readFileToObject(fileName);
46
+ }
47
+ )
48
+
49
+ fastify.decorate("fixtures", {
50
+ annotations2SvgValid: annotations2SvgValid,
51
+ annotationListUri: annotationListUri,
52
+ annotationListUriArray: annotationListUriArray,
53
+ annotationList: annotationList,
54
+ annotationListArray: annotationListArray,
55
+ annotationListUriArrayInvalid: annotationListUriArrayInvalid,
56
+ annotationListUriInvalid: annotationListUriInvalid,
57
+ annotations2Invalid: annotations2Invalid,
58
+ annotations2Valid: annotations2Valid,
59
+ manifest2Valid: manifest2Valid,
60
+ manifest2ValidUri: manifest2ValidUri,
61
+ manifest2Invalid: manifest2Invalid,
62
+ manifest2InvalidUri: manifest2InvalidUri,
63
+ generateIiif2Manifest: generateIiif2Manifest,
64
+ generateIiif2AnnotationList: generateIiif2AnnotationList,
65
+ generateIiif2ManifestAndAnnotationsList: generateIiif2ManifestAndAnnotationsList
66
+ });
67
+ }
68
+
69
+ export default fastifyPlugin(fixtures);
@@ -0,0 +1,14 @@
1
+ import { readFileToObject, toUrl } from "#fixtures/utils.js";
2
+
3
+ const manifest2ValidUri = { uri: toUrl("bnf_valid_manifest.json") };
4
+ const manifest2Valid = readFileToObject("bnf_valid_manifest.json");
5
+ const manifest2InvalidUri = { uri: toUrl("bnf_invalid_manifest.json") };
6
+ const manifest2Invalid = readFileToObject("bnf_invalid_manifest.json");
7
+
8
+
9
+ export {
10
+ manifest2ValidUri,
11
+ manifest2Valid,
12
+ manifest2InvalidUri,
13
+ manifest2Invalid
14
+ }
@@ -0,0 +1,37 @@
1
+ import url from "node:url";
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+
5
+ import { BASE_URL } from "#constants";
6
+
7
+ // path to dirctory of curent file
8
+ const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
9
+ // path to fixtures/data
10
+ const dataDir = path.join(__dirname, "data");
11
+
12
+ const availableFiles = fs.readdirSync(dataDir);
13
+
14
+ /**
15
+ * for simplicity, `readFileToObject` is synchronous. given that `fixtures` should only be used in tests, there is no performance downgrade for the prod server.
16
+ * @param {string} fn: the filename
17
+ * @returns {object}
18
+ */
19
+ const readFileToObject = (fn) => {
20
+ if (!availableFiles.includes(fn)) {
21
+ throw new Error(`file not found: ${fn}`);
22
+ }
23
+ return JSON.parse(fs.readFileSync(path.join(dataDir, fn), { encoding: "utf8" }));
24
+ }
25
+
26
+ /**
27
+ * @param {string} fn
28
+ * @returns {string}
29
+ */
30
+ const toUrl = (fn) => `${BASE_URL}/fixtures/${fn}`;
31
+
32
+
33
+ export {
34
+ dataDir,
35
+ readFileToObject,
36
+ toUrl
37
+ }
@@ -0,0 +1,100 @@
1
+ import fastifyPlugin from "fastify-plugin";
2
+
3
+ import addSchemasBase from "#schemas/schemasBase.js";
4
+ import addSchemasPresentation3 from "#schemas/schemasPresentation3.js";
5
+ import addSchemasPresentation2 from "#src/schemas/schemasPresentation2.js";
6
+ import addSchemasRoutes from "#schemas/schemasRoutes.js";
7
+ import schemasResolver from "#schemas/schemasResolver.js";
8
+ import { PUBLIC_URL } from "#constants";
9
+
10
+ /** @typedef {import("#types").FastifyInstanceType} FastifyInstanceType */
11
+ /** @typedef {import("#types").FastifySchemaType} FastifySchemaType */
12
+
13
+ /**
14
+ * NOTE : how are schemas managed in aiiinotate ?
15
+ * we implement "namespaced schemas" (aka, schemas grouped together in a single fastify decorator).
16
+ *
17
+ * TLDR: schemas are added to the global fastify instance, but their IDs are namespaced;
18
+ * schemas they are then accessed through decorators, each decorator is used to resolve annotations
19
+ * within a namespace.
20
+ *
21
+ * @example
22
+ * > fastify.schemasPresentation2.getSchema("annotations") =>
23
+ * > // resolves to
24
+ * > fastify.getSchema("$PUBLIC_URL/schemas/schemasPresentation2/annotation")
25
+ *
26
+ * IN MORE DETAIL:
27
+ * - namespacing is done through the IDs of the schemas, defined on `fastify.addSchema` and used
28
+ * to retrieve a schema with `fastify.getSchema()`
29
+ * - the global schema ID anatomy is defined here, in `_makeSchemaUri`: it's an URI that
30
+ * accepts a "namespace". each schema has a unique `slug` appended to the end of the URI.
31
+ * - each file in this module (appart from `schemasResolver` and this file) exports
32
+ * an `addSchemas` function that adds schemas to the global fastify instance with
33
+ * a specific namespace.
34
+ * - then, a decorator is added to the fastify instance for each namespace. it adds 2 functions
35
+ * `getSchema` and `makeSchemaUri` that are used to resolve schemas names and to access
36
+ * all schemas within a certain namespace.
37
+ * - thanks to that, schemas can be accessed by their unique slug with the pattern:
38
+ * `fastify.$schemaNamespace.getSchema($schemaSlug)`.
39
+ * - schemas defined here are accessible in the entire fastify app.
40
+ */
41
+
42
+ /**
43
+ * @type {(namespace: string) => (slug: string) => string}
44
+ */
45
+ const _makeSchemaUri = (namespace) =>
46
+ (slug) => `${PUBLIC_URL}/schemas/${namespace}/${slug}`;
47
+
48
+ /**
49
+ * @type {(fastify: FastifyInstanceType, namespace: string) => (slug: string) => FastifySchemaType}
50
+ */
51
+ const _getSchema = (fastify, namespace) =>
52
+ (slug) => fastify.getSchema(_makeSchemaUri(namespace)(slug));
53
+
54
+ /**
55
+ * decorate fastify with namespaced schemas
56
+ * @param {FastifyInstanceType} fastify
57
+ * @param {{ namespace: string, addSchemasFunc: Function }} options
58
+ */
59
+ const addSchemasDecorator = (fastify, options) => {
60
+ if (!(options.namespace || "").length) {
61
+ throw new Error(`addSchemasDecorator requires options.namespace to be set ! Got '${options.namespace}'`);
62
+ }
63
+ if (!options.addSchemasFunc || typeof options.addSchemasFunc !== "function") {
64
+ throw new Error(`addSchemasDecorator requires options.addSchemasFunc to be a function ! Got '${options.addSchemasFunc}'`)
65
+ }
66
+
67
+ const
68
+ { namespace, addSchemasFunc } = options,
69
+ makeSchemaUri = _makeSchemaUri(namespace),
70
+ getSchema = _getSchema(fastify, namespace);
71
+
72
+ fastify = addSchemasFunc(fastify, makeSchemaUri);
73
+ fastify.decorate(namespace, {
74
+ makeSchemaUri: makeSchemaUri,
75
+ getSchema: getSchema
76
+ })
77
+ }
78
+
79
+ function schemas(fastify, options, done) {
80
+
81
+ const schemasData = [
82
+ [ "schemasPresentation2", addSchemasPresentation2 ],
83
+ [ "schemasPresentation3", addSchemasPresentation3 ],
84
+ [ "schemasBase", addSchemasBase ],
85
+ [ "schemasRoutes", addSchemasRoutes ],
86
+ ]
87
+
88
+ fastify.register(schemasResolver);
89
+
90
+ for (let [ schemasNamespace, addSchemasFunc ] of schemasData) {
91
+ addSchemasDecorator(fastify, {
92
+ namespace: schemasNamespace,
93
+ addSchemasFunc: addSchemasFunc,
94
+ })
95
+ }
96
+
97
+ done()
98
+ }
99
+
100
+ export default fastifyPlugin(schemas);
@@ -0,0 +1,19 @@
1
+ /** @typedef {import("#types").FastifyInstanceType} FastifyInstanceType */
2
+
3
+ function addSchemas(fastify, makeSchemaUri) {
4
+ fastify.addSchema({
5
+ $id: makeSchemaUri("presentation"),
6
+ type: "integer",
7
+ enum: [ 2, 3 ],
8
+ description: "IIIF presentation API versions"
9
+ });
10
+ fastify.addSchema({
11
+ $id: makeSchemaUri("search"),
12
+ type: "integer",
13
+ enum: [ 1, 2 ],
14
+ description: "IIIF search API versions"
15
+ });
16
+ return fastify
17
+ }
18
+
19
+ export default addSchemas;