You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@annotator.apache.org by ge...@apache.org on 2022/11/23 21:38:27 UTC

[incubator-annotator] 01/01: Create new package @apache-annotator/annotation

This is an automated email from the ASF dual-hosted git repository.

gerben pushed a commit to branch annotator-package
in repository https://gitbox.apache.org/repos/asf/incubator-annotator.git

commit 0ebebdb3b51b6730c7253178b358d97925663298
Author: Gerben <ge...@treora.com>
AuthorDate: Wed Nov 23 22:00:40 2022 +0100

    Create new package @apache-annotator/annotation
    
    Types & utility functions for annotations as a whole.
    
    With code imported from <https://code.treora.com/gerben/web-annotation-utils>.
---
 .../{apache-annotator => annotation}/package.json  |  13 +-
 packages/annotation/src/index.ts                   |  25 +++
 packages/annotation/src/multiplicity.ts            |  48 +++++
 packages/annotation/src/web-annotation.ts          | 227 +++++++++++++++++++++
 .../annotation/test/model/multiplicity.test.ts     |  39 ++++
 packages/{selector => annotation}/tsconfig.json    |   0
 packages/apache-annotator/package.json             |   1 +
 packages/apache-annotator/src/annotation.ts        |  37 ++++
 packages/apache-annotator/tsconfig.json            |   1 +
 packages/selector/src/types.ts                     |  21 +-
 packages/selector/tsconfig.json                    |   5 +-
 tsconfig.json                                      |   1 +
 tsconfig.test.json                                 |   1 +
 13 files changed, 391 insertions(+), 28 deletions(-)

