@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.
Files changed (82) hide show
  1. package/dist/accept.js +0 -1
  2. package/dist/actions/actionSets.d.ts +3 -3
  3. package/dist/actions/actions.d.ts +69 -48
  4. package/dist/actions/actions.js +39 -4
  5. package/dist/actions/context.d.ts +15 -11
  6. package/dist/actions/context.js +5 -0
  7. package/dist/actions/meta.d.ts +18 -12
  8. package/dist/actions/meta.js +114 -38
  9. package/dist/actions/spec.d.ts +3 -3
  10. package/dist/actions/types.d.ts +45 -16
  11. package/dist/actions/writer.d.ts +1 -1
  12. package/dist/actions/writer.test.js +2 -2
  13. package/dist/cache/cache.d.ts +3 -3
  14. package/dist/cache/cache.js +111 -42
  15. package/dist/cache/etag.test.js +1 -1
  16. package/dist/cache/file.d.ts +33 -1
  17. package/dist/cache/file.js +92 -10
  18. package/dist/cache/memory.d.ts +12 -2
  19. package/dist/cache/memory.js +63 -1
  20. package/dist/cache/types.d.ts +51 -22
  21. package/dist/errors.d.ts +1 -1
  22. package/dist/jsonld.d.ts +1 -1
  23. package/dist/makeTypeDefs.d.ts +2 -2
  24. package/dist/mod.d.ts +17 -15
  25. package/dist/mod.js +17 -15
  26. package/dist/processAction.d.ts +2 -2
  27. package/dist/processAction.js +1 -1
  28. package/dist/registry.d.ts +74 -8
  29. package/dist/registry.js +70 -8
  30. package/dist/registry.test.js +1 -1
  31. package/dist/scopes.d.ts +8 -8
  32. package/dist/scopes.js +8 -5
  33. package/dist/utils/contextBuilder.d.ts +1 -1
  34. package/dist/utils/getActionContext.d.ts +2 -2
  35. package/dist/utils/getPropertyValueSpecifications.d.ts +1 -1
  36. package/dist/utils/getRequestBodyValues.d.ts +3 -3
  37. package/dist/utils/getRequestIRIValues.d.ts +2 -2
  38. package/dist/utils/isPopulatedObject.js +1 -1
  39. package/dist/utils/makeAppendProblemDetails.d.ts +1 -1
  40. package/dist/utils/makeURLPattern.js +1 -0
  41. package/dist/utils/parseSearchParams.d.ts +2 -2
  42. package/dist/validators.d.ts +2 -2
  43. package/dist/validators.js +2 -2
  44. package/lib/accept.test.ts +1 -1
  45. package/lib/accept.ts +0 -2
  46. package/lib/actions/actionSets.ts +4 -4
  47. package/lib/actions/actions.ts +159 -99
  48. package/lib/actions/context.ts +22 -10
  49. package/lib/actions/meta.ts +140 -55
  50. package/lib/actions/path.test.ts +1 -1
  51. package/lib/actions/path.ts +1 -1
  52. package/lib/actions/spec.ts +3 -3
  53. package/lib/actions/types.ts +60 -15
  54. package/lib/actions/writer.test.ts +2 -2
  55. package/lib/actions/writer.ts +1 -1
  56. package/lib/cache/cache.ts +138 -52
  57. package/lib/cache/etag.test.ts +1 -1
  58. package/lib/cache/file.ts +113 -12
  59. package/lib/cache/memory.ts +85 -3
  60. package/lib/cache/types.ts +70 -23
  61. package/lib/errors.ts +1 -1
  62. package/lib/jsonld.ts +1 -1
  63. package/lib/makeTypeDefs.ts +5 -5
  64. package/lib/mod.ts +17 -15
  65. package/lib/processAction.ts +14 -14
  66. package/lib/registry.test.ts +1 -1
  67. package/lib/registry.ts +96 -19
  68. package/lib/request.ts +1 -1
  69. package/lib/scopes.test.ts +2 -2
  70. package/lib/scopes.ts +14 -11
  71. package/lib/utils/contextBuilder.ts +3 -3
  72. package/lib/utils/getActionContext.ts +4 -4
  73. package/lib/utils/getInternalName.ts +1 -1
  74. package/lib/utils/getPropertyValueSpecifications.ts +4 -4
  75. package/lib/utils/getRequestBodyValues.ts +5 -5
  76. package/lib/utils/getRequestIRIValues.ts +4 -4
  77. package/lib/utils/isPopulatedObject.ts +1 -1
  78. package/lib/utils/makeAppendProblemDetails.ts +1 -1
  79. package/lib/utils/makeURLPattern.ts +1 -0
  80. package/lib/utils/parseSearchParams.ts +2 -2
  81. package/lib/validators.ts +5 -5
  82. package/package.json +4 -2
