@occultist/occultist 0.0.9 → 0.0.11

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.
@@ -1,4 +1,4 @@
1
- import type { CacheOperation, HandlerDefinition } from "../mod.ts";
1
+ import type { CacheOperation, HandlerDefinition, StaticAsset } from "../mod.ts";
2
2
  import type { Registry } from "../registry.ts";
3
3
  import type { ActionPayload, ActionSpec, ContextState, ParsedIRIValues } from "./spec.ts";
4
4
  import type { AuthState, ImplementedAction } from "./types.ts";
@@ -84,5 +84,14 @@ export declare class Context<State extends ContextState = ContextState, Auth ext
84
84
  set status(status: number);
85
85
  get body(): undefined | ResponseBody;
86
86
  set body(body: ResponseBody);
87
+ /**
88
+ * Returns the public facing URL of a static asset using its
89
+ * static file alias.
90
+ *
91
+ * @param assetAlias The alias of the static asset.
92
+ * @param cspDirective A directive to add the asset to when generating CSP headers.
93
+ * @returns The public facing URL of the static asset.
94
+ */
95
+ useAsset(assetAlias: string, cspDirective?: string): StaticAsset | undefined;
87
96
  get [Symbol.toStringTag](): string;
88
97
  }
@@ -3,6 +3,8 @@ class EditableContext {
3
3
  etag;
4
4
  status;
5
5
  body;
6
+ staticAssets = new Map();
7
+ cspDirectives;
6
8
  }
7
9
  ;
8
10
  /**
@@ -121,6 +123,33 @@ export class Context {
121
123
  set body(body) {
122
124
  this.#editable.body = body;
123
125
  }
126
+ /**
127
+ * Returns the public facing URL of a static asset using its
128
+ * static file alias.
129
+ *
130
+ * @param assetAlias The alias of the static asset.
131
+ * @param cspDirective A directive to add the asset to when generating CSP headers.
132
+ * @returns The public facing URL of the static asset.
133
+ */
134
+ useAsset(assetAlias, cspDirective) {
135
+ const staticAlias = assetAlias.split('/')[0];
136
+ const extension = this.registry.getStaticExtension(staticAlias);
137
+ if (extension == null)
138
+ return;
139
+ const asset = extension.getAsset(assetAlias);
140
+ if (asset == null)
141
+ return;
142
+ this.#editable.staticAssets.set(asset.alias, asset);
143
+ if (typeof cspDirective === 'string' && cspDirective != null) {
144
+ if (!this.#editable.cspDirectives.has(cspDirective)) {
145
+ this.#editable.cspDirectives.set(cspDirective, [asset.alias]);
146
+ }
147
+ else {
148
+ this.#editable.cspDirectives.get(cspDirective).push(asset.alias);
149
+ }
150
+ }
151
+ return asset;
152
+ }
124
153
  get [Symbol.toStringTag]() {
125
154
  return `action=${this.action.name} method=${this.method} contentType=${this.contentType}`;
126
155
  }
@@ -7,7 +7,7 @@ import { IncomingMessage, type ServerResponse } from "node:http";
7
7
  import type { Merge } from "./actions/spec.ts";
8
8
  import type { ContextState, Middleware } from "./actions/spec.ts";
9
9
  import { type CacheOperationResult } from "./mod.ts";
10
- import type { Extension } from "./types.ts";
10
+ import type { Extension, StaticExtension } from "./types.ts";
11
11
  export interface Callable<State extends ContextState = ContextState> {
12
12
  method(method: string, name: string, path: string): ActionAuth<State>;
13
13
  }
@@ -249,6 +249,13 @@ export declare class Registry<State extends ContextState = ContextState> impleme
249
249
  * @returns A NodeJS server response instance.
250
250
  */
251
251
  handleRequest(req: IncomingMessage, res: ServerResponse): Promise<ServerResponse>;
252
+ /**
253
+ * Retrieves a static extension by one of the static aliases it uses.
254
+ *
255
+ * @param staticAlias A static alias used to create paths to files served
256
+ * by the static extension.
257
+ */
258
+ getStaticExtension(staticAlias: string): StaticExtension | undefined;
252
259
  /**
253
260
  * Registers an Occultist extension. This is usually done
254
261
  * by extensions when they are created.
package/dist/registry.js CHANGED
@@ -123,6 +123,7 @@ export class Registry {
123
123
  #actions = null;
124
124
  #handlers = null;
125
125
  #extensions = [];
126
+ #staticExtensions = new Map();
126
127
  constructor(args) {
127
128
  const url = new URL(args.rootIRI);
128
129
  this.#rootIRI = args.rootIRI;
@@ -500,6 +501,15 @@ export class Registry {
500
501
  return res;
501
502
  }
502
503
  }
504
+ /**
505
+ * Retrieves a static extension by one of the static aliases it uses.
506
+ *
507
+ * @param staticAlias A static alias used to create paths to files served
508
+ * by the static extension.
509
+ */
510
+ getStaticExtension(staticAlias) {
511
+ return this.#staticExtensions.get(staticAlias);
512
+ }
503
513
  /**
504
514
  * Registers an Occultist extension. This is usually done
505
515
  * by extensions when they are created.
@@ -507,6 +517,17 @@ export class Registry {
507
517
  * @param The Occultist extension to register.
508
518
  */
