@occultist/occultist 0.0.4 → 0.0.6
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.
- package/dist/accept.js +0 -1
- package/dist/actions/actionSets.d.ts +3 -3
- package/dist/actions/actions.d.ts +69 -48
- package/dist/actions/actions.js +39 -4
- package/dist/actions/context.d.ts +15 -11
- package/dist/actions/context.js +5 -0
- package/dist/actions/meta.d.ts +18 -12
- package/dist/actions/meta.js +114 -38
- package/dist/actions/spec.d.ts +3 -3
- package/dist/actions/types.d.ts +45 -16
- package/dist/actions/writer.d.ts +1 -1
- package/dist/actions/writer.test.js +2 -2
- package/dist/cache/cache.d.ts +3 -3
- package/dist/cache/cache.js +111 -42
- package/dist/cache/etag.test.js +1 -1
- package/dist/cache/file.d.ts +33 -1
- package/dist/cache/file.js +92 -10
- package/dist/cache/memory.d.ts +12 -2
- package/dist/cache/memory.js +63 -1
- package/dist/cache/types.d.ts +51 -22
- package/dist/errors.d.ts +1 -1
- package/dist/jsonld.d.ts +1 -1
- package/dist/makeTypeDefs.d.ts +2 -2
- package/dist/mod.d.ts +17 -15
- package/dist/mod.js +17 -15
- package/dist/processAction.d.ts +2 -2
- package/dist/processAction.js +1 -1
- package/dist/registry.d.ts +74 -8
- package/dist/registry.js +70 -8
- package/dist/registry.test.js +1 -1
- package/dist/scopes.d.ts +8 -8
- package/dist/scopes.js +8 -5
- package/dist/utils/contextBuilder.d.ts +1 -1
- package/dist/utils/getActionContext.d.ts +2 -2
- package/dist/utils/getPropertyValueSpecifications.d.ts +1 -1
- package/dist/utils/getRequestBodyValues.d.ts +3 -3
- package/dist/utils/getRequestIRIValues.d.ts +2 -2
- package/dist/utils/isPopulatedObject.js +1 -1
- package/dist/utils/makeAppendProblemDetails.d.ts +1 -1
- package/dist/utils/makeURLPattern.js +1 -0
- package/dist/utils/parseSearchParams.d.ts +2 -2
- package/dist/validators.d.ts +2 -2
- package/dist/validators.js +2 -2
- package/lib/accept.test.ts +1 -1
- package/lib/accept.ts +0 -2
- package/lib/actions/actionSets.ts +4 -4
- package/lib/actions/actions.ts +159 -99
- package/lib/actions/context.ts +22 -10
- package/lib/actions/meta.ts +140 -55
- package/lib/actions/path.test.ts +1 -1
- package/lib/actions/path.ts +1 -1
- package/lib/actions/spec.ts +3 -3
- package/lib/actions/types.ts +60 -15
- package/lib/actions/writer.test.ts +2 -2
- package/lib/actions/writer.ts +1 -1
- package/lib/cache/cache.ts +138 -52
- package/lib/cache/etag.test.ts +1 -1
- package/lib/cache/file.ts +113 -12
- package/lib/cache/memory.ts +85 -3
- package/lib/cache/types.ts +70 -23
- package/lib/errors.ts +1 -1
- package/lib/jsonld.ts +1 -1
- package/lib/makeTypeDefs.ts +5 -5
- package/lib/mod.ts +17 -15
- package/lib/processAction.ts +14 -14
- package/lib/registry.test.ts +1 -1
- package/lib/registry.ts +96 -19
- package/lib/request.ts +1 -1
- package/lib/scopes.test.ts +2 -2
- package/lib/scopes.ts +14 -11
- package/lib/utils/contextBuilder.ts +3 -3
- package/lib/utils/getActionContext.ts +4 -4
- package/lib/utils/getInternalName.ts +1 -1
- package/lib/utils/getPropertyValueSpecifications.ts +4 -4
- package/lib/utils/getRequestBodyValues.ts +5 -5
- package/lib/utils/getRequestIRIValues.ts +4 -4
- package/lib/utils/isPopulatedObject.ts +1 -1
- package/lib/utils/makeAppendProblemDetails.ts +1 -1
- package/lib/utils/makeURLPattern.ts +1 -0
- package/lib/utils/parseSearchParams.ts +2 -2
- package/lib/validators.ts +5 -5
- package/package.json +4 -2
package/lib/cache/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {ImplementedAction} from "../actions/types.
|
|
2
|
-
import {CacheContext} from "../mod.
|
|
1
|
+
import type {AuthState, ImplementedAction} from "../actions/types.ts";
|
|
2
|
+
import type {CacheContext} from "../mod.ts";
|
|
3
3
|
|
|
4
4
|
export type CacheStrategyType =
|
|
5
5
|
| 'http'
|
|
@@ -7,15 +7,28 @@ export type CacheStrategyType =
|
|
|
7
7
|
| 'store'
|
|
8
8
|
;
|
|
9
9
|
|
|
10
|
+
export type CacheSemantics =
|
|
11
|
+
| 'options'
|
|
12
|
+
| 'head'
|
|
13
|
+
| 'get'
|
|
14
|
+
| 'post'
|
|
15
|
+
| 'put'
|
|
16
|
+
| 'delete'
|
|
17
|
+
| 'query'
|
|
18
|
+
;
|
|
19
|
+
|
|
10
20
|
export interface CacheEntryDescriptor {
|
|
11
21
|
contentType: string;
|
|
22
|
+
semantics: CacheSemantics;
|
|
12
23
|
action: ImplementedAction;
|
|
13
24
|
request: Request;
|
|
14
25
|
args: CacheInstanceArgs;
|
|
15
26
|
};
|
|
16
27
|
|
|
17
|
-
export type CacheWhenFn
|
|
18
|
-
|
|
28
|
+
export type CacheWhenFn<
|
|
29
|
+
Auth extends AuthState = AuthState,
|
|
30
|
+
> = (
|
|
31
|
+
ctx: CacheContext<Auth>,
|
|
19
32
|
) => boolean;
|
|
20
33
|
|
|
21
34
|
export type CacheRuleArgs = {
|
|
@@ -36,6 +49,11 @@ export type CacheRuleArgs = {
|
|
|
36
49
|
|
|
37
50
|
varyOnCapabilities?: string | string[];
|
|
38
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Overrides the semantics of the cache.
|
|
54
|
+
*/
|
|
55
|
+
semantics?: CacheSemantics;
|
|
56
|
+
|
|
39
57
|
/**
|
|
40
58
|
* Defaults to false when a querystring is present
|
|
41
59
|
* or the request is authenticated.
|
|
@@ -47,7 +65,7 @@ export type CacheRuleArgs = {
|
|
|
47
65
|
|
|
48
66
|
export type CacheControlArgs = {
|
|
49
67
|
private?: boolean;
|
|
50
|
-
|
|
68
|
+
publicWhenAuthenticated?: true;
|
|
51
69
|
noCache?: true;
|
|
52
70
|
noStore?: true;
|
|
53
71
|
mustRevalidate?: true;
|
|
@@ -62,7 +80,6 @@ export type CacheControlArgs = {
|
|
|
62
80
|
|
|
63
81
|
export type CacheHTTPArgs =
|
|
64
82
|
& {
|
|
65
|
-
strategy: 'http';
|
|
66
83
|
strong?: undefined;
|
|
67
84
|
fromRequest?: undefined;
|
|
68
85
|
}
|
|
@@ -77,7 +94,6 @@ export type CacheHTTPInstanceArgs =
|
|
|
77
94
|
|
|
78
95
|
export type CacheETagArgs =
|
|
79
96
|
& {
|
|
80
|
-
strategy: 'etag';
|
|
81
97
|
strong?: boolean;
|
|
82
98
|
fromRequest?: boolean;
|
|
83
99
|
etag?: undefined;
|
|
@@ -88,12 +104,11 @@ export type CacheETagArgs =
|
|
|
88
104
|
|
|
89
105
|
export type CacheETagInstanceArgs =
|
|
90
106
|
& CacheETagArgs
|
|
91
|
-
& {
|
|
107
|
+
& { strategy: 'etag', cache: CacheBuilder }
|
|
92
108
|
;
|
|
93
109
|
|
|
94
110
|
export type CacheStoreArgs =
|
|
95
111
|
& {
|
|
96
|
-
strategy: 'store';
|
|
97
112
|
strong?: boolean;
|
|
98
113
|
fromRequest?: boolean;
|
|
99
114
|
etag?: undefined;
|
|
@@ -120,7 +135,7 @@ export type CacheDetails = {
|
|
|
120
135
|
hasContent: boolean;
|
|
121
136
|
authKey: string;
|
|
122
137
|
etag: string;
|
|
123
|
-
headers:
|
|
138
|
+
headers: Record<string, string | string[]>;
|
|
124
139
|
contentType: string;
|
|
125
140
|
contentLength?: number;
|
|
126
141
|
contentEncoding?: string;
|
|
@@ -132,18 +147,18 @@ export type CacheHitHandle =
|
|
|
132
147
|
& CacheDetails
|
|
133
148
|
& {
|
|
134
149
|
type: 'cache-hit';
|
|
135
|
-
set(details: CacheDetails): Promise<void>;
|
|
150
|
+
set(details: CacheDetails): void | Promise<void>;
|
|
136
151
|
};
|
|
137
152
|
|
|
138
153
|
export type CacheMissHandle = {
|
|
139
154
|
type: 'cache-miss';
|
|
140
|
-
set(details: CacheDetails): Promise<void>;
|
|
155
|
+
set(details: CacheDetails): void | Promise<void>;
|
|
141
156
|
};
|
|
142
157
|
|
|
143
158
|
export type LockedCacheMissHandle = {
|
|
144
159
|
type: 'locked-cache-miss';
|
|
145
|
-
set(details: CacheDetails): Promise<void>;
|
|
146
|
-
release(): Promise<void>;
|
|
160
|
+
set(details: CacheDetails): void | Promise<void>;
|
|
161
|
+
release(): void | Promise<void>;
|
|
147
162
|
};
|
|
148
163
|
|
|
149
164
|
|
|
@@ -161,28 +176,50 @@ export interface CacheMeta {
|
|
|
161
176
|
/**
|
|
162
177
|
* Sets the cache details for a representation.
|
|
163
178
|
*
|
|
164
|
-
* @param key
|
|
165
|
-
* @param details
|
|
179
|
+
* @param key Unique key for this cached value.
|
|
180
|
+
* @param details Details of the cache to store.
|
|
166
181
|
*/
|
|
167
182
|
set(key: string, details: CacheDetails): void | Promise<void>;
|
|
168
183
|
|
|
169
184
|
/**
|
|
170
185
|
* Retrieves the cache details of a representation.
|
|
171
186
|
*
|
|
172
|
-
* @param key
|
|
187
|
+
* @param key Unique key for this cached value.
|
|
173
188
|
*/
|
|
174
189
|
get(key: string): CacheHitHandle | CacheMissHandle | Promise<CacheHitHandle | CacheMissHandle>;
|
|
175
190
|
|
|
176
191
|
/**
|
|
177
192
|
* Retrieves the cache details of a representation and takes a lock
|
|
178
|
-
* for update if the representation is not current.
|
|
193
|
+
* for update if the representation is not current. All concurrent requests
|
|
194
|
+
* targeting the same cached value will wait for the cache to be populated
|
|
195
|
+
* and respond from cache. This can occur across processes if the locking
|
|
196
|
+
* mechanism allows for it.
|
|
197
|
+
*
|
|
198
|
+
* This is an experimental API and its benifits are untested. APIs that
|
|
199
|
+
* have a high failure rate could see degredation in services as requests
|
|
200
|
+
* will queue to take the lock, but fail to set a new cache value causing
|
|
201
|
+
* the queued requests to continue locking.
|
|
179
202
|
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
203
|
+
* However, this could help protect downstream services from thundering herd
|
|
204
|
+
* like scenarios as only one requester will build the representation that all
|
|
205
|
+
* requesters use.
|
|
182
206
|
*
|
|
183
|
-
* @param key
|
|
207
|
+
* @param key Unique key for this cached value.
|
|
184
208
|
*/
|
|
185
209
|
getOrLock?(key: string): Promise<CacheHitHandle | LockedCacheMissHandle>;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Invalidates a cached value by key.
|
|
213
|
+
*
|
|
214
|
+
* @param key Unique key for this cached value.
|
|
215
|
+
*/
|
|
216
|
+
invalidate(key: string): void | Promise<void>;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Flushes the entire cache of values.
|
|
220
|
+
*/
|
|
221
|
+
flush(): void | Promise<void>;
|
|
222
|
+
|
|
186
223
|
}
|
|
187
224
|
|
|
188
225
|
export interface UpstreamCache {
|
|
@@ -200,6 +237,7 @@ export interface UpstreamCache {
|
|
|
200
237
|
* Invalidates a representation in the upstream cache.
|
|
201
238
|
*/
|
|
202
239
|
invalidate(url: string): Promise<void>;
|
|
240
|
+
|
|
203
241
|
};
|
|
204
242
|
|
|
205
243
|
export interface CacheStorage {
|
|
@@ -215,9 +253,15 @@ export interface CacheStorage {
|
|
|
215
253
|
set(key: string, data: Blob): void | Promise<void>;
|
|
216
254
|
|
|
217
255
|
/**
|
|
218
|
-
*
|
|
256
|
+
* Deletes a cache entry.
|
|
219
257
|
*/
|
|
220
258
|
invalidate(key: string): void | Promise<void>;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Flushes the entire cache of values.
|
|
262
|
+
*/
|
|
263
|
+
flush(): void | Promise<void>;
|
|
264
|
+
|
|
221
265
|
};
|
|
222
266
|
|
|
223
267
|
export interface CacheBuilder {
|
|
@@ -233,7 +277,10 @@ export interface CacheBuilder {
|
|
|
233
277
|
|
|
234
278
|
store(args?: CacheStoreArgs): CacheInstanceArgs;
|
|
235
279
|
|
|
236
|
-
|
|
280
|
+
/**
|
|
281
|
+
* Removes an item from the cache.
|
|
282
|
+
*/
|
|
283
|
+
invalidate(key: string, url: string): void | Promise<void>;
|
|
237
284
|
|
|
238
285
|
push?(request: Request): Promise<void>;
|
|
239
286
|
}
|
package/lib/errors.ts
CHANGED
package/lib/jsonld.ts
CHANGED
package/lib/makeTypeDefs.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { joinPaths } from "./utils/joinPaths.
|
|
2
|
-
import type { ContextDefinition, ContextDefinitionContainer, JSONLDContext, TypeDef } from "./jsonld.
|
|
3
|
-
import type { Context } from "./actions/context.
|
|
4
|
-
import { isPopulatedObject } from "./utils/isPopulatedObject.
|
|
5
|
-
import { isPopulatedString } from "./utils/isPopulatedString.
|
|
1
|
+
import { joinPaths } from "./utils/joinPaths.ts"
|
|
2
|
+
import type { ContextDefinition, ContextDefinitionContainer, JSONLDContext, TypeDef } from "./jsonld.ts";
|
|
3
|
+
import type { Context } from "./actions/context.ts";
|
|
4
|
+
import { isPopulatedObject } from "./utils/isPopulatedObject.ts";
|
|
5
|
+
import { isPopulatedString } from "./utils/isPopulatedString.ts";
|
|
6
6
|
|
|
7
7
|
export type MakeTypeDefArgsFromType<Term extends string, Type extends string> =
|
|
8
8
|
{
|
package/lib/mod.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
export * from './cache/types.
|
|
2
|
-
export * from './cache/cache.
|
|
3
|
-
export * from './cache/memory.
|
|
4
|
-
export * from './cache/file.
|
|
5
|
-
export * from './actions/types.
|
|
6
|
-
export * from './actions/meta.
|
|
7
|
-
export * from './actions/spec.
|
|
8
|
-
export * from './actions/context.
|
|
9
|
-
export * from './actions/path.
|
|
10
|
-
export * from './actions/actions.
|
|
11
|
-
export * from './actions/actionSets.
|
|
12
|
-
export * from './
|
|
13
|
-
export * from './
|
|
14
|
-
export * from './
|
|
15
|
-
export * from './
|
|
1
|
+
export * from './cache/types.ts';
|
|
2
|
+
export * from './cache/cache.ts';
|
|
3
|
+
export * from './cache/memory.ts';
|
|
4
|
+
export * from './cache/file.ts';
|
|
5
|
+
export * from './actions/types.ts';
|
|
6
|
+
export * from './actions/meta.ts';
|
|
7
|
+
export * from './actions/spec.ts';
|
|
8
|
+
export * from './actions/context.ts';
|
|
9
|
+
export * from './actions/path.ts';
|
|
10
|
+
export * from './actions/actions.ts';
|
|
11
|
+
export * from './actions/actionSets.ts';
|
|
12
|
+
export * from './request.ts';
|
|
13
|
+
export * from './actions/writer.ts';
|
|
14
|
+
export * from './registry.ts';
|
|
15
|
+
export * from './makeTypeDefs.ts';
|
|
16
|
+
export * from './utils/joinPaths.ts';
|
|
17
|
+
export * from './utils/contextBuilder.ts';
|
package/lib/processAction.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { JsonPointer } from
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { type
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import type {
|
|
14
|
-
import type {
|
|
1
|
+
import { JsonPointer } from "json-ptr";
|
|
2
|
+
import { getInternalName } from "./utils/getInternalName.ts";
|
|
3
|
+
import { type BodyValue, getRequestBodyValues } from "./utils/getRequestBodyValues.ts";
|
|
4
|
+
import { type IRIValue, getRequestIRIValues } from "./utils/getRequestIRIValues.ts";
|
|
5
|
+
import { isNil } from "./utils/isNil.ts";
|
|
6
|
+
import { isObject } from "./utils/isObject.ts";
|
|
7
|
+
import { isPopulatedObject } from "./utils/isPopulatedObject.ts";
|
|
8
|
+
import { type ProblemDetailsParamsRefs, makeAppendProblemDetails } from "./utils/makeAppendProblemDetails.ts";
|
|
9
|
+
import { failsRequiredRequirement, failsTypeRequirement, failsContentTypeRequirement, failsMaxValue, failsMinValue, failsValueMinLength, failsValueMaxLength, failsStepValue, failsPatternValue, failsValidator, isObjectArraySpec, isObjectSpec, isArraySpec } from "./validators.ts";
|
|
10
|
+
import { InvalidActionParamsError, ProblemDetailsError } from "./errors.ts";
|
|
11
|
+
import { alwaysArray } from "./utils/alwaysArray.ts";
|
|
12
|
+
import type { ImplementedAction } from "./actions/types.ts";
|
|
13
|
+
import type { ActionPayload, ActionSpec, ArraySpec, ContextState, ObjectArraySpec, ObjectSpec, ParsedIRIValues, PropertySpec, SpecValue, ValueSpec } from "./actions/spec.ts";
|
|
14
|
+
import type {JSONValue} from "./jsonld.ts";
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
|
package/lib/registry.test.ts
CHANGED
package/lib/registry.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { Accept } from "./accept.
|
|
2
|
-
import { ActionAuth, HandlerDefinition } from "./actions/actions.
|
|
3
|
-
import { type ActionMatchResult, ActionSet } from "./actions/actionSets.
|
|
4
|
-
import { ActionMeta } from "./actions/meta.
|
|
5
|
-
import type { ImplementedAction } from "./actions/types.
|
|
6
|
-
import { ResponseWriter } from "./actions/writer.
|
|
7
|
-
import { Scope } from './scopes.
|
|
1
|
+
import { Accept } from "./accept.ts";
|
|
2
|
+
import { ActionAuth, HandlerDefinition } from "./actions/actions.ts";
|
|
3
|
+
import { type ActionMatchResult, ActionSet } from "./actions/actionSets.ts";
|
|
4
|
+
import { ActionMeta } from "./actions/meta.ts";
|
|
5
|
+
import type { CacheHitHeader, ImplementedAction } from "./actions/types.ts";
|
|
6
|
+
import { ResponseWriter } from "./actions/writer.ts";
|
|
7
|
+
import { Scope } from './scopes.ts';
|
|
8
8
|
import { IncomingMessage, type ServerResponse } from "node:http";
|
|
9
|
-
import type { Merge } from "./actions/spec.
|
|
10
|
-
import type { ContextState, Middleware } from "./actions/spec.
|
|
11
|
-
import {ProblemDetailsError} from "./errors.
|
|
12
|
-
import {WrappedRequest} from "./request.
|
|
9
|
+
import type { Merge } from "./actions/spec.ts";
|
|
10
|
+
import type { ContextState, Middleware } from "./actions/spec.ts";
|
|
11
|
+
import {ProblemDetailsError} from "./errors.ts"
|
|
12
|
+
import {WrappedRequest} from "./request.ts";
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
export interface Callable<
|
|
@@ -28,10 +28,6 @@ export class HTTP<
|
|
|
28
28
|
this.#callable = callable;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
trace(name: string, path: string): ActionAuth<State> {
|
|
32
|
-
return this.#callable.method('trace', name, path);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
31
|
options(name: string, path: string): ActionAuth<State> {
|
|
36
32
|
return this.#callable.method('options', name, path);
|
|
37
33
|
}
|
|
@@ -60,6 +56,10 @@ export class HTTP<
|
|
|
60
56
|
return this.#callable.method('delete', name, path);
|
|
61
57
|
}
|
|
62
58
|
|
|
59
|
+
query(name: string, path: string): ActionAuth<State> {
|
|
60
|
+
return this.#callable.method('query', name, path);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
|
|
@@ -95,10 +95,79 @@ export type RegistryEvents =
|
|
|
95
95
|
;
|
|
96
96
|
|
|
97
97
|
export type RegistryArgs = {
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The public root endpoint the registry is bound to.
|
|
101
|
+
*/
|
|
98
102
|
rootIRI: string;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Set to `true` if a cache header should be added to the response when
|
|
106
|
+
* cache is successfully hit. Or assign custom header values.
|
|
107
|
+
*/
|
|
108
|
+
cacheHitHeader?: CacheHitHeader;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Enables adding server timing headers to the response.
|
|
112
|
+
*/
|
|
99
113
|
serverTiming?: boolean;
|
|
100
114
|
};
|
|
101
115
|
|
|
116
|
+
/**
|
|
117
|
+
* All actions of an Occultist based API are created through an action registry.
|
|
118
|
+
* The registry exposes an interface for querying registered actions and emits events
|
|
119
|
+
* when userland actions have all been defined. Extensions can register themselves
|
|
120
|
+
* with the registry and create more actions and endpoints using the actions defined
|
|
121
|
+
* in userland. Userland code might also use the registry's querying functionality
|
|
122
|
+
* to programically make API calls as though they were made over the network via HTTP.
|
|
123
|
+
*
|
|
124
|
+
* @example <caption>Creates a simple registry that responds with a HTML document</caption>
|
|
125
|
+
*
|
|
126
|
+
* ```
|
|
127
|
+
* import {createServer} from 'node:http':
|
|
128
|
+
* import {Registry} from '@occultist/occultist';
|
|
129
|
+
*
|
|
130
|
+
* const server = createServer();
|
|
131
|
+
* const registry = new Registry({ rootIRI: 'https://example.com' });
|
|
132
|
+
*
|
|
133
|
+
* registry.http.get('get-root', '/')
|
|
134
|
+
* .handle('text/html', `
|
|
135
|
+
* <!doctype html>
|
|
136
|
+
* <html>
|
|
137
|
+
* <head><title>Hello, World!</title></head>
|
|
138
|
+
* <body>
|
|
139
|
+
* <h1>Hello, World!</h1>
|
|
140
|
+
* </body>
|
|
141
|
+
* </body>
|
|
142
|
+
* `);
|
|
143
|
+
*
|
|
144
|
+
*
|
|
145
|
+
* server.on('request', (req, res) => registry.handleRequest(req, res));
|
|
146
|
+
* server.listen(3000);
|
|
147
|
+
*
|
|
148
|
+
* // makes a call programically to the registry
|
|
149
|
+
* const res = await registry.handleRequest(new Request('https://example.com'));
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* @param args.rootIRI The public root endpoint the registry is bound to. If the
|
|
153
|
+
* registry responds to requests on a subpath, the subpath should be included
|
|
154
|
+
* in the `rootIRI` value.
|
|
155
|
+
*
|
|
156
|
+
* @param args.cacheHitHeader A custom cache hit header. If set to true Occultist
|
|
157
|
+
* will use the standard `X-Cache` header and the value `HIT`. If a string is
|
|
158
|
+
* provided the header name will be set to the value of the string. If an array
|
|
159
|
+
* is provided the header name will be set to the first item in the array, and
|
|
160
|
+
* the header value the second. Occultist does not set the cache header on
|
|
161
|
+
* cache misses. By default Occultist will not set a cache hit header.
|
|
162
|
+
*
|
|
163
|
+
* @param args.serverTiming Enables server timing headers in responses. When
|
|
164
|
+
* enabled requests log the duration of the steps Occultist takes when
|
|
165
|
+
* finding the action to respond to the request, retrieving values from
|
|
166
|
+
* cache, or calling the handler functions of an action. Browser debug tools
|
|
167
|
+
* add these values to their network performance charts.
|
|
168
|
+
* Enabling server timing can leak information and is not recommended for
|
|
169
|
+
* production environments.
|
|
170
|
+
*/
|
|
102
171
|
export class Registry<
|
|
103
172
|
State extends ContextState = ContextState,
|
|
104
173
|
> implements Callable<State> {
|
|
@@ -107,6 +176,7 @@ export class Registry<
|
|
|
107
176
|
#path: string;
|
|
108
177
|
#rootIRI: string;
|
|
109
178
|
#serverTiming: boolean;
|
|
179
|
+
#cacheHitHeader: CacheHitHeader;
|
|
110
180
|
#http: HTTP<State>;
|
|
111
181
|
#scopes: Scope[] = [];
|
|
112
182
|
#children: ActionMeta[] = [];
|
|
@@ -123,6 +193,7 @@ export class Registry<
|
|
|
123
193
|
this.#rootIRI = args.rootIRI;
|
|
124
194
|
this.#path = url.pathname;
|
|
125
195
|
this.#serverTiming = args.serverTiming ?? false;
|
|
196
|
+
this.#cacheHitHeader = args.cacheHitHeader ?? false;
|
|
126
197
|
this.#http = new HTTP<State>(this);
|
|
127
198
|
}
|
|
128
199
|
|
|
@@ -395,6 +466,10 @@ export class Registry<
|
|
|
395
466
|
req: Request | IncomingMessage,
|
|
396
467
|
res?: ServerResponse,
|
|
397
468
|
): Promise<Response | ServerResponse> {
|
|
469
|
+
if (!this.#finalized) {
|
|
470
|
+
this.finalize();
|
|
471
|
+
}
|
|
472
|
+
|
|
398
473
|
const startTime = performance.now();
|
|
399
474
|
const wrapped = new WrappedRequest(this.#rootIRI, req);
|
|
400
475
|
const writer = new ResponseWriter(res);
|
|
@@ -415,12 +490,14 @@ export class Registry<
|
|
|
415
490
|
req: wrapped,
|
|
416
491
|
writer,
|
|
417
492
|
startTime,
|
|
493
|
+
cacheHitHeader: this.#cacheHitHeader,
|
|
418
494
|
});
|
|
419
495
|
}
|
|
420
496
|
} catch (err2) {
|
|
421
497
|
if (err2 instanceof ProblemDetailsError) {
|
|
422
498
|
err = err2;
|
|
423
499
|
} else {
|
|
500
|
+
console.log(err2);
|
|
424
501
|
err = new ProblemDetailsError(500, 'Internal server error');
|
|
425
502
|
}
|
|
426
503
|
}
|
|
@@ -430,17 +507,17 @@ export class Registry<
|
|
|
430
507
|
}
|
|
431
508
|
|
|
432
509
|
if (err instanceof ProblemDetailsError && req instanceof Request) {
|
|
433
|
-
return new Response(err.toContent('application/problem
|
|
510
|
+
return new Response(err.toContent('application/problem.json'), {
|
|
434
511
|
status: err.status,
|
|
435
512
|
headers: {
|
|
436
|
-
'Content-Type': 'application/problem
|
|
513
|
+
'Content-Type': 'application/problem.json',
|
|
437
514
|
},
|
|
438
515
|
});
|
|
439
516
|
} else if (err instanceof ProblemDetailsError && res != null) {
|
|
440
517
|
res.writeHead(err.status, {
|
|
441
|
-
'Content-Type': 'application/problem
|
|
518
|
+
'Content-Type': 'application/problem.json',
|
|
442
519
|
});
|
|
443
|
-
res.end(err.toContent('application/problem
|
|
520
|
+
res.end(err.toContent('application/problem.json'));
|
|
444
521
|
return res;
|
|
445
522
|
}
|
|
446
523
|
}
|
package/lib/request.ts
CHANGED
package/lib/scopes.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import test from 'node:test';
|
|
3
|
-
import { Registry } from "./registry.
|
|
4
|
-
import { makeTypeDef, makeTypeDefs } from "./makeTypeDefs.
|
|
3
|
+
import { Registry } from "./registry.ts";
|
|
4
|
+
import { makeTypeDef, makeTypeDefs } from "./makeTypeDefs.ts";
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
const typeDefs = makeTypeDefs([
|
package/lib/scopes.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { joinPaths } from "./utils/joinPaths.
|
|
2
|
-
import { ActionAuth, HandlerDefinition } from "./actions/actions.
|
|
3
|
-
import { ActionMeta } from "./actions/meta.
|
|
4
|
-
import type { ContextState } from "./actions/spec.
|
|
5
|
-
import type { ImplementedAction } from "./actions/types.
|
|
6
|
-
import type { HTTPWriter } from "./actions/writer.
|
|
7
|
-
import { type Callable, HTTP, type Registry } from './registry.
|
|
1
|
+
import { joinPaths } from "./utils/joinPaths.ts"
|
|
2
|
+
import { ActionAuth, HandlerDefinition } from "./actions/actions.ts";
|
|
3
|
+
import { ActionMeta } from "./actions/meta.ts";
|
|
4
|
+
import type { ContextState } from "./actions/spec.ts";
|
|
5
|
+
import type { AuthMiddleware, ImplementedAction } from "./actions/types.ts";
|
|
6
|
+
import type { HTTPWriter } from "./actions/writer.ts";
|
|
7
|
+
import { type Callable, HTTP, type Registry } from './registry.ts';
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
export type MetaPropatator = (meta: ActionMeta) => void;
|
|
@@ -28,6 +28,7 @@ export class Scope<
|
|
|
28
28
|
#http: HTTP<State>;
|
|
29
29
|
#children: Array<ActionMeta> = [];
|
|
30
30
|
#public: boolean = true;
|
|
31
|
+
#auth: AuthMiddleware | undefined;
|
|
31
32
|
#propergateMeta: (meta: ActionMeta) => void;
|
|
32
33
|
|
|
33
34
|
constructor({
|
|
@@ -73,14 +74,16 @@ export class Scope<
|
|
|
73
74
|
return this.actions.flatMap((action) => action.handlers);
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
public(): Scope<State> {
|
|
77
|
+
public(authMiddleware?: AuthMiddleware): Scope<State> {
|
|
77
78
|
this.#public = true;
|
|
79
|
+
this.#auth = authMiddleware;
|
|
78
80
|
|
|
79
81
|
return this;
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
private(): Scope<State> {
|
|
84
|
+
private(authMiddleware: AuthMiddleware): Scope<State> {
|
|
83
85
|
this.#public = false;
|
|
86
|
+
this.#auth = authMiddleware;
|
|
84
87
|
|
|
85
88
|
return this;
|
|
86
89
|
}
|
|
@@ -161,13 +164,13 @@ export class Scope<
|
|
|
161
164
|
|
|
162
165
|
if (this.#public) {
|
|
163
166
|
this.#registry.http.get('scope-action', joinPaths(this.url(), action.name))
|
|
164
|
-
.public()
|
|
167
|
+
.public(this.#auth)
|
|
165
168
|
.handle('application/ld+json', async (ctx) => {
|
|
166
169
|
ctx.body = JSON.stringify(await action.jsonld());
|
|
167
170
|
});
|
|
168
171
|
} else {
|
|
169
172
|
this.#registry.http.get('scope-action', joinPaths(this.url(), action.name))
|
|
170
|
-
.private()
|
|
173
|
+
.private(this.#auth)
|
|
171
174
|
.handle('application/ld+json', async (ctx) => {
|
|
172
175
|
ctx.body = JSON.stringify(await action.jsonld());
|
|
173
176
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { JSONLDContext, ContextVersion, TypeDef } from "../jsonld.
|
|
2
|
-
import { isNil } from "./isNil.
|
|
3
|
-
import { isPopulatedObject } from "./isPopulatedObject.
|
|
1
|
+
import type { JSONLDContext, ContextVersion, TypeDef } from "../jsonld.ts";
|
|
2
|
+
import { isNil } from "./isNil.ts";
|
|
3
|
+
import { isPopulatedObject } from "./isPopulatedObject.ts";
|
|
4
4
|
|
|
5
5
|
export function contextBuilder({
|
|
6
6
|
vocab,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { JSONLDContext, TypeDef } from "../jsonld.
|
|
2
|
-
import { makeTypeDef, makeTypeDefs } from "../makeTypeDefs.
|
|
3
|
-
import { contextBuilder } from "./contextBuilder.
|
|
4
|
-
import type { ActionSpec, PropertySpec } from '../actions/spec.
|
|
1
|
+
import type { JSONLDContext, TypeDef } from "../jsonld.ts";
|
|
2
|
+
import { makeTypeDef, makeTypeDefs } from "../makeTypeDefs.ts";
|
|
3
|
+
import { contextBuilder } from "./contextBuilder.ts";
|
|
4
|
+
import type { ActionSpec, PropertySpec } from '../actions/spec.ts';
|
|
5
5
|
|
|
6
6
|
const defaultTypeDefs = makeTypeDefs([
|
|
7
7
|
makeTypeDef({ schema: 'https://schema.org/', term: 'Entrypoint' }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { OrArray, JSONValue } from "../jsonld.
|
|
2
|
-
import type { ActionSpec, PropertySpec, ResponseInputSpec, SpecValue, ActionOption } from "../actions/spec.
|
|
3
|
-
import { isNil } from "./isNil.
|
|
4
|
-
import { isObject } from "./isObject.
|
|
1
|
+
import type { OrArray, JSONValue } from "../jsonld.ts";
|
|
2
|
+
import type { ActionSpec, PropertySpec, ResponseInputSpec, SpecValue, ActionOption } from "../actions/spec.ts";
|
|
3
|
+
import { isNil } from "./isNil.ts";
|
|
4
|
+
import { isObject } from "./isObject.ts";
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
// deno-lint-ignore no-explicit-any
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ProblemDetailsError } from "../errors.
|
|
2
|
-
import type { JSONValue,
|
|
3
|
-
import type { ContextState, ActionSpec, PropertySpec } from "../actions/spec.
|
|
4
|
-
import jsonld from 'jsonld';
|
|
5
|
-
import type { ImplementedAction } from "../actions/types.
|
|
1
|
+
import { ProblemDetailsError } from "../errors.ts";
|
|
2
|
+
import type { JSONValue, JSONObject } from "../jsonld.ts";
|
|
3
|
+
import type { ContextState, ActionSpec, PropertySpec } from "../actions/spec.ts";
|
|
4
|
+
import jsonld, {type ContextDefinition} from 'jsonld';
|
|
5
|
+
import type { ImplementedAction } from "../actions/types.ts";
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
// export type BodyValue = Record<string, FileInput | FileInput[] | JSONValue>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { ImplementedAction } from "../actions/types.
|
|
2
|
-
import { ProblemDetailsError } from "../errors.
|
|
3
|
-
import type { ActionSpec, ContextState, FileSingleSpec, FileMultiSpec, BooleanSingleSpec, BooleanMultiSpec, NumberSingleSpec, NumberMultiSpec, StringSingleSpec, StringMultiSpec, ParsedIRIValues, PropertySpec } from "../actions/spec.
|
|
4
|
-
import { getParamLocation } from "./getParamLocation.
|
|
1
|
+
import type { ImplementedAction } from "../actions/types.ts";
|
|
2
|
+
import { ProblemDetailsError } from "../errors.ts";
|
|
3
|
+
import type { ActionSpec, ContextState, FileSingleSpec, FileMultiSpec, BooleanSingleSpec, BooleanMultiSpec, NumberSingleSpec, NumberMultiSpec, StringSingleSpec, StringMultiSpec, ParsedIRIValues, PropertySpec } from "../actions/spec.ts";
|
|
4
|
+
import { getParamLocation } from "./getParamLocation.ts";
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
export type IRIValue<
|