@helia/verified-fetch 2.4.0 → 2.5.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.
- package/README.md +192 -0
- package/dist/index.min.js +354 -32
- package/dist/src/index.d.ts +198 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +192 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/plugins/errors.d.ts +25 -0
- package/dist/src/plugins/errors.d.ts.map +1 -0
- package/dist/src/plugins/errors.js +33 -0
- package/dist/src/plugins/errors.js.map +1 -0
- package/dist/src/plugins/index.d.ts +8 -0
- package/dist/src/plugins/index.d.ts.map +1 -0
- package/dist/src/plugins/index.js +7 -0
- package/dist/src/plugins/index.js.map +1 -0
- package/dist/src/plugins/plugin-base.d.ts +19 -0
- package/dist/src/plugins/plugin-base.d.ts.map +1 -0
- package/dist/src/plugins/plugin-base.js +26 -0
- package/dist/src/plugins/plugin-base.js.map +1 -0
- package/dist/src/plugins/plugin-handle-car.d.ts +11 -0
- package/dist/src/plugins/plugin-handle-car.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-car.js +28 -0
- package/dist/src/plugins/plugin-handle-car.js.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts +11 -0
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-cbor.js +73 -0
- package/dist/src/plugins/plugin-handle-dag-cbor.js.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts +15 -0
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-pb.js +152 -0
- package/dist/src/plugins/plugin-handle-dag-pb.js.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-walk.d.ts +16 -0
- package/dist/src/plugins/plugin-handle-dag-walk.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-walk.js +45 -0
- package/dist/src/plugins/plugin-handle-dag-walk.js.map +1 -0
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts +9 -0
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-dir-index-html.js +42 -0
- package/dist/src/plugins/plugin-handle-dir-index-html.js.map +1 -0
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts +12 -0
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-ipns-record.js +62 -0
- package/dist/src/plugins/plugin-handle-ipns-record.js.map +1 -0
- package/dist/src/plugins/plugin-handle-json.d.ts +11 -0
- package/dist/src/plugins/plugin-handle-json.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-json.js +51 -0
- package/dist/src/plugins/plugin-handle-json.js.map +1 -0
- package/dist/src/plugins/plugin-handle-raw.d.ts +8 -0
- package/dist/src/plugins/plugin-handle-raw.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-raw.js +80 -0
- package/dist/src/plugins/plugin-handle-raw.js.map +1 -0
- package/dist/src/plugins/plugin-handle-tar.d.ts +12 -0
- package/dist/src/plugins/plugin-handle-tar.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-tar.js +36 -0
- package/dist/src/plugins/plugin-handle-tar.js.map +1 -0
- package/dist/src/plugins/plugins.d.ts +5 -0
- package/dist/src/plugins/plugins.d.ts.map +1 -0
- package/dist/src/plugins/plugins.js +5 -0
- package/dist/src/plugins/plugins.js.map +1 -0
- package/dist/src/plugins/types.d.ts +68 -0
- package/dist/src/plugins/types.d.ts.map +1 -0
- package/dist/src/plugins/types.js +2 -0
- package/dist/src/plugins/types.js.map +1 -0
- package/dist/src/types.d.ts +0 -27
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +1 -2
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/dir-index-html.d.ts +16 -0
- package/dist/src/utils/dir-index-html.d.ts.map +1 -0
- package/dist/src/utils/dir-index-html.js +384 -0
- package/dist/src/utils/dir-index-html.js.map +1 -0
- package/dist/src/utils/get-e-tag.d.ts +1 -1
- package/dist/src/utils/get-e-tag.d.ts.map +1 -1
- package/dist/src/utils/get-e-tag.js +18 -3
- package/dist/src/utils/get-e-tag.js.map +1 -1
- package/dist/src/utils/response-headers.d.ts.map +1 -1
- package/dist/src/utils/response-headers.js +4 -0
- package/dist/src/utils/response-headers.js.map +1 -1
- package/dist/src/utils/walk-path.d.ts +3 -2
- package/dist/src/utils/walk-path.d.ts.map +1 -1
- package/dist/src/utils/walk-path.js +1 -1
- package/dist/src/utils/walk-path.js.map +1 -1
- package/dist/src/verified-fetch.d.ts +6 -24
- package/dist/src/verified-fetch.d.ts.map +1 -1
- package/dist/src/verified-fetch.js +164 -387
- package/dist/src/verified-fetch.js.map +1 -1
- package/dist/typedoc-urls.json +32 -24
- package/package.json +6 -2
- package/src/index.ts +199 -0
- package/src/plugins/errors.ts +37 -0
- package/src/plugins/index.ts +8 -0
- package/src/plugins/plugin-base.ts +30 -0
- package/src/plugins/plugin-handle-car.ts +32 -0
- package/src/plugins/plugin-handle-dag-cbor.ts +84 -0
- package/src/plugins/plugin-handle-dag-pb.ts +168 -0
- package/src/plugins/plugin-handle-dag-walk.ts +53 -0
- package/src/plugins/plugin-handle-dir-index-html.ts +50 -0
- package/src/plugins/plugin-handle-ipns-record.ts +69 -0
- package/src/plugins/plugin-handle-json.ts +57 -0
- package/src/plugins/plugin-handle-raw.ts +92 -0
- package/src/plugins/plugin-handle-tar.ts +44 -0
- package/src/plugins/plugins.ts +4 -0
- package/src/plugins/types.ts +73 -0
- package/src/types.ts +0 -34
- package/src/utils/dir-index-html.ts +442 -0
- package/src/utils/get-e-tag.ts +20 -3
- package/src/utils/response-headers.ts +4 -0
- package/src/utils/walk-path.ts +3 -3
- package/src/verified-fetch.ts +187 -430
package/README.md
CHANGED
|
@@ -658,6 +658,198 @@ Known Errors that can be thrown:
|
|
|
658
658
|
3. `TypeError` - If the options argument is passed and is malformed.
|
|
659
659
|
4. `AbortError` - If the content request is aborted due to user aborting provided AbortSignal. Note that this is a `AbortError` from `@libp2p/interface` and not the standard `AbortError` from the Fetch API.
|
|
660
660
|
|
|
661
|
+
## Pluggability and Extensibility
|
|
662
|
+
|
|
663
|
+
Verified‑fetch can now be extended to alter how it handles requests by using plugins.
|
|
664
|
+
Plugins are classes that extend the `BasePlugin` class and implement the `VerifiedFetchPlugin`
|
|
665
|
+
interface. They are instantiated with `PluginOptions` when the `VerifiedFetch` class is created.
|
|
666
|
+
|
|
667
|
+
Each plugin must implement two methods:
|
|
668
|
+
|
|
669
|
+
- **`canHandle(context: PluginContext): boolean`**
|
|
670
|
+
Inspects the current `PluginContext` (which includes the CID, path, query, accept header, etc.)
|
|
671
|
+
and returns `true` if the plugin can operate on the current state of the request.
|
|
672
|
+
|
|
673
|
+
- **`handle(context: PluginContext): Promise<Response | null>`**
|
|
674
|
+
Performs the plugin’s work. It may:
|
|
675
|
+
- **Return a final `Response`**: This stops the pipeline immediately.
|
|
676
|
+
- **Return `null`**: This indicates that the plugin has only partially processed the request
|
|
677
|
+
(for example, by performing path walking or decoding) and the pipeline should continue.
|
|
678
|
+
- **Throw a `PluginError`**: This logs a non-fatal error and continues the pipeline.
|
|
679
|
+
- **Throw a `PluginFatalError`**: This logs a fatal error and stops the pipeline immediately.
|
|
680
|
+
|
|
681
|
+
Plugins are executed in a chain (a **plugin pipeline**):
|
|
682
|
+
|
|
683
|
+
1. **Initialization:**
|
|
684
|
+
- The `VerifiedFetch` class is instantiated with a list of plugins.
|
|
685
|
+
- When a request is made via the `fetch` method, the resource and options are parsed to
|
|
686
|
+
create a mutable `PluginContext` object.
|
|
687
|
+
|
|
688
|
+
2. **Pipeline Execution:**
|
|
689
|
+
|
|
690
|
+
- The pipeline repeatedly checks, up to a maximum number of passes (default = 3), which plugins
|
|
691
|
+
are currently able to handle the request by calling each plugin’s `canHandle()` method.
|
|
692
|
+
- Plugins that have not yet been called in the current run and return `true` for `canHandle()`
|
|
693
|
+
are invoked in sequence.
|
|
694
|
+
- If a plugin returns a final `Response` or throws a `PluginFatalError`, the pipeline immediately
|
|
695
|
+
stops and that response is returned.
|
|
696
|
+
- If a plugin returns `null`, it may have updated the context (for example, by
|
|
697
|
+
performing path walking), other plugins that said they `canHandle` will run.
|
|
698
|
+
- If no plugin modifies the context (i.e. no change to `context.modified`) and no final response is
|
|
699
|
+
produced after iterating through all plugins, the pipeline exits and a default “Not Supported”
|
|
700
|
+
response is returned.
|
|
701
|
+
|
|
702
|
+
**Diagram of the Plugin Pipeline:**
|
|
703
|
+
|
|
704
|
+
```mermaid
|
|
705
|
+
flowchart TD
|
|
706
|
+
A[Resource & Options] --> B[Parse into PluginContext]
|
|
707
|
+
B --> C[Plugin Pipeline]
|
|
708
|
+
subgraph IP[Iterative Passes max 3 passes]
|
|
709
|
+
C1[Check canHandle for each plugin]
|
|
710
|
+
C2[Call handle on ready plugins]
|
|
711
|
+
C3[Update PluginContext if partial work is done]
|
|
712
|
+
C1 --> C2
|
|
713
|
+
C2 --> C3
|
|
714
|
+
end
|
|
715
|
+
C --> IP
|
|
716
|
+
IP --> D[Final Response]
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
3. **Finalization:**
|
|
720
|
+
- After the pipeline completes, the resulting response & context is processed (e.g. headers such as ETag,
|
|
721
|
+
Cache‑Control, and Content‑Disposition are set) and returned.
|
|
722
|
+
|
|
723
|
+
Please see the original discussion on extensibility in [Issue #167](https://github.com/ipfs/helia-verified-fetch/issues/167).
|
|
724
|
+
|
|
725
|
+
***
|
|
726
|
+
|
|
727
|
+
### Extending Verified‑Fetch with Custom Plugins
|
|
728
|
+
|
|
729
|
+
To add your own plugin:
|
|
730
|
+
|
|
731
|
+
1. **Extend the BasePlugin:**
|
|
732
|
+
|
|
733
|
+
Create a new class that extends `BasePlugin` and implements:
|
|
734
|
+
|
|
735
|
+
- `canHandle(context: PluginContext): boolean`
|
|
736
|
+
- `handle(context: PluginContext): Promise<Response | null>`
|
|
737
|
+
|
|
738
|
+
## Example - custom plugin
|
|
739
|
+
|
|
740
|
+
```typescript
|
|
741
|
+
import { BasePlugin, type PluginContext, type VerifiedFetchPluginFactory, type PluginOptions } from '@helia/verified-fetch'
|
|
742
|
+
import { okResponse } from './dist/src/utils/responses.js'
|
|
743
|
+
|
|
744
|
+
export class MyCustomPlugin extends BasePlugin {
|
|
745
|
+
// Optionally, list any codec codes your plugin supports:
|
|
746
|
+
codes = [] //
|
|
747
|
+
|
|
748
|
+
canHandle(context: PluginContext): boolean {
|
|
749
|
+
// Only handle requests if the Accept header matches your custom type
|
|
750
|
+
// Or check context for pathDetails, custom values, etc...
|
|
751
|
+
return context.accept === 'application/vnd.my-custom-type'
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
async handle(context: PluginContext): Promise<Response | null> {
|
|
755
|
+
// Perform any partial processing here, e.g., modify the context:
|
|
756
|
+
context.customProcessed = true;
|
|
757
|
+
|
|
758
|
+
// If you are ready to finalize the response:
|
|
759
|
+
return new Response('Hello, world!', {
|
|
760
|
+
status: 200,
|
|
761
|
+
headers: {
|
|
762
|
+
'Content-Type': 'text/plain'
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
// Or, if further processing is needed by another plugin, simply return null.
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
export const myCustomPluginFactory: VerifiedFetchPluginFactory = (opts: PluginOptions) => new MyCustomPlugin(opts)
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
2. **Integrate Your Plugin:**
|
|
773
|
+
|
|
774
|
+
Add your custom plugin to Verified‑Fetch’s plugin list when instantiating Verified‑Fetch:
|
|
775
|
+
|
|
776
|
+
## Example - Integrate custom plugin
|
|
777
|
+
|
|
778
|
+
```typescript
|
|
779
|
+
import { createVerifiedFetch, type VerifiedFetchPluginFactory } from '@helia/verified-fetch'
|
|
780
|
+
import { createHelia } from 'helia'
|
|
781
|
+
|
|
782
|
+
const helia = await createHelia()
|
|
783
|
+
const plugins: VerifiedFetchPluginFactory[] = [
|
|
784
|
+
// myCustomPluginFactory
|
|
785
|
+
]
|
|
786
|
+
|
|
787
|
+
const fetch = await createVerifiedFetch(helia, { plugins })
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
***
|
|
791
|
+
|
|
792
|
+
### Error Handling in the Plugin Pipeline
|
|
793
|
+
|
|
794
|
+
Verified‑Fetch distinguishes between two types of errors thrown by plugins:
|
|
795
|
+
|
|
796
|
+
- **PluginError (Non‑Fatal):**
|
|
797
|
+
- Use this when your plugin encounters an issue that should be logged but does not prevent the pipeline
|
|
798
|
+
from continuing.
|
|
799
|
+
- When a plugin throws a `PluginError`, the error is logged and the pipeline continues with the next plugin.
|
|
800
|
+
|
|
801
|
+
- **PluginFatalError (Fatal):**
|
|
802
|
+
- Use this when a critical error occurs that should immediately abort the request.
|
|
803
|
+
- When a plugin throws a `PluginFatalError`, the pipeline immediately terminates and the provided error
|
|
804
|
+
response is returned.
|
|
805
|
+
|
|
806
|
+
## Example - Plugin error Handling
|
|
807
|
+
|
|
808
|
+
```typescript
|
|
809
|
+
import { PluginError, PluginFatalError } from '@helia/verified-fetch'
|
|
810
|
+
|
|
811
|
+
// async handle(context: PluginContext): Promise<Response | null> {
|
|
812
|
+
const recoverable = Math.random() > 0.5 // Use more sophisticated logic here ;)
|
|
813
|
+
if (recoverable === true) {
|
|
814
|
+
throw new PluginError('MY_CUSTOM_WARNING', 'A non‑fatal issue occurred', {
|
|
815
|
+
details: {
|
|
816
|
+
someKey: 'Additional details here'
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (recoverable === false) {
|
|
822
|
+
throw new PluginFatalError('MY_CUSTOM_FATAL', 'A critical error occurred', {
|
|
823
|
+
response: new Response('Something happened', { status: 500 }) // Required: supply your own error response
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// Otherwise, continue processing...
|
|
828
|
+
// }
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
### How the Plugin Pipeline Works
|
|
832
|
+
|
|
833
|
+
- **Shared Context:**
|
|
834
|
+
A mutable `PluginContext` is created for each request. It includes the parsed CID, path, query parameters,
|
|
835
|
+
accept header, and any other metadata. Plugins can update this context as they perform partial work (for example,
|
|
836
|
+
by doing path walking or decoding).
|
|
837
|
+
|
|
838
|
+
- **Iterative Processing:**
|
|
839
|
+
The pipeline repeatedly checks which plugins can currently handle the request by calling `canHandle(context)`.
|
|
840
|
+
- Plugins that perform partial processing update the context and return `null`, allowing subsequent passes by other plugins.
|
|
841
|
+
- Once a plugin is ready to finalize the response, it returns a final `Response` and the pipeline terminates.
|
|
842
|
+
|
|
843
|
+
- **No Strict Ordering:**
|
|
844
|
+
Plugins are invoked based solely on whether they can handle the current state of the context.
|
|
845
|
+
This means you do not have to specify a rigid order, each plugin simply checks the context and acts if appropriate.
|
|
846
|
+
|
|
847
|
+
- **Error Handling:**
|
|
848
|
+
- A thrown `PluginError` is considered non‑fatal and is logged, allowing the pipeline to continue.
|
|
849
|
+
- A thrown `PluginFatalError` immediately stops the pipeline and returns the error response.
|
|
850
|
+
|
|
851
|
+
For a detailed explanation of the pipeline, please refer to the discussion in [Issue #167](https://github.com/ipfs/helia-verified-fetch/issues/167).
|
|
852
|
+
|
|
661
853
|
# Install
|
|
662
854
|
|
|
663
855
|
```console
|