@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
@@ -0,0 +1,106 @@
1
+
2
+ export class EtagConditions {
3
+
4
+ headers: Headers;
5
+
6
+ constructor(headers: Headers) {
7
+ this.headers = headers;
8
+ }
9
+
10
+ /**
11
+ * Returns true if the representation is not modified based of
12
+ * conditional headers and the value of the current representation's
13
+ * etag. True results should result in a 304 response.
14
+ *
15
+ * @param representationEtag - The current etag value for this representation.
16
+ */
17
+ public isNotModified(representationEtag?: string): boolean {
18
+ if (this.headers.has('If-Match')) {
19
+ return this.ifMatch(representationEtag);
20
+ } else if (this.headers.has('If-None-Match')) {
21
+ return !this.ifNoneMatch(representationEtag);
22
+ }
23
+
24
+ return false;
25
+ }
26
+
27
+ public ifMatch(
28
+ representationEtag?: string,
29
+ ): boolean {
30
+ let etag: string;
31
+ const header = this.headers.get('If-Match');
32
+
33
+ if (header == null) {
34
+ return true;
35
+ } else if (header.trim() === '*') {
36
+ return representationEtag != null;
37
+ }
38
+
39
+ const etags = header.split?.(',');
40
+
41
+ if (etags.length === 0) {
42
+ return true;
43
+ } else if (representationEtag == null) {
44
+ return false;
45
+ }
46
+
47
+ for (let i = 0; i < etags.length; i++) {
48
+ etag = etags[i].trim();
49
+
50
+ // If-Match ignores weak etags
51
+ if (etag.startsWith('W\/')) {
52
+ continue;
53
+ } else if (etag === representationEtag) {
54
+ return true;
55
+ }
56
+ }
57
+
58
+ return false;
59
+ }
60
+
61
+ public ifNoneMatch(
62
+ representationEtag?: string,
63
+ ): boolean {
64
+ let etag: string;
65
+ const header = this.headers.get('If-None-Match');
66
+
67
+ if (header == null || representationEtag == null) {
68
+ return true;
69
+ }
70
+
71
+ const etags = header.split?.(',');
72
+
73
+ if (etags.length === 0) {
74
+ return true;
75
+ }
76
+
77
+ if (etags[0] === '*') {
78
+ return representationEtag == null;
79
+ }
80
+
81
+ for (let i = 0; i < etags.length; i++) {
82
+ etag = etags[i].trim();
83
+
84
+ if (!etag.startsWith('W\/')) {
85
+ continue;
86
+ } else if (etag === representationEtag) {
87
+ return false;
88
+ }
89
+ }
90
+
91
+ return true;
92
+ }
93
+
94
+ public ifModifiedSince(): never {
95
+ throw new Error('Not implemented');
96
+ }
97
+
98
+ public ifUnmodifiedSince(): never {
99
+ throw new Error('Not implemented');
100
+ }
101
+
102
+ public ifRange(): never {
103
+ throw new Error('Not implemented');
104
+ }
105
+
106
+ }
@@ -0,0 +1,52 @@
1
+ import type {CacheDetails, CacheHitHandle, CacheMeta, CacheStorage, CacheMissHandle} from './types.js';
2
+
3
+
4
+ export class InMemoryCacheMeta implements CacheMeta {
5
+ #details: Map<string, CacheDetails> = new Map();
6
+
7
+ async get(key: string): Promise<CacheHitHandle| CacheMissHandle> {
8
+ const details = this.#details.get(key);
9
+ async function set(details: CacheDetails) {
10
+ this.#details.set(key, details);
11
+ }
12
+
13
+ if (details == null) {
14
+ return {
15
+ type: 'cache-miss',
16
+ set,
17
+ };
18
+ }
19
+
20
+ return {
21
+ ...details,
22
+ type: 'cache-hit',
23
+ set,
24
+ };
25
+ }
26
+
27
+ async set(key: string, details: CacheDetails) {
28
+ this.#details.set(key, details);
29
+ }
30
+
31
+ }
32
+
33
+ export class InMemoryCacheStorage implements CacheStorage {
34
+ #cache: Map<string, Blob> = new Map();
35
+
36
+ get(key: string): Blob {
37
+ const value = this.#cache.get(key);
38
+
39
+ return value as Blob;
40
+ }
41
+
42
+ set(key: string, value: Blob): void {
43
+ this.#cache.set(key, value);
44
+ }
45
+
46
+ invalidate(key: string): void {
47
+ this.#cache.delete(key);
48
+ }
49
+
50
+ }
51
+
52
+
@@ -0,0 +1,240 @@
1
+ import {ImplementedAction} from "../actions/types.js";
2
+ import {CacheContext} from "../mod.js";
3
+
4
+ export type CacheStrategyType =
5
+ | 'http'
6
+ | 'etag'
7
+ | 'store'
8
+ ;
9
+
10
+ export interface CacheEntryDescriptor {
11
+ contentType: string;
12
+ action: ImplementedAction;
13
+ request: Request;
14
+ args: CacheInstanceArgs;
15
+ };
16
+
17
+ export type CacheWhenFn = (
18
+ ctx: CacheContext,
19
+ ) => boolean;
20
+
21
+ export type CacheRuleArgs = {
22
+ /**
23
+ * A version which should increment every when a new release
24
+ * causes the existing cache to become stale.
25
+ */
26
+ version?: number;
27
+
28
+ lock?: boolean;
29
+ /**
30
+ * Defaults to varying on the authorization header
31
+ * when authenticated.
32
+ */
33
+ vary?: string;
34
+
35
+ varyOnAuth?: boolean;
36
+
37
+ varyOnCapabilities?: string | string[];
38
+
39
+ /**
40
+ * Defaults to false when a querystring is present
41
+ * or the request is authenticated.
42
+ *
43
+ * @default 'public-no-query'
44
+ */
45
+ when?: 'always' | 'public' | 'private' | 'no-query' | 'public-no-query' | 'private-no-query' | CacheWhenFn;
46
+ };
47
+
48
+ export type CacheControlArgs = {
49
+ private?: boolean;
50
+ public?: true;
51
+ noCache?: true;
52
+ noStore?: true;
53
+ mustRevalidate?: true;
54
+ mustUndestand?: true;
55
+ noTransform?: true;
56
+ immutable?: true;
57
+ proxyRevalidate?: true;
58
+ expires?: () => number | Date;
59
+ maxAge?: number | Date | (() => number | Date);
60
+ etag?: string;
61
+ };
62
+
63
+ export type CacheHTTPArgs =
64
+ & {
65
+ strategy: 'http';
66
+ strong?: undefined;
67
+ fromRequest?: undefined;
68
+ }
69
+ & CacheRuleArgs
70
+ & CacheControlArgs
71
+ ;
72
+
73
+ export type CacheHTTPInstanceArgs =
74
+ & CacheHTTPArgs
75
+ & { strategy: 'http', cache: CacheBuilder }
76
+ ;
77
+
78
+ export type CacheETagArgs =
79
+ & {
80
+ strategy: 'etag';
81
+ strong?: boolean;
82
+ fromRequest?: boolean;
83
+ etag?: undefined;
84
+ }
85
+ & CacheRuleArgs
86
+ & Omit<CacheControlArgs, 'etag'>
87
+ ;
88
+
89
+ export type CacheETagInstanceArgs =
90
+ & CacheETagArgs
91
+ & { stratey: 'etag', cache: CacheBuilder }
92
+ ;
93
+
94
+ export type CacheStoreArgs =
95
+ & {
96
+ strategy: 'store';
97
+ strong?: boolean;
98
+ fromRequest?: boolean;
99
+ etag?: undefined;
100
+ }
101
+ & CacheRuleArgs
102
+ & Omit<CacheControlArgs, 'etag'>
103
+ ;
104
+
105
+ export type CacheStoreInstanceArgs =
106
+ & CacheStoreArgs
107
+ & { strategy: 'store', cache: CacheBuilder }
108
+ ;
109
+
110
+ export type CacheInstanceArgs =
111
+ | CacheHTTPInstanceArgs
112
+ | CacheETagInstanceArgs
113
+ | CacheStoreInstanceArgs
114
+ ;
115
+
116
+ export type CacheDetails = {
117
+ key: string;
118
+ iri: string;
119
+ status?: number;
120
+ hasContent: boolean;
121
+ authKey: string;
122
+ etag: string;
123
+ headers: Headers;
124
+ contentType: string;
125
+ contentLength?: number;
126
+ contentEncoding?: string;
127
+ contentLanguage?: string;
128
+ contentRange?: string;
129
+ };
130
+
131
+ export type CacheHitHandle =
132
+ & CacheDetails
133
+ & {
134
+ type: 'cache-hit';
135
+ set(details: CacheDetails): Promise<void>;
136
+ };
137
+
138
+ export type CacheMissHandle = {
139
+ type: 'cache-miss';
140
+ set(details: CacheDetails): Promise<void>;
141
+ };
142
+
143
+ export type LockedCacheMissHandle = {
144
+ type: 'locked-cache-miss';
145
+ set(details: CacheDetails): Promise<void>;
146
+ release(): Promise<void>;
147
+ };
148
+
149
+
150
+ /**
151
+ * The cache meta knows how to query a storage for current freshness
152
+ * information on a cache entry without querying the data itself.
153
+ *
154
+ * Meta information might be stored in a separate data store to the
155
+ * cached response bodies. If supported by the storage, the representation
156
+ * can be locked for update preventing other requests for the same
157
+ * resource from proceeding until the cached representation is created.
158
+ */
159
+ export interface CacheMeta {
160
+
161
+ /**
162
+ * Sets the cache details for a representation.
163
+ *
164
+ * @param key A unique key for this representation.
165
+ * @param details The cache details.
166
+ */
167
+ set(key: string, details: CacheDetails): void | Promise<void>;
168
+
169
+ /**
170
+ * Retrieves the cache details of a representation.
171
+ *
172
+ * @param key A unique key for this representation.
173
+ */
174
+ get(key: string): CacheHitHandle | CacheMissHandle | Promise<CacheHitHandle | CacheMissHandle>;
175
+
176
+ /**
177
+ * Retrieves the cache details of a representation and takes a lock
178
+ * for update if the representation is not current.
179
+ *
180
+ * Any other requests for this representation will wait for the request
181
+ * holding the lock to populate the cache before proceeding.
182
+ *
183
+ * @param key A unique key for this representation.
184
+ */
185
+ getOrLock?(key: string): Promise<CacheHitHandle | LockedCacheMissHandle>;
186
+ }
187
+
188
+ export interface UpstreamCache {
189
+
190
+ /**
191
+ * Pushes a representation to the upstream cache.
192
+ */
193
+ push(args: {
194
+ url: string;
195
+ headers: Headers;
196
+ data: Blob;
197
+ }): Promise<void>;
198
+
199
+ /**
200
+ * Invalidates a representation in the upstream cache.
201
+ */
202
+ invalidate(url: string): Promise<void>;
203
+ };
204
+
205
+ export interface CacheStorage {
206
+
207
+ /**
208
+ * Retrieves a cache entry.
209
+ */
210
+ get(key: string): Blob | Promise<Blob>;
211
+
212
+ /**
213
+ * Sets a cache entry.
214
+ */
215
+ set(key: string, data: Blob): void | Promise<void>;
216
+
217
+ /**
218
+ * Invalidates a cache entry.
219
+ */
220
+ invalidate(key: string): void | Promise<void>;
221
+ };
222
+
223
+ export interface CacheBuilder {
224
+ meta: CacheMeta;
225
+
226
+ storage: CacheStorage;
227
+
228
+ upstream: UpstreamCache | undefined;
229
+
230
+ http(args?: CacheHTTPArgs): CacheInstanceArgs;
231
+
232
+ etag(args?: CacheETagArgs): CacheInstanceArgs;
233
+
234
+ store(args?: CacheStoreArgs): CacheInstanceArgs;
235
+
236
+ invalidate?(request: Request): Promise<void>;
237
+
238
+ push?(request: Request): Promise<void>;
239
+ }
240
+
package/lib/errors.ts ADDED
@@ -0,0 +1,66 @@
1
+ import {ProblemDetails} from "./types.js";
2
+
3
+ // https://datatracker.ietf.org/doc/html/rfc9457
4
+ export class ProblemDetailsError extends Error {
5
+ status: number;
6
+ problemDetails: ProblemDetails;
7
+ parentErr?: unknown;
8
+
9
+ constructor(
10
+ status: number,
11
+ problemDetails: string | ProblemDetails,
12
+ parentErr?: unknown,
13
+ ) {
14
+ const message = typeof problemDetails === 'string'
15
+ ? problemDetails
16
+ : problemDetails.title;
17
+
18
+ super(message);
19
+
20
+ this.status = status;
21
+ this.parentErr = parentErr;
22
+
23
+ if (typeof problemDetails === 'string') {
24
+ this.problemDetails = { title: problemDetails };
25
+ } else {
26
+ this.problemDetails = problemDetails;
27
+ }
28
+ }
29
+
30
+ toContent(contentType: string = 'application/ld+json') {
31
+ if (contentType === 'text/html') {
32
+ return `
33
+ <!doctype html>
34
+ <html lang="en">
35
+ <head>
36
+ <title>${this.problemDetails.title}</title>
37
+ </head>
38
+ <body>
39
+ <h1>${this.problemDetails.title}</h1>
40
+
41
+ <dl>
42
+ ${this.problemDetails?.errors?.map((error) => {
43
+ return `
44
+ <dt>
45
+ <code>${error.name}</code>
46
+ </dt>
47
+ <dd>
48
+ ${error.reason}
49
+ </dd>
50
+ `;
51
+ })}
52
+ </dl>
53
+ </body>
54
+ </html>
55
+ `;
56
+ }
57
+
58
+ return JSON.stringify(this.problemDetails);
59
+ }
60
+ }
61
+
62
+ export class InvalidActionParamsError extends ProblemDetailsError {
63
+ constructor(title: string) {
64
+ super(400, { title, type: 'invalid-param' });
65
+ }
66
+ }
package/lib/jsonld.ts ADDED
@@ -0,0 +1,67 @@
1
+ import type { ShallowMerge } from "./merge.js";
2
+
3
+ // Union of numbers within our depth limit
4
+ export type RecursiveDigit = 1 | 2 | 3 | 4 | 5 | 6 | 7;
5
+
6
+ // Array where each value is one greater than its index in the array
7
+ export type RecursiveNextDigit = [1, 2, 3, 4, 5, 6, 7, 'STOP'];
8
+
9
+ // Type to get the number from the index in the array
10
+ export type RecursiveIncrement<T> = T extends RecursiveDigit
11
+ ? RecursiveNextDigit[T]
12
+ : 'STOP';
13
+
14
+ // deno-lint-ignore no-explicit-any
15
+ export type EmptyObject = Pick<{ [key: string]: any }, ''>;
16
+
17
+ // deno-lint-ignore no-explicit-any
18
+ export type GuardType<T> = T extends (value: any) => value is infer U ? U
19
+ : never;
20
+
21
+ export type JSONPrimitive = string | number | boolean | null | undefined;
22
+
23
+ export type JSONValue = JSONPrimitive | JSONObject | JSONArray;
24
+
25
+ export type JSONObject = { [member: string]: JSONValue };
26
+
27
+ export interface JSONArray extends Array<JSONValue> {}
28
+
29
+ export type OrArray<T> = T | Array<T>;
30
+
31
+ export type Merge<T1 extends object, T2 extends object> = ShallowMerge<T1, T2>;
32
+
33
+ export type IRI = string;
34
+
35
+ export type IRIObject = { '@id': IRI };
36
+
37
+
38
+ // https://w3c.github.io/json-ld-syntax/#syntax-tokens-and-keywords
39
+ export type Aliases = Record<string, string>;
40
+
41
+ export type ContextVersion = 1.1;
42
+ export type ContextDefinitionType = '@id';
43
+ export type ContextDefinitionContainer =
44
+ | '@list'
45
+ | '@set';
46
+
47
+ export type ContextDefinition = {
48
+ '@id'?: string;
49
+ '@type'?: ContextDefinitionType;
50
+ '@container'?: ContextDefinitionContainer;
51
+ '@context'?: JSONLDContext;
52
+ '@protected'?: boolean;
53
+ };
54
+
55
+ export type JSONLDContext = {
56
+ '@version'?: ContextVersion;
57
+ '@base'?: string;
58
+ '@protected'?: boolean;
59
+ '@vocab'?: string;
60
+ } & Record<string, string | ContextDefinition>;
61
+
62
+ // deno-lint-ignore no-explicit-any
63
+ export type TypeDef<Term extends string = any, Type extends string = any> = {
64
+ type: Type;
65
+ term: Term;
66
+ contextDefinition?: ContextDefinition;
67
+ };
@@ -0,0 +1,138 @@
1
+ import { joinPaths } from "./utils/joinPaths.js"
2
+ import type { ContextDefinition, ContextDefinitionContainer, JSONLDContext, TypeDef } from "./jsonld.js";
3
+ import type { Context } from "./actions/context.js";
4
+ import { isPopulatedObject } from "./utils/isPopulatedObject.js";
5
+ import { isPopulatedString } from "./utils/isPopulatedString.js";
6
+
7
+ export type MakeTypeDefArgsFromType<Term extends string, Type extends string> =
8
+ {
9
+ term: Term;
10
+ type: Type;
11
+ schema?: undefined;
12
+ isIRI?: boolean;
13
+ protect?: boolean;
14
+ container?: ContextDefinitionContainer;
15
+ context?: Context;
16
+ };
17
+
18
+ export type MakeTypeDefArgsFromSchema<
19
+ Term extends string,
20
+ Schema extends string,
21
+ > = {
22
+ term: Term;
23
+ schema: Schema;
24
+ type?: undefined;
25
+ isIRI?: boolean;
26
+ protect?: boolean;
27
+ container?: ContextDefinitionContainer;
28
+ context?: Context;
29
+ };
30
+
31
+ export type MakeTypeDefArgs<A extends string, B extends string> =
32
+ | MakeTypeDefArgsFromType<A, B>
33
+ | MakeTypeDefArgsFromSchema<A, B>;
34
+
35
+ export function makeTypeDef<Term extends string, Schema extends string>(
36
+ term: Term,
37
+ type: Schema,
38
+ ): TypeDef<Term, `${Schema}${Term}`>;
39
+
40
+ export function makeTypeDef<Term extends string, Type extends string>(
41
+ args: MakeTypeDefArgsFromType<Term, Type>,
42
+ ): TypeDef<Term, Type>;
43
+
44
+ export function makeTypeDef<Term extends string, Schema extends string>(
45
+ args: MakeTypeDefArgsFromSchema<Term, Schema>,
46
+ ): TypeDef<Term, `${Schema}${Term}`>;
47
+
48
+ export function makeTypeDef<Term extends string, TypeOrSchema extends string>(
49
+ arg1: Term | MakeTypeDefArgs<Term, TypeOrSchema>,
50
+ arg2?: TypeOrSchema,
51
+ ) {
52
+ let contextDefinition: ContextDefinition = {};
53
+ let protect: boolean = false;
54
+ let args: MakeTypeDefArgs<Term, TypeOrSchema>;
55
+
56
+ if (typeof arg1 === 'string') {
57
+ args = {
58
+ term: arg1,
59
+ schema: arg2 as TypeOrSchema,
60
+ };
61
+ } else {
62
+ args = arg1;
63
+
64
+ if (typeof arg1.protect === 'boolean') {
65
+ protect = arg1.protect;
66
+ }
67
+ }
68
+
69
+ if (
70
+ args.isIRI || isPopulatedString(args.container) ||
71
+ isPopulatedObject(args.context)
72
+ ) {
73
+ contextDefinition = {};
74
+
75
+ if (args.isIRI) {
76
+ contextDefinition['@type'] = '@id';
77
+ }
78
+
79
+ if (isPopulatedString(args.container)) {
80
+ contextDefinition['@container'] = args.container;
81
+ }
82
+
83
+ if (isPopulatedObject(args.context)) {
84
+ contextDefinition['@context'] = args.context as JSONLDContext;
85
+ }
86
+
87
+ if (protect) {
88
+ contextDefinition['@protected'] = true;
89
+ }
90
+
91
+ if (isPopulatedString(args.schema)) {
92
+ return {
93
+ term: args.term,
94
+ type: joinPaths(args.schema, args.term),
95
+ contextDefinition,
96
+ } as TypeDef<Term, `${TypeOrSchema}${Term}`>;
97
+ }
98
+
99
+ return {
100
+ term: args.term,
101
+ type: args.type,
102
+ contextDefinition,
103
+ };
104
+ }
105
+
106
+ if (protect) {
107
+ contextDefinition = { '@protected': true };
108
+ }
109
+
110
+ if (isPopulatedString(args.schema)) {
111
+ return {
112
+ term: args.term,
113
+ type: joinPaths(args.schema, args.term),
114
+ contextDefinition,
115
+ } as TypeDef<Term, `${TypeOrSchema}${Term}`>;
116
+ }
117
+
118
+ return {
119
+ term: args.term,
120
+ type: args.type,
121
+ contextDefinition,
122
+ };
123
+ }
124
+
125
+ export function makeTypeDefs<Term extends string, TD extends TypeDef<Term>>(
126
+ typeDefs: Readonly<Array<TD>>,
127
+ ): { [TypeDef in TD as TypeDef['term']]: TypeDef } {
128
+ type TypeDefs = { [TypeDef in TD as TypeDef['term']]: TypeDef };
129
+ const result = typeDefs.reduce(
130
+ (acc, typeDef) => ({
131
+ ...acc,
132
+ [typeDef.term]: typeDef,
133
+ }),
134
+ {},
135
+ );
136
+
137
+ return result as TypeDefs;
138
+ }