@occultist/occultist 0.0.1

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 (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/dist/accept.d.ts +41 -0
  4. package/dist/accept.js +110 -0
  5. package/dist/accept.test.d.ts +1 -0
  6. package/dist/accept.test.js +44 -0
  7. package/dist/action.test.d.ts +1 -0
  8. package/dist/action.test.js +1 -0
  9. package/dist/actions/actionSets.d.ts +23 -0
  10. package/dist/actions/actionSets.js +49 -0
  11. package/dist/actions/actions.d.ts +163 -0
  12. package/dist/actions/actions.js +436 -0
  13. package/dist/actions/context.d.ts +78 -0
  14. package/dist/actions/context.js +112 -0
  15. package/dist/actions/meta.d.ts +49 -0
  16. package/dist/actions/meta.js +177 -0
  17. package/dist/actions/path.d.ts +21 -0
  18. package/dist/actions/path.js +83 -0
  19. package/dist/actions/path.test.d.ts +1 -0
  20. package/dist/actions/path.test.js +9 -0
  21. package/dist/actions/spec.d.ts +214 -0
  22. package/dist/actions/spec.js +1 -0
  23. package/dist/actions/types.d.ts +112 -0
  24. package/dist/actions/types.js +2 -0
  25. package/dist/actions/writer.d.ts +27 -0
  26. package/dist/actions/writer.js +140 -0
  27. package/dist/actions/writer.test.d.ts +1 -0
  28. package/dist/actions/writer.test.js +42 -0
  29. package/dist/auth/types.d.ts +14 -0
  30. package/dist/auth/types.js +1 -0
  31. package/dist/cache/cache.d.ts +30 -0
  32. package/dist/cache/cache.js +220 -0
  33. package/dist/cache/etag.d.ts +17 -0
  34. package/dist/cache/etag.js +83 -0
  35. package/dist/cache/etag.test.d.ts +1 -0
  36. package/dist/cache/etag.test.js +91 -0
  37. package/dist/cache/memory.d.ts +12 -0
  38. package/dist/cache/memory.js +36 -0
  39. package/dist/cache/types.d.ts +175 -0
  40. package/dist/cache/types.js +4 -0
  41. package/dist/errors.d.ts +11 -0
  42. package/dist/errors.js +54 -0
  43. package/dist/jsonld.d.ts +43 -0
  44. package/dist/jsonld.js +1 -0
  45. package/dist/makeTypeDefs.d.ts +27 -0
  46. package/dist/makeTypeDefs.js +70 -0
  47. package/dist/merge.d.ts +61 -0
  48. package/dist/merge.js +1 -0
  49. package/dist/mod.d.ts +14 -0
  50. package/dist/mod.js +14 -0
  51. package/dist/processAction.d.ts +15 -0
  52. package/dist/processAction.js +512 -0
  53. package/dist/registry.d.ts +88 -0
  54. package/dist/registry.js +314 -0
  55. package/dist/registry.test.d.ts +1 -0
  56. package/dist/registry.test.js +133 -0
  57. package/dist/request.d.ts +29 -0
  58. package/dist/request.js +118 -0
  59. package/dist/scopes.d.ts +35 -0
  60. package/dist/scopes.js +121 -0
  61. package/dist/scopes.test.d.ts +1 -0
  62. package/dist/scopes.test.js +55 -0
  63. package/dist/transformers/fileTransformer.d.ts +1 -0
  64. package/dist/transformers/fileTransformer.js +8 -0
  65. package/dist/types.d.ts +12 -0
  66. package/dist/types.js +1 -0
  67. package/dist/utils/alwaysArray.d.ts +1 -0
  68. package/dist/utils/alwaysArray.js +9 -0
  69. package/dist/utils/contextBuilder.d.ts +9 -0
  70. package/dist/utils/contextBuilder.js +82 -0
  71. package/dist/utils/getActionContext.d.ts +7 -0
  72. package/dist/utils/getActionContext.js +48 -0
  73. package/dist/utils/getInternalName.d.ts +6 -0
  74. package/dist/utils/getInternalName.js +7 -0
  75. package/dist/utils/getParamLocation.d.ts +2 -0
  76. package/dist/utils/getParamLocation.js +6 -0
  77. package/dist/utils/getPropertyValueSpecifications.d.ts +2 -0
  78. package/dist/utils/getPropertyValueSpecifications.js +49 -0
  79. package/dist/utils/getRequestBodyValues.d.ts +11 -0
  80. package/dist/utils/getRequestBodyValues.js +122 -0
  81. package/dist/utils/getRequestIRIValues.d.ts +14 -0
  82. package/dist/utils/getRequestIRIValues.js +133 -0
  83. package/dist/utils/isBodyInit.d.ts +1 -0
  84. package/dist/utils/isBodyInit.js +21 -0
  85. package/dist/utils/isNil.d.ts +1 -0
  86. package/dist/utils/isNil.js +4 -0
  87. package/dist/utils/isObject.d.ts +6 -0
  88. package/dist/utils/isObject.js +6 -0
  89. package/dist/utils/isPopulatedObject.d.ts +5 -0
  90. package/dist/utils/isPopulatedObject.js +8 -0
  91. package/dist/utils/isPopulatedString.d.ts +1 -0
  92. package/dist/utils/isPopulatedString.js +4 -0
  93. package/dist/utils/joinPaths.d.ts +1 -0
  94. package/dist/utils/joinPaths.js +31 -0
  95. package/dist/utils/makeAppendProblemDetails.d.ts +14 -0
  96. package/dist/utils/makeAppendProblemDetails.js +26 -0
  97. package/dist/utils/makeURLPattern.d.ts +5 -0
  98. package/dist/utils/makeURLPattern.js +12 -0
  99. package/dist/utils/normalizeURL.d.ts +4 -0
  100. package/dist/utils/normalizeURL.js +11 -0
  101. package/dist/utils/parseSearchParams.d.ts +3 -0
  102. package/dist/utils/parseSearchParams.js +24 -0
  103. package/dist/utils/preferredMediaTypes.d.ts +42 -0
  104. package/dist/utils/preferredMediaTypes.js +149 -0
  105. package/dist/utils/urlToIRI.d.ts +1 -0
  106. package/dist/utils/urlToIRI.js +8 -0
  107. package/dist/utils/validateSpecValue.d.ts +1 -0
  108. package/dist/utils/validateSpecValue.js +1 -0
  109. package/dist/validators.d.ts +16 -0
  110. package/dist/validators.js +134 -0
  111. package/lib/accept.test.ts +55 -0
  112. package/lib/accept.ts +147 -0
  113. package/lib/action.test.ts +2 -0
  114. package/lib/actions/actionSets.ts +88 -0
  115. package/lib/actions/actions.ts +795 -0
  116. package/lib/actions/context.ts +170 -0
  117. package/lib/actions/meta.ts +251 -0
  118. package/lib/actions/path.test.ts +15 -0
  119. package/lib/actions/path.ts +99 -0
  120. package/lib/actions/spec.ts +545 -0
  121. package/lib/actions/types.ts +146 -0
  122. package/lib/actions/writer.test.ts +57 -0
  123. package/lib/actions/writer.ts +176 -0
  124. package/lib/auth/types.ts +22 -0
  125. package/lib/cache/cache.ts +291 -0
  126. package/lib/cache/etag.test.ts +122 -0
  127. package/lib/cache/etag.ts +106 -0
  128. package/lib/cache/memory.ts +52 -0
  129. package/lib/cache/types.ts +240 -0
  130. package/lib/errors.ts +66 -0
  131. package/lib/jsonld.ts +67 -0
  132. package/lib/makeTypeDefs.ts +138 -0
  133. package/lib/merge.ts +86 -0
  134. package/lib/mod.ts +14 -0
  135. package/lib/processAction.ts +690 -0
  136. package/lib/registry.test.ts +174 -0
  137. package/lib/registry.ts +455 -0
  138. package/lib/request.ts +153 -0
  139. package/lib/scopes.test.ts +70 -0
  140. package/lib/scopes.ts +178 -0
  141. package/lib/transformers/fileTransformer.ts +10 -0
  142. package/lib/types.ts +13 -0
  143. package/lib/utils/alwaysArray.ts +10 -0
  144. package/lib/utils/contextBuilder.ts +111 -0
  145. package/lib/utils/getActionContext.ts +76 -0
  146. package/lib/utils/getInternalName.ts +15 -0
  147. package/lib/utils/getParamLocation.ts +14 -0
  148. package/lib/utils/getPropertyValueSpecifications.ts +76 -0
  149. package/lib/utils/getRequestBodyValues.ts +155 -0
  150. package/lib/utils/getRequestIRIValues.ts +201 -0
  151. package/lib/utils/isBodyInit.ts +22 -0
  152. package/lib/utils/isNil.ts +4 -0
  153. package/lib/utils/isObject.ts +8 -0
  154. package/lib/utils/isPopulatedObject.ts +9 -0
  155. package/lib/utils/isPopulatedString.ts +4 -0
  156. package/lib/utils/joinPaths.ts +36 -0
  157. package/lib/utils/makeAppendProblemDetails.ts +57 -0
  158. package/lib/utils/makeURLPattern.ts +18 -0
  159. package/lib/utils/normalizeURL.ts +15 -0
  160. package/lib/utils/parseSearchParams.ts +36 -0
  161. package/lib/utils/preferredMediaTypes.ts +220 -0
  162. package/lib/utils/urlToIRI.ts +11 -0
  163. package/lib/utils/validateSpecValue.ts +0 -0
  164. package/lib/validators.ts +186 -0
  165. package/package.json +41 -0
package/lib/request.ts ADDED
@@ -0,0 +1,153 @@
1
+ import {IncomingMessage} from "node:http";
2
+ import {Readable} from "node:stream";
3
+ import {normalizeURL} from "./utils/normalizeURL.js";
4
+
5
+
6
+ export class WrappedRequest implements Request {
7
+
8
+ #url: string;
9
+ #req: Request | IncomingMessage;
10
+ #stream: ReadableStream;
11
+ #bodyUsed: boolean = false;
12
+ #headers: Headers = new Headers();
13
+
14
+ constructor(rootIRI: string, req: Request | IncomingMessage) {
15
+ this.#req = req;
16
+ this.#url = normalizeURL(rootIRI, req.url);
17
+
18
+ if (req instanceof Request) {
19
+ this.#stream = req.body;
20
+ this.#headers = req.headers;
21
+ } else {
22
+ this.#stream = Readable.toWeb(req) as ReadableStream;
23
+
24
+ for (const [key, value] of Object.entries(req.headers)) {
25
+ if (Array.isArray(value)) {
26
+ for (let i = 0; i < value.length; i++) {
27
+ this.#headers.append(key, value[i]);
28
+ }
29
+ } else {
30
+ this.#headers.set(key, value);
31
+ }
32
+ }
33
+ }
34
+ }
35
+
36
+ get body(): ReadableStream {
37
+ if (this.#bodyUsed) {
38
+ throw new Error('Body has already been consumed');
39
+ }
40
+
41
+ this.#bodyUsed = true;
42
+ return this.#stream;
43
+ }
44
+
45
+ arrayBuffer(): Promise<ArrayBuffer> {
46
+ return new Response(this.body).arrayBuffer();
47
+ }
48
+
49
+ blob(): Promise<Blob> {
50
+ return new Response(this.body).blob();
51
+ }
52
+
53
+ bytes(): Promise<Uint8Array<ArrayBuffer>> {
54
+ return new Response(this.body).bytes();
55
+ }
56
+
57
+ clone(): Request {
58
+ if (this.#bodyUsed) {
59
+ throw new Error('Body has already been consumed');
60
+ }
61
+
62
+ const [a, b] = this.#stream.tee();
63
+
64
+ this.#stream = a;
65
+
66
+ return new Request(this.url, {
67
+ ...this,
68
+ body: b,
69
+ });
70
+ }
71
+
72
+ formData(): Promise<FormData> {
73
+ return new Response(this.body).formData();
74
+ }
75
+
76
+ json(): Promise<unknown> {
77
+ return new Response(this.body).json();
78
+ }
79
+
80
+ text(): Promise<string> {
81
+ return new Response(this.body).text();
82
+ }
83
+
84
+ get bodyUsed(): boolean {
85
+ return this.#bodyUsed;
86
+ }
87
+
88
+ get cache(): RequestCache {
89
+ return 'default';
90
+ }
91
+
92
+ get credentials(): RequestCredentials {
93
+ return 'same-origin';
94
+ }
95
+
96
+ get destination(): RequestDestination {
97
+ return '';
98
+ }
99
+
100
+ get duplex(): undefined | 'half' {
101
+ return undefined;
102
+ }
103
+
104
+ get headers(): Headers {
105
+ return this.#headers;
106
+ }
107
+
108
+ get integrity(): string {
109
+ return '';
110
+ }
111
+
112
+ get isHistoryNavigation(): boolean {
113
+ return false;
114
+ }
115
+
116
+ get keepalive(): boolean {
117
+ return false;
118
+ }
119
+
120
+ get method(): string {
121
+ return this.#req.method ?? 'GET';
122
+ }
123
+
124
+ get mode(): RequestMode {
125
+ return 'cors';
126
+ }
127
+
128
+ get redirect(): RequestRedirect {
129
+ return 'follow';
130
+ }
131
+
132
+ get referrer(): string {
133
+ return this.#headers.get('Referrer') ?? 'no-referrer';
134
+ }
135
+
136
+ get referrerPolicy(): ReferrerPolicy {
137
+ const referrerPolicy = this.#headers.get('Referrer-Policy');
138
+
139
+ if (Array.isArray(referrerPolicy)) {
140
+ return referrerPolicy[0] as ReferrerPolicy ?? '';
141
+ }
142
+
143
+ return referrerPolicy as ReferrerPolicy ?? '';
144
+ }
145
+
146
+ get signal(): AbortSignal | undefined {
147
+ return undefined;
148
+ }
149
+
150
+ get url(): string {
151
+ return this.#url;
152
+ }
153
+ }
@@ -0,0 +1,70 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { Registry } from "./registry.js";
4
+ import { makeTypeDef, makeTypeDefs } from "./makeTypeDefs.js";
5
+
6
+
7
+ const typeDefs = makeTypeDefs([
8
+ makeTypeDef('GetThing', 'https://schema.example.com/'),
9
+ makeTypeDef('name', 'https://schema.org/'),
10
+ makeTypeDef('description', 'https://schema.org/'),
11
+ ]);
12
+
13
+ const registry = new Registry({
14
+ rootIRI: 'https://example.com',
15
+ });
16
+
17
+ const scope = registry.scope('/actions')
18
+ .public();
19
+
20
+ scope.http.get('get-thing', '/thing')
21
+ .public()
22
+ .define({
23
+ typeDef: typeDefs.GetThing,
24
+ spec: {
25
+ name: {
26
+ typeDef: typeDefs.name,
27
+ valueMinLength: 3,
28
+ valueRequired: true,
29
+ },
30
+ description: {
31
+ type: 'https://schema.org/description',
32
+ valueRequired: false,
33
+ },
34
+ },
35
+ });
36
+
37
+ registry.finalize();
38
+
39
+ test('It responds with actions partials as a jsonld type map', async () => {
40
+ const res = await registry.handleRequest(
41
+ new Request('https://example.com/actions'),
42
+ )
43
+ const body = await res.json();
44
+
45
+ assert.deepEqual(body[typeDefs.GetThing.type], {
46
+ '@type': typeDefs.GetThing.type,
47
+ '@id': 'https://example.com/actions/get-thing',
48
+ });
49
+ });
50
+
51
+ test('It responds with actions partials as a jsonld type map', async () => {
52
+ const res = await registry.handleRequest(
53
+ new Request('https://example.com/actions/get-thing'),
54
+ )
55
+ const body = await res.json();
56
+
57
+ // compacted representation is returned
58
+ assert.equal(body['@id'], 'https://example.com/actions/get-thing');
59
+ assert.equal(body['@type'], typeDefs.GetThing.term);
60
+ assert.deepEqual(body[`${typeDefs.name.term}-input`], {
61
+ '@type': 'https://schema.org/PropertyValueSpecification',
62
+ valueRequired: true,
63
+ valueMinLength: 3,
64
+ });
65
+ assert.deepEqual(body[`${typeDefs.description.term}-input`], {
66
+ '@type': 'https://schema.org/PropertyValueSpecification',
67
+ valueRequired: false,
68
+ });
69
+ });
70
+
package/lib/scopes.ts ADDED
@@ -0,0 +1,178 @@
1
+ import { joinPaths } from "./utils/joinPaths.js";
2
+ import { ActionAuth, HandlerDefinition } from "./actions/actions.js";
3
+ import { ActionMeta } from "./actions/meta.js";
4
+ import type { ContextState } from "./actions/spec.js";
5
+ import type { ImplementedAction } from "./actions/types.js";
6
+ import type { HTTPWriter } from "./actions/writer.js";
7
+ import { type Callable, HTTP, type Registry } from './registry.js';
8
+
9
+
10
+ export type MetaPropatator = (meta: ActionMeta) => void;
11
+
12
+ export type ScopeArgs = {
13
+ path: string;
14
+ serverTiming?: boolean;
15
+ registry: Registry;
16
+ writer: HTTPWriter;
17
+ propergateMeta: MetaPropatator;
18
+ }
19
+
20
+
21
+ export class Scope<
22
+ State extends ContextState = ContextState,
23
+ > implements Callable<State> {
24
+ #path: string;
25
+ #serverTiming: boolean = false;
26
+ #registry: Registry;
27
+ #writer: HTTPWriter;
28
+ #http: HTTP<State>;
29
+ #children: Array<ActionMeta> = [];
30
+ #public: boolean = true;
31
+ #propergateMeta: (meta: ActionMeta) => void;
32
+
33
+ constructor({
34
+ path,
35
+ serverTiming,
36
+ registry,
37
+ writer,
38
+ propergateMeta,
39
+ }: ScopeArgs) {
40
+ this.#path = path;
41
+ this.#serverTiming = serverTiming;
42
+ this.#registry = registry;
43
+ this.#writer = writer;
44
+ this.#http = new HTTP<State>(this);
45
+ this.#propergateMeta = propergateMeta;
46
+ }
47
+
48
+ get path(): string {
49
+ return this.#path;
50
+ }
51
+
52
+ get registry(): Registry {
53
+ return this.#registry;
54
+ }
55
+
56
+ get http(): HTTP<State> {
57
+ return this.#http;
58
+ }
59
+
60
+ get actions(): Array<ImplementedAction> {
61
+ return this.#children
62
+ .filter((meta) => {
63
+ if (meta.action == null) {
64
+ console.warn(`Action ${meta.method}: ${meta.path} not fully implemented before processing`);
65
+ }
66
+
67
+ return meta.action != null;
68
+ })
69
+ .map((meta) => meta.action) as Array<ImplementedAction>;
70
+ }
71
+
72
+ get handlers(): HandlerDefinition[] {
73
+ return this.actions.flatMap((action) => action.handlers);
74
+ }
75
+
76
+ public(): Scope<State> {
77
+ this.#public = true;
78
+
79
+ return this;
80
+ }
81
+
82
+ private(): Scope<State> {
83
+ this.#public = false;
84
+
85
+ return this;
86
+ }
87
+
88
+ /**
89
+ * Creates any HTTP method.
90
+ *
91
+ * @param method The HTTP method.
92
+ * @param name Name for the action being produced.
93
+ * @param path Path the action responds to.
94
+ */
95
+ method(method: string, name: string, path: string): ActionAuth<State> {
96
+ const meta = new ActionMeta<State>(
97
+ this.#registry.rootIRI,
98
+ method.toUpperCase(),
99
+ name,
100
+ path,
101
+ this.#registry,
102
+ this.#writer,
103
+ this,
104
+ );
105
+
106
+ meta.serverTiming = this.#serverTiming;
107
+
108
+ this.#children.push(meta);
109
+ this.#propergateMeta(meta);
110
+
111
+ return new ActionAuth<State>(meta);
112
+ }
113
+
114
+ url(): string {
115
+ return joinPaths(this.#registry.rootIRI, this.#path);
116
+ }
117
+
118
+ finalize(): void {
119
+ const partials = {
120
+ '@id': this.url(),
121
+ '@container': '@type',
122
+ };
123
+
124
+ for (let index = 0; index < this.#children.length; index++) {
125
+ const meta = this.#children[index];
126
+ const action = meta.action;
127
+
128
+ if (action == null || action.type == null) {
129
+ continue;
130
+ }
131
+
132
+ const partial = action.jsonldPartial();
133
+
134
+ if (partial == null) {
135
+ continue;
136
+ }
137
+
138
+ partials[partial['@type']] = partial;
139
+ }
140
+
141
+ if (this.#public) {
142
+ this.#registry.http.get('scope', this.#path)
143
+ .public()
144
+ .handle('application/ld+json', (ctx) => {
145
+ ctx.body = JSON.stringify(partials);
146
+ });
147
+ } else {
148
+ this.#registry.http.get('scope', this.#path)
149
+ .public()
150
+ .handle('application/ld+json', (ctx) => {
151
+ ctx.body = JSON.stringify(partials);
152
+ });
153
+ }
154
+
155
+ for (let index = 0; index < this.#children.length; index++) {
156
+ const action = this.#children[index].action;
157
+
158
+ if (action == null || action.type == null) {
159
+ continue;
160
+ }
161
+
162
+ if (this.#public) {
163
+ this.#registry.http.get('scope-action', joinPaths(this.url(), action.name))
164
+ .public()
165
+ .handle('application/ld+json', async (ctx) => {
166
+ ctx.body = JSON.stringify(await action.jsonld());
167
+ });
168
+ } else {
169
+ this.#registry.http.get('scope-action', joinPaths(this.url(), action.name))
170
+ .private()
171
+ .handle('application/ld+json', async (ctx) => {
172
+ ctx.body = JSON.stringify(await action.jsonld());
173
+ });
174
+ }
175
+ }
176
+ }
177
+ }
178
+
@@ -0,0 +1,10 @@
1
+
2
+ export async function fileTransformer(value: File | string): Promise<File | Blob> {
3
+ if (typeof value === 'string') {
4
+ return fetch(value)
5
+ .then((res) => res.blob())
6
+ .catch(() => Promise.reject('Invalid data url'))
7
+ }
8
+
9
+ return value;
10
+ }
package/lib/types.ts ADDED
@@ -0,0 +1,13 @@
1
+ export type ProblemDetailsParam = {
2
+ name: string;
3
+ reason: string;
4
+ pointer?: string;
5
+ };
6
+
7
+ export type ProblemDetails = {
8
+ title: string;
9
+ type?: string;
10
+ detail?: string;
11
+ instance?: string;
12
+ errors?: Array<ProblemDetailsParam>;
13
+ };
@@ -0,0 +1,10 @@
1
+ export function alwaysArray<T>(value: T | Array<T>): Array<T> {
2
+ if (Array.isArray(value)) {
3
+ return value;
4
+ }
5
+ if (typeof value !== 'undefined' && value !== null) {
6
+ return [value];
7
+ }
8
+
9
+ return [];
10
+ }
@@ -0,0 +1,111 @@
1
+ import type { JSONLDContext, ContextVersion, TypeDef } from "../jsonld.js";
2
+ import { isNil } from "./isNil.js";
3
+ import { isPopulatedObject } from "./isPopulatedObject.js";
4
+
5
+ export function contextBuilder({
6
+ vocab,
7
+ version = 1.1,
8
+ protect = false,
9
+ idTerm,
10
+ aliases,
11
+ typeDefs: argsTypeDefs,
12
+ }: {
13
+ idTerm?: string;
14
+ vocab?: string;
15
+ version?: ContextVersion;
16
+ protect?: boolean;
17
+ aliases?: Record<string, string>;
18
+ typeDefs?: Record<string, TypeDef> | Array<TypeDef>;
19
+ }): JSONLDContext {
20
+ let typeDefs: null | Array<TypeDef> = null;
21
+
22
+ if (Array.isArray(argsTypeDefs)) {
23
+ typeDefs = argsTypeDefs;
24
+ } else if (!isNil(argsTypeDefs)) {
25
+ typeDefs = Object.values(argsTypeDefs);
26
+ }
27
+
28
+ const inverseAliases: Record<string, string> = {};
29
+ const shadowedAliases: string[] = [];
30
+ const context: JSONLDContext = {};
31
+
32
+ if (version === 1.1) {
33
+ context['@version'] = version;
34
+ }
35
+
36
+ if (vocab != null) {
37
+ context['@vocab'] = vocab;
38
+ }
39
+
40
+ if (protect) {
41
+ context['@protected'] = true;
42
+ }
43
+
44
+ if (idTerm != null) {
45
+ context[idTerm] = '@id';
46
+ }
47
+
48
+ if (Array.isArray(typeDefs)) {
49
+ for (const typeDef of typeDefs) {
50
+ if (typeDef.term.startsWith('@')) {
51
+ throw new Error(`Terms cannot start with @. Recieved ${typeDef.term}`);
52
+ }
53
+
54
+ if (
55
+ typeof aliases !== 'undefined' && Object.hasOwn(aliases, typeDef.term)
56
+ ) {
57
+ shadowedAliases.push(typeDef.term);
58
+ }
59
+ }
60
+ }
61
+
62
+ if (typeof aliases !== 'undefined') {
63
+ for (const [alias, iri] of Object.entries(aliases)) {
64
+ if (iri === vocab) {
65
+ continue;
66
+ }
67
+
68
+ if (!shadowedAliases.includes(alias)) {
69
+ context[alias] = iri;
70
+ inverseAliases[iri] = alias;
71
+ }
72
+ }
73
+ }
74
+
75
+ if (Array.isArray(typeDefs)) {
76
+ for (const typeDef of typeDefs) {
77
+ if (idTerm === typeDef.term) {
78
+ continue;
79
+ }
80
+
81
+ let type: string = typeDef.type;
82
+ const matchesVocab = typeDef.type.startsWith(vocab);
83
+
84
+ if (vocab != null && matchesVocab) {
85
+ type = typeDef.type.replace(vocab, '');
86
+ } else if (typeof aliases !== 'undefined') {
87
+ for (const [alias, value] of Object.entries(aliases)) {
88
+ if (typeDef.type.startsWith(value)) {
89
+ type = typeDef.type.replace(value, `${alias}:`);
90
+ }
91
+ }
92
+ }
93
+
94
+ if (!matchesVocab && isPopulatedObject(typeDef.contextDefinition)) {
95
+ context[typeDef.term] = {
96
+ '@id': type,
97
+ ...typeDef.contextDefinition,
98
+ };
99
+ } else if (isPopulatedObject(typeDef.contextDefinition)) {
100
+ context[typeDef.term] = {
101
+ '@id': type,
102
+ ...typeDef.contextDefinition,
103
+ };
104
+ } else if (!matchesVocab) {
105
+ context[typeDef.term] = type;
106
+ }
107
+ }
108
+ }
109
+
110
+ return context;
111
+ }
@@ -0,0 +1,76 @@
1
+ import type { JSONLDContext, TypeDef } from "../jsonld.js";
2
+ import { makeTypeDef, makeTypeDefs } from "../makeTypeDefs.js";
3
+ import { contextBuilder } from "./contextBuilder.js";
4
+ import type { ActionSpec, PropertySpec } from '../actions/spec.js';
5
+
6
+ const defaultTypeDefs = makeTypeDefs([
7
+ makeTypeDef({ schema: 'https://schema.org/', term: 'Entrypoint' }),
8
+ makeTypeDef({ schema: 'https://schema.org/', term: 'target' }),
9
+ makeTypeDef({ schema: 'https://schema.org/', term: 'httpMethod' }),
10
+ makeTypeDef({ schema: 'https://schema.org/', term: 'urlTemplate' }),
11
+ makeTypeDef({ schema: 'https://schema.org/', term: 'PropertyValueSpecification' }),
12
+ makeTypeDef({ schema: 'https://schema.org/', term: 'maxValue' }),
13
+ makeTypeDef({ schema: 'https://schema.org/', term: 'minValue' }),
14
+ makeTypeDef({ schema: 'https://schema.org/', term: 'readonlyValue' }),
15
+ makeTypeDef({ schema: 'https://schema.org/', term: 'stepValue' }),
16
+ makeTypeDef({ schema: 'https://schema.org/', term: 'valueName' }),
17
+ makeTypeDef({ schema: 'https://schema.org/', term: 'valueRequired' }),
18
+ makeTypeDef({ schema: 'https://schema.org/', term: 'multipleValues' }),
19
+ ]);
20
+
21
+ export function getActionContext({
22
+ spec,
23
+ vocab,
24
+ aliases,
25
+ }: {
26
+ vocab?: string;
27
+ aliases?: Record<string, string>;
28
+ // deno-lint-ignore no-explicit-any
29
+ spec: ActionSpec
30
+ }): JSONLDContext {
31
+ const typeDefs: TypeDef[] = Object.values(defaultTypeDefs);
32
+
33
+ function searchAndAssignContextValues(
34
+ term: string,
35
+ propertySpec: PropertySpec,
36
+ ) {
37
+ if (propertySpec.typeDef != null) {
38
+ typeDefs.push(propertySpec.typeDef);
39
+ typeDefs.push(
40
+ makeTypeDef({
41
+ term: `${term}-input`,
42
+ type: `${propertySpec.typeDef.type}-input`,
43
+ }),
44
+ );
45
+ } else if (propertySpec.type === 'string') {
46
+ typeDefs.push(makeTypeDef({ term, type: propertySpec.type }));
47
+ typeDefs.push(
48
+ makeTypeDef({
49
+ term: `${term}-input`,
50
+ type: `${propertySpec.type}-input`,
51
+ }),
52
+ );
53
+ }
54
+
55
+ if (propertySpec.properties != null) {
56
+ for (
57
+ const [term, childPropertySpec] of Object.entries(
58
+ propertySpec.properties,
59
+ )
60
+ ) {
61
+ searchAndAssignContextValues(term, childPropertySpec);
62
+ }
63
+ }
64
+ }
65
+
66
+ for (const [term, propertySpec] of Object.entries(spec)) {
67
+ searchAndAssignContextValues(term, propertySpec);
68
+ }
69
+
70
+
71
+ return contextBuilder({
72
+ vocab,
73
+ aliases,
74
+ typeDefs,
75
+ });
76
+ }
@@ -0,0 +1,15 @@
1
+ import { isPopulatedString } from "./isPopulatedString.js";
2
+
3
+ export function getInternalName({
4
+ paramName,
5
+ specValue,
6
+ }: {
7
+ paramName: string;
8
+ specValue: { internalTerm?: string };
9
+ }): string {
10
+ if (isPopulatedString(specValue.internalTerm)) {
11
+ return specValue.internalTerm;
12
+ }
13
+
14
+ return paramName;
15
+ }
@@ -0,0 +1,14 @@
1
+
2
+
3
+ export type ParamLocation =
4
+ | 'path'
5
+ | 'search'
6
+ ;
7
+
8
+ export function getParamLocation(valueName: string, urlPattern: URLPattern): ParamLocation {
9
+ if (urlPattern.pathname.includes(`:${valueName}`)) {
10
+ return 'path';
11
+ }
12
+
13
+ return 'search';
14
+ }