@@ -1,5 +1,5 @@
1
- import {ImplementedAction} from "../actions/types.js";
2
- import {CacheContext} from "../mod.js";
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
- ctx: CacheContext,
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
- public?: true;
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
- & { stratey: 'etag', cache: CacheBuilder }
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: 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 A unique key for this representation.
165
- * @param details The cache 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 A unique key for this representation.
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
- * Any other requests for this representation will wait for the request
181
- * holding the lock to populate the cache before proceeding.
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 A unique key for this representation.
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
- * Invalidates a cache entry.
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
- invalidate?(request: Request): Promise<void>;
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
@@ -1,4 +1,4 @@
1
- import {ProblemDetails} from "./types.js";
1
+ import type {ProblemDetails} from "./types.ts";
2
2
 
3
3
  // https://datatracker.ietf.org/doc/html/rfc9457
4
4
  export class ProblemDetailsError extends Error {
package/lib/jsonld.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ShallowMerge } from "./merge.js";
1
+ import type { ShallowMerge } from "./merge.ts";
2
2
 
3
3
  // Union of numbers within our depth limit
4
4
  export type RecursiveDigit = 1 | 2 | 3 | 4 | 5 | 6 | 7;
@@ -1,8 +1,8 @@
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";
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.js';
2
- export * from './cache/cache.js';
3
- export * from './cache/memory.js';
4
- export * from './cache/file.js';
5
- export * from './actions/types.js';
6
- export * from './actions/meta.js';
7
- export * from './actions/spec.js';
8
- export * from './actions/context.js';
9
- export * from './actions/path.js';
10
- export * from './actions/actions.js';
11
- export * from './actions/actionSets.js';
12
- export * from './registry.js';
13
- export * from './makeTypeDefs.js';
14
- export * from './utils/joinPaths.js';
15
- export * from './utils/contextBuilder.js';
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';
@@ -1,17 +1,17 @@
1
- import { JsonPointer } from 'json-ptr';
2
- import type { JSONValue } from "./jsonld.js";
3
- import { getInternalName } from "./utils/getInternalName.js";
4
- import { type BodyValue, getRequestBodyValues } from "./utils/getRequestBodyValues.js";
5
- import { type IRIValue, getRequestIRIValues } from "./utils/getRequestIRIValues.js";
6
- import { isNil } from "./utils/isNil.js";
7
- import { isObject } from "./utils/isObject.js";
8
- import { isPopulatedObject } from "./utils/isPopulatedObject.js";
9
- import { type ProblemDetailsParamsRefs, makeAppendProblemDetails } from "./utils/makeAppendProblemDetails.js";
10
- import { failsRequiredRequirement, failsTypeRequirement, failsContentTypeRequirement, failsMaxValue, failsMinValue, failsValueMinLength, failsValueMaxLength, failsStepValue, failsPatternValue, failsValidator, isObjectArraySpec, isObjectSpec, isArraySpec } from "./validators.js";
11
- import { InvalidActionParamsError, ProblemDetailsError } from "./errors.js";
12
- import { alwaysArray } from "./utils/alwaysArray.js";
13
- import type { ImplementedAction } from "./actions/types.js";
14
- import type { ActionPayload, ActionSpec, ArraySpec, ContextState, ObjectArraySpec, ObjectSpec, ParsedIRIValues, PropertySpec, SpecValue, ValueSpec } from "./actions/spec.js";
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
 