509
519
  registerExtension(extension) {
520
+ let staticAlias;
521
+ if (typeof extension.getAsset === 'function' &&
522
+ Array.isArray(extension.staticAliases)) {
523
+ for (let i = 0; i < extension.staticAliases.length; i++) {
524
+ staticAlias = extension.staticAliases[i];
525
+ if (this.#staticExtensions.has(staticAlias)) {
526
+ throw new Error(`Static alias '${staticAlias}' already used by other extension`);
527
+ }
528
+ this.#staticExtensions.set(staticAlias, extension);
529
+ }
530
+ }
510
531
  this.#extensions.push(extension);
511
532
  }
512
533
  /**
package/dist/types.d.ts CHANGED
@@ -1,10 +1,50 @@
1
+ export interface StaticAsset {
2
+ alias: string;
3
+ contentType: string;
4
+ url: string;
5
+ integrity?: string;
6
+ }
1
7
  export interface StaticContext {
2
8
  link(alias: string, as: string): string;
3
9
  }
4
10
  export interface Extension {
11
+ /**
12
+ * The name of the extension.
13
+ */
5
14
  name: string;
15
+ /**
16
+ * Setup method which can perform async setup tasks
17
+ * and report status via a readable stream.
18
+ */
6
19
  setup?(): ReadableStream;
7
- createStaticContext?(): StaticContext;
20
+ /**
21
+ * Retrieves a static assets from the extension.
22
+ *
23
+ * @param assetAlias The alias for the asset.
24
+ */
25
+ getAsset?(assetAlias: string): StaticAsset | undefined;
26
+ /**
27
+ * Root level aliases the extension uses to identify
28
+ * the assets it manages.
29
+ */
30
+ staticAliases?: string[];
31
+ }
32
+ export interface StaticExtension {
33
+ /**
34
+ * The name of the extension.
35
+ */
36
+ name: string;
37
+ /**
38
+ * Root level aliases the extension uses to identify
39
+ * assets it manages.
40
+ */
41
+ staticAliases: string[];
42
+ /**
43
+ * Retrieves a static assets from the extension.
44
+ *
45
+ * @param assetAlias The alias for the asset.
46
+ */
47
+ getAsset(assetAlias: string): StaticAsset | undefined;
8
48
  }
9
49
  export type ProblemDetailsParam = {
10
50
  name: string;
package/dist/types.js CHANGED
@@ -1,2 +1,3 @@
1
1
  ;
2
+ ;
2
3
  export {};
@@ -1,4 +1,4 @@
1
- import type {CacheOperation, HandlerDefinition} from "../mod.ts";
1
+ import type {CacheOperation, HandlerDefinition, StaticAsset} from "../mod.ts";
2
2
  import type {Registry} from "../registry.ts";
3
3
  import type {ActionPayload, ActionSpec, ContextState, ParsedIRIValues} from "./spec.ts";
4
4
  import type {AuthState, ImplementedAction} from "./types.ts";
@@ -10,6 +10,8 @@ class EditableContext {
10
10
  etag?: string;
11
11
  status?: number;
12
12
  body?: ResponseBody;
13
+ staticAssets: Map<string, StaticAsset> = new Map();
14
+ cspDirectives: Map<string, string[]>;
13
15
  };
14
16
 
15
17
  export type CacheContextArgs<
@@ -187,6 +189,37 @@ export class Context<
187
189
  this.#editable.body = body;
188
190
  }
189
191
 
192
+ /**
193
+ * Returns the public facing URL of a static asset using its
194
+ * static file alias.
195
+ *
196
+ * @param assetAlias The alias of the static asset.
197
+ * @param cspDirective A directive to add the asset to when generating CSP headers.
198
+ * @returns The public facing URL of the static asset.
199
+ */
200
+ useAsset(assetAlias: string, cspDirective?: string): StaticAsset | undefined {
201
+ const staticAlias = assetAlias.split('/')[0];
202
+ const extension = this.registry.getStaticExtension(staticAlias);
203
+
204
+ if (extension == null) return;
205
+
206
+ const asset = extension.getAsset(assetAlias);
207
+
208
+ if (asset == null) return;
209
+
210
+ this.#editable.staticAssets.set(asset.alias, asset);
211
+
212
+ if (typeof cspDirective === 'string' && cspDirective != null) {
213
+ if (!this.#editable.cspDirectives.has(cspDirective)) {
214
+ this.#editable.cspDirectives.set(cspDirective, [asset.alias]);
215
+ } else {
216
+ this.#editable.cspDirectives.get(cspDirective).push(asset.alias);
217
+ }
218
+ }
219
+
220
+ return asset;
221
+ }
222
+
190
223
  get [Symbol.toStringTag]() {
191
224
  return `action=${this.action.name} method=${this.method} contentType=${this.contentType}`;
192
225
  }