diff --git a/packages/apache-annotator/package.json b/packages/annotation/package.json
similarity index 58%
copy from packages/apache-annotator/package.json
copy to packages/annotation/package.json
index ebec725..a663402 100644
--- a/packages/apache-annotator/package.json
+++ b/packages/annotation/package.json
@@ -1,22 +1,19 @@
 {
-  "name": "apache-annotator",
+  "name": "@apache-annotator/annotation",
   "version": "0.3.0",
-  "description": "Apache Annotator provides annotation enabling code for browsers, servers, and humans.",
+  "description": "Web Annotation types and utilities.",
   "homepage": "https://annotator.apache.org",
   "repository": {
     "type": "git",
     "url": "https://github.com/apache/incubator-annotator.git",
-    "directory": "packages/apache-annotator"
+    "directory": "packages/annotation"
   },
   "license": "Apache-2.0",
   "author": "Apache Software Foundation",
   "type": "module",
-  "exports": {
-    "./*": "./lib/*.js"
-  },
+  "exports": "./lib/index.js",
+  "main": "./lib/index.js",
   "dependencies": {
-    "@apache-annotator/dom": "^0.3.0",
-    "@apache-annotator/selector": "^0.3.0",
     "@babel/runtime-corejs3": "^7.13.10"
   },
   "engines": {
diff --git a/packages/annotation/src/index.ts b/packages/annotation/src/index.ts
new file mode 100644
index 0000000..1e5f467
--- /dev/null
+++ b/packages/annotation/src/index.ts
@@ -0,0 +1,25 @@
+/**
+ * @license
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-FileCopyrightText: The Apache Software Foundation
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './web-annotation.js';
+export * from './multiplicity.js';
diff --git a/packages/annotation/src/multiplicity.ts b/packages/annotation/src/multiplicity.ts
new file mode 100644
index 0000000..93a74d4
--- /dev/null
+++ b/packages/annotation/src/multiplicity.ts
@@ -0,0 +1,48 @@
+/**
+ * @license
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-FileCopyrightText: The Apache Software Foundation
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export type OneOrMore<T> = T | T[];
+export type ZeroOrMore<T> = undefined | null | T | T[];
+
+export type OneOrMoreIncluding<Other extends any, RequiredValue extends any> =
+  | RequiredValue
+  | [RequiredValue, ...Other[]]
+  | [...Other[], RequiredValue];
+// | [Other, ...OneOrMoreIncluding<Other, RequiredValue>]; // FIXME TypeScript complains about the circular reference..
+
+/**
+ * OnlyOne<T> extracts the T from a One/ZeroOrMore<T> type
+ */
+export type OnlyOne<T> = T extends (infer X)[] ? X : T;
+
+export function asArray<T>(value: ZeroOrMore<T>): T[] {
+  if (Array.isArray(value)) return value;
+  if (value === undefined || value === null) return [];
+  return [value];
+}
+
+export function asSingleValue<T>(value: ZeroOrMore<T>): T | undefined {
+  if (value instanceof Array) return value[0];
+  if (value === undefined || value === null) return undefined;
+  return value;
+}
diff --git a/packages/annotation/src/web-annotation.ts b/packages/annotation/src/web-annotation.ts
new file mode 100644
index 0000000..013abef
--- /dev/null
+++ b/packages/annotation/src/web-annotation.ts
@@ -0,0 +1,227 @@
+/**
+ * @license
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-FileCopyrightText: The Apache Software Foundation
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import type {
+  OneOrMore,
+  OneOrMoreIncluding,
+  ZeroOrMore,
+} from './multiplicity.js';
+
+/**
+ * A Web Annotation object.
+ *
+ * This is an interpretation of the Web Annotation Data Model:
+ * <https://www.w3.org/TR/2017/REC-annotation-model-20170223/>
+ *
+ * TODO Deal more systemically with ‘relations’, i.e. values that could be
+ * either a nested object or a URI referring to such an object.
+ */
+export interface WebAnnotation {
+  '@context': OneOrMoreIncluding<string, 'http://www.w3.org/ns/anno.jsonld'>;
+  type: OneOrMoreIncluding<string, 'Annotation'>;
+  id: string;
+  target: OneOrMore<Target>;
+  creator?: ZeroOrMore<Agent>;
+  created?: UtcDateTime;
+  generator?: ZeroOrMore<Agent>;
+  generated?: UtcDateTime;
+  modified?: UtcDateTime;
+  motivation?: ZeroOrMore<Motivation>;
+  audience?: ZeroOrMore<Audience>;
+  rights?: ZeroOrMore<string>;
+  canonical?: string;
+  via?: ZeroOrMore<string>;
+  body?: BodyChoice | OneOrMore<Body>;
+  bodyValue?: string;
+}
+
+/**
+ * A slightly stricter type for WebAnnotation, not allowing both a body and bodyValue.
+ */
+export type WebAnnotationStrict = WebAnnotation & (WithBody | WithBodyValue | WithoutBody);
+
+interface WithBody {
+  body: BodyChoice | OneOrMore<Body>;
+  bodyValue?: undefined;
+}
+
+interface WithBodyValue {
+  body?: undefined;
+  bodyValue: string;
+}
+
+interface WithoutBody {
+  body?: undefined;
+  bodyValue?: undefined;
+}
+
+export type Body = string | BodyObject;
+
+export type BodyObject = {
+  creator?: ZeroOrMore<Agent>;
+  created?: UtcDateTime;
+  modified?: UtcDateTime;
+  purpose?: ZeroOrMore<Motivation>;
+} & (TextualBody | SpecificResource | ExternalResource);
+
+export type Target = string | SpecificResource | ExternalResource;
+
+export type Agent =
+  | string
+  | {
+      id?: string;
+      type?: ZeroOrMore<'Person' | 'Organization' | 'Software'>;
+      name?: ZeroOrMore<string>;
+      nickname?: ZeroOrMore<string>;
+      email?: ZeroOrMore<string>;
+      email_sha1?: ZeroOrMore<string>;
+      homepage?: ZeroOrMore<string>;
+    };
+
+export type Audience =
+  | string
+  | {
+      id?: string;
+      type?: string;
+    };
+
+export interface BodyChoice {
+  type: 'Choice';
+  items: Body[];
+}
+
+export interface TextualBody extends Omit<ExternalResource, 'id' | 'type'> {
+  id?: string;
+  type: 'TextualBody';
+  value: string;
+}
+
+export interface SpecificResource {
+  id?: string;
+  type?: 'SpecificResource';
+  source: string;
+  selector?: string | OneOrMore<Selector>;
+  accessibility?: AccessibilityFeatures;
+  rights?: ZeroOrMore<string>;
+  canonical?: string;
+  via?: ZeroOrMore<string>;
+}
+
+/**
+ * A {@link https://www.w3.org/TR/2017/REC-annotation-model-20170223/#selectors
+ * | Selector} object of the Web Annotation Data Model.
+ *
+ * Corresponds to RDF class {@link http://www.w3.org/ns/oa#Selector}
+ *
+ * @public
+ */
+export interface Selector {
+  type?: string;
+
+  /**
+   * A Selector can be refined by another Selector.
+   *
+   * See {@link https://www.w3.org/TR/2017/REC-annotation-model-20170223/#refinement-of-selection
+   * | §4.2.9 Refinement of Selection} in the Web Annotation Data Model.
+   *
+   * Corresponds to RDF property {@link http://www.w3.org/ns/oa#refinedBy}
+   */
+  refinedBy?: Selector;
+}
+
+export interface ExternalResource {
+  id: string;
+  // XXX type’s value SHOULD be one of these, “but MAY come from other vocabularies”.
+  type?: ZeroOrMore<'Dataset' | 'Image' | 'Video' | 'Sound' | 'Text'>;
+  format?: ZeroOrMore<string>;
+  language?: ZeroOrMore<string>;
+  processingLanguage?: string;
+  textDirection?: 'ltr' | 'rtl' | 'auto';
+  accessibility?: AccessibilityFeatures;
+  rights?: ZeroOrMore<string>;
+  canonical?: string;
+  via?: ZeroOrMore<string>;
+}
+
+export type Motivation =
+  | 'assessing'
+  | 'bookmarking'
+  | 'classifying'
+  | 'commenting'
+  | 'describing'
+  | 'editing'
+  | 'highlighting'
+  | 'identifying'
+  | 'linking'
+  | 'moderating'
+  | 'questioning'
+  | 'replying'
+  | 'tagging';
+
+// “The datetime MUST be a xsd:dateTime with the UTC timezone expressed as "Z".”
+type UtcDateTime = `${string}Z`;
+
+// To help usage, narrow the type of Date.toISOString(); it is guaranteed to end with a 'Z'.
+declare global {
+  interface Date {
+    toISOString(): UtcDateTime;
+  }
+}
+
+// From <https://www.w3.org/2021/a11y-discov-vocab/latest/CG-FINAL-a11y-discov-vocab-20220610.html>
+export type AccessibilityFeatures =
+  | ZeroOrMore<AccessibilityFeature>
+  | 'none'
+  | ['none'];
+export type AccessibilityFeature =
+  | 'annotations'
+  | 'ARIA'
+  | 'bookmarks'
+  | 'index'
+  | 'printPageNumbers'
+  | 'readingOrder'
+  | 'structuralNavigation'
+  | 'tableOfContents'
+  | 'taggedPDF'
+  | 'alternativeText'
+  | 'audioDescription'
+  | 'captions'
+  | 'describedMath'
+  | 'longDescription'
+  | 'rubyAnnotations'
+  | 'signLanguage'
+  | 'transcript'
+  | 'displayTransformability'
+  | 'synchronizedAudioText'
+  | 'timingControl'
+  | 'unlocked'
+  | 'ChemML'
+  | 'latex'
+  | 'MathML'
+  | 'ttsMarkup'
+  | 'highContrastAudio'
+  | 'highContrastDisplay'
+  | 'largePrint'
+  | 'braille'
+  | 'tactileGraphic'
+  | 'tactileObject';
diff --git a/packages/annotation/test/model/multiplicity.test.ts b/packages/annotation/test/model/multiplicity.test.ts
new file mode 100644
index 0000000..6e18e10
--- /dev/null
+++ b/packages/annotation/test/model/multiplicity.test.ts
@@ -0,0 +1,39 @@
+/**
+ * @license
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-FileCopyrightText: The Apache Software Foundation
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { strict as assert } from 'assert';
+import { asArray, asSingleValue } from '../../src/multiplicity';
+import type { OneOrMore, OnlyOne, ZeroOrMore } from '../../src/multiplicity';
+
+describe('asArray', () => {
+  it('wraps a single value', () => {
+    const input: OneOrMore<string> = 'blub';
+    const output = asArray(input);
+    assert.strictEqual(output, ['blub']);
+  });
+  it('leaves an array untouched', () => {
+    const input: OneOrMore<string> = ['blub'];
+    const output = asArray(input);
+    assert.strictEqual(output, input);
+  });
+});
diff --git a/packages/selector/tsconfig.json b/packages/annotation/tsconfig.json
similarity index 100%
copy from packages/selector/tsconfig.json
copy to packages/annotation/tsconfig.json
diff --git a/packages/apache-annotator/package.json b/packages/apache-annotator/package.json
index ebec725..597dd7e 100644
--- a/packages/apache-annotator/package.json
+++ b/packages/apache-annotator/package.json
@@ -15,6 +15,7 @@
     "./*": "./lib/*.js"
   },
   "dependencies": {
+    "@apache-annotator/annotation": "^0.3.0",
     "@apache-annotator/dom": "^0.3.0",
     "@apache-annotator/selector": "^0.3.0",
     "@babel/runtime-corejs3": "^7.13.10"
diff --git a/packages/apache-annotator/src/annotation.ts b/packages/apache-annotator/src/annotation.ts
new file mode 100644
index 0000000..98bc670
--- /dev/null
+++ b/packages/apache-annotator/src/annotation.ts
@@ -0,0 +1,37 @@
+/**
+ * @license
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-FileCopyrightText: The Apache Software Foundation
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * This module provides types and utilities for the {@link https://www.w3.org/TR/2017/REC-annotation-model-20170223/
+ * | Web Annotation Data Model}.
+ *
+ * Besides a type definition, it provides convenience functions for dealing with
+ * Web Annotations, such as getting the URL(s) of pages an annotation targets,
+ * or the plain text content the annotation body. It aims to provide some basic
+ * tools to get started writing interoperable annotation tools without having to
+ * deal with the intricacies of the data model.
+ *
+ * @module
+ */
+
+export * from '@apache-annotator/annotation';
diff --git a/packages/apache-annotator/tsconfig.json b/packages/apache-annotator/tsconfig.json
index 0ac1cf1..cabb91b 100644
--- a/packages/apache-annotator/tsconfig.json
+++ b/packages/apache-annotator/tsconfig.json
@@ -6,6 +6,7 @@
     "rootDir": "src"
   },
   "references": [
+    { "path": "../annotation" },
     { "path": "../dom" },
     { "path": "../selector" }
   ]
diff --git a/packages/selector/src/types.ts b/packages/selector/src/types.ts
index d3c227b..afbf3b4 100644
--- a/packages/selector/src/types.ts
+++ b/packages/selector/src/types.ts
@@ -21,25 +21,8 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-/**
- * A {@link https://www.w3.org/TR/2017/REC-annotation-model-20170223/#selectors
- * | Selector} object of the Web Annotation Data Model.
- *
- * Corresponds to RDF class {@link http://www.w3.org/ns/oa#Selector}
- *
- * @public
- */
-export interface Selector {
-  /**
-   * A Selector can be refined by another Selector.
-   *
-   * See {@link https://www.w3.org/TR/2017/REC-annotation-model-20170223/#refinement-of-selection
-   * | §4.2.9 Refinement of Selection} in the Web Annotation Data Model.
-   *
-   * Corresponds to RDF property {@link http://www.w3.org/ns/oa#refinedBy}
-   */
-  refinedBy?: Selector;
-}
+import type { Selector } from '@apache-annotator/annotation';
+export type { Selector };
 
 /**
  * The {@link https://www.w3.org/TR/2017/REC-annotation-model-20170223/#css-selector
diff --git a/packages/selector/tsconfig.json b/packages/selector/tsconfig.json
index 653b0a5..2281984 100644
--- a/packages/selector/tsconfig.json
+++ b/packages/selector/tsconfig.json
@@ -4,5 +4,8 @@
   "compilerOptions": {
     "outDir": "lib",
     "rootDir": "src"
-  }
+  },
+  "references": [
+    { "path": "../annotation" }
+  ]
 }
diff --git a/tsconfig.json b/tsconfig.json
index b9a2c0d..126dcba 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,6 +2,7 @@
   "files": [],
   "references": [
     { "path": "packages/apache-annotator" },
+    { "path": "packages/annotation" },
     { "path": "packages/dom" },
     { "path": "packages/selector" },
     { "path": "tsconfig.test.json"}
diff --git a/tsconfig.test.json b/tsconfig.test.json
index f439fba..0c2f128 100644
--- a/tsconfig.test.json
+++ b/tsconfig.test.json
@@ -2,6 +2,7 @@
   "extends": "./tsconfig.base.json",
   "include": ["test", "packages/*/test"],
   "references": [
+    { "path": "packages/annotation" },
     { "path": "packages/dom" },
     { "path": "packages/selector" }
   ]