@@ -1,6 +1,6 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import test from 'node:test';
3
- import { Registry } from './registry.js';
3
+ import { Registry } from './registry.ts';
4
4
  import { createServer } from 'node:http';
5
5
  import type { AddressInfo } from 'node:net';
6
6
 
package/lib/registry.ts CHANGED
@@ -1,15 +1,15 @@
1
- import { Accept } from "./accept.js";
2
- import { ActionAuth, HandlerDefinition } from "./actions/actions.js";
3
- import { type ActionMatchResult, ActionSet } from "./actions/actionSets.js";
4
- import { ActionMeta } from "./actions/meta.js";
5
- import type { ImplementedAction } from "./actions/types.js";
6
- import { ResponseWriter } from "./actions/writer.js";
7
- import { Scope } from './scopes.js';
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.js";
10
- import type { ContextState, Middleware } from "./actions/spec.js";
11
- import {ProblemDetailsError} from "./errors.js"
12
- import {WrappedRequest} from "./request.js";
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+json'), {
510
+ return new Response(err.toContent('application/problem.json'), {
434
511
  status: err.status,
435
512
  headers: {
436
- 'Content-Type': 'application/problem+json',
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+json',
518
+ 'Content-Type': 'application/problem.json',
442
519
  });
443
- res.end(err.toContent('application/problem+json'));
520
+ res.end(err.toContent('application/problem.json'));
444
521
  return res;
445
522
  }
446
523
  }
package/lib/request.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import {IncomingMessage} from "node:http";
2
2
  import {Readable} from "node:stream";
3
- import {normalizeURL} from "./utils/normalizeURL.js";
3
+ import {normalizeURL} from "./utils/normalizeURL.ts";
4
4
 
5
5
 
6
6
  export class WrappedRequest implements Request {
@@ -1,7 +1,7 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import test from 'node:test';
3
- import { Registry } from "./registry.js";
4
- import { makeTypeDef, makeTypeDefs } from "./makeTypeDefs.js";
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.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';
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.js";
2
- import { isNil } from "./isNil.js";
3
- import { isPopulatedObject } from "./isPopulatedObject.js";
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.js";
2
- import { makeTypeDef, makeTypeDefs } from "../makeTypeDefs.js";
3
- import { contextBuilder } from "./contextBuilder.js";
4
- import type { ActionSpec, PropertySpec } from '../actions/spec.js';
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,4 +1,4 @@
1
- import { isPopulatedString } from "./isPopulatedString.js";
1
+ import { isPopulatedString } from "./isPopulatedString.ts";
2
2
 
3
3
  export function getInternalName({
4
4
  paramName,
@@ -1,7 +1,7 @@
1
- import type { OrArray, JSONValue } from "../jsonld.js";
2
- import type { ActionSpec, PropertySpec, ResponseInputSpec, SpecValue, ActionOption } from "../actions/spec.js";
3
- import { isNil } from "./isNil.js";
4
- import { isObject } from "./isObject.js";
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.js";
2
- import type { JSONValue, ContextDefinition, JSONObject } from "../jsonld.js";
3
- import type { ContextState, ActionSpec, PropertySpec } from "../actions/spec.js";
4
- import jsonld from 'jsonld';
5
- import type { ImplementedAction } from "../actions/types.js";
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.js";
2
- import { ProblemDetailsError } from "../errors.js";
3
- import type { ActionSpec, ContextState, FileSingleSpec, FileMultiSpec, BooleanSingleSpec, BooleanMultiSpec, NumberSingleSpec, NumberMultiSpec, StringSingleSpec, StringMultiSpec, ParsedIRIValues, PropertySpec } from "../actions/spec.js";
4
- import { getParamLocation } from "./getParamLocation.js";
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<