package/lib/registry.ts CHANGED
@@ -11,7 +11,7 @@ import type { ContextState, Middleware } from "./actions/spec.ts";
11
11
  import {ProblemDetailsError} from "./errors.ts"
12
12
  import {WrappedRequest} from "./request.ts";
13
13
  import {type CacheOperationResult} from "./mod.ts";
14
- import type {Extension} from "./types.ts";
14
+ import type {Extension, StaticExtension} from "./types.ts";
15
15
 
16
16
 
17
17
  export interface Callable<
@@ -189,6 +189,7 @@ export class Registry<
189
189
  #actions: ImplementedAction[] | null = null;
190
190
  #handlers: HandlerDefinition[] | null = null;
191
191
  #extensions: Extension[] = [];
192
+ #staticExtensions: Map<string, StaticExtension> = new Map();
192
193
 
193
194
  constructor(args: RegistryArgs) {
194
195
  const url = new URL(args.rootIRI);
@@ -733,6 +734,16 @@ export class Registry<
733
734
  }
734
735
  }
735
736
 
737
+ /**
738
+ * Retrieves a static extension by one of the static aliases it uses.
739
+ *
740
+ * @param staticAlias A static alias used to create paths to files served
741
+ * by the static extension.
742
+ */
743
+ getStaticExtension(staticAlias: string): StaticExtension | undefined {
744
+ return this.#staticExtensions.get(staticAlias);
745
+ }
746
+
736
747
  /**
737
748
  * Registers an Occultist extension. This is usually done
738
749
  * by extensions when they are created.
@@ -740,6 +751,22 @@ export class Registry<
740
751
  * @param The Occultist extension to register.
741
752
  */
742
753
  registerExtension(extension: Extension): void {
754
+ let staticAlias: string;
755
+ if (
756
+ typeof extension.getAsset === 'function' &&
757
+ Array.isArray(extension.staticAliases)
758
+ ) {
759
+ for (let i = 0; i < extension.staticAliases.length; i++) {
760
+ staticAlias = extension.staticAliases[i];
761
+
762
+ if (this.#staticExtensions.has(staticAlias)) {
763
+ throw new Error(`Static alias '${staticAlias}' already used by other extension`);
764
+ }
765
+
766
+ this.#staticExtensions.set(staticAlias, extension as StaticExtension);
767
+ }
768
+ }
769
+
743
770
  this.#extensions.push(extension);
744
771
  }
745
772
 
package/lib/types.ts CHANGED
@@ -1,14 +1,65 @@
1
1
 
2
+ export interface StaticAsset {
3
+ alias: string;
4
+ contentType: string;
5
+ url: string;
6
+ integrity?: string;
7
+ };
8
+
2
9
  export interface StaticContext {
3
10
  link(alias: string, as: string): string;
4
11
  }
5
12
 
6
13
  export interface Extension {
14
+
15
+ /**
16
+ * The name of the extension.
17
+ */
7
18
  name: string;
19
+
20
+ /**
21
+ * Setup method which can perform async setup tasks
22
+ * and report status via a readable stream.
23
+ */
8
24
  setup?(): ReadableStream;
9
- createStaticContext?(): StaticContext;
25
+
26
+ /**
27
+ * Retrieves a static assets from the extension.
28
+ *
29
+ * @param assetAlias The alias for the asset.
30
+ */
31
+ getAsset?(assetAlias: string): StaticAsset | undefined;
32
+
33
+ /**
34
+ * Root level aliases the extension uses to identify
35
+ * the assets it manages.
36
+ */
37
+ staticAliases?: string[];
38
+
10
39
  };
11
40
 
41
+ export interface StaticExtension {
42
+
43
+ /**
44
+ * The name of the extension.
45
+ */
46
+ name: string;
47
+
48
+ /**
49
+ * Root level aliases the extension uses to identify
50
+ * assets it manages.
51
+ */
52
+ staticAliases: string[];
53
+
54
+ /**
55
+ * Retrieves a static assets from the extension.
56
+ *
57
+ * @param assetAlias The alias for the asset.
58
+ */
59
+ getAsset(assetAlias: string): StaticAsset | undefined;
60
+
61
+ }
62
+
12
63
  export type ProblemDetailsParam = {
13
64
  name: string;
14
65
  reason: string;
package/package.json CHANGED
@@ -35,7 +35,7 @@
35
35
  "jsonld": "^9.0.0",
36
36
  "typescript": "^5.9.3"
37
37
  },
38
- "version": "0.0.9",
38
+ "version": "0.0.11",
39
39
  "scripts": {
40
40
  "build": "tsc -p tsconfig.build.json",
41
41
  "test": "node --test"