Skip to content

Commit

Permalink
feat: merge main into fallback branch (#22)
Browse files Browse the repository at this point in the history
* Abort on error (#19)

* feat: use controller from options if exists. abort fetch if error occurs.

* test: check if external abort controller is used

* build: move build output to dist/ folder

* fix: newline

* 0.1.1

* Build exports (#20)

* chore: rename file

* feat: add new entrypoint with exports.

Switch Saturn to named export

* build: expose entire module instead of just the default export

* docs: update README

* 0.2.0

* feat: include worker scopes when checking for browser runtime (#21)

* 0.3.0

---------

Co-authored-by: Eric Guan <guanzo91@gmail.com>
  • Loading branch information
AmeanAsad and guanzo authored Oct 16, 2023
1 parent d81038f commit a1ecd50
Show file tree
Hide file tree
Showing 12 changed files with 61 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules/
dist/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ npm install @filecoin-saturn/js-client
## Usage

```js
import Saturn from '@filecoin-saturn/js-client'
import { Saturn } from '@filecoin-saturn/js-client'

const client = new Saturn()

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@filecoin-saturn/js-client",
"version": "0.1.0",
"version": "0.3.0",
"description": "Filecoin Saturn Client",
"homepage": "https://github.com/filecoin-saturn/js-client",
"main": "src/index.js",
Expand Down
27 changes: 13 additions & 14 deletions src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { randomUUID } from './utils/uuid.js'
import { memoryStorage } from './storage/index.js'
import { getJWT } from './utils/jwt.js'
import { parseUrl, addHttpPrefix } from './utils/url.js'
import { isBrowserContext } from './utils/runtime.js'

export class Saturn {
/**
Expand All @@ -27,7 +28,7 @@ export class Saturn {
cdnURL: 'saturn.ms',
logURL: 'https://twb3qukm2i654i3tnvx36char40aymqq.lambda-url.us-west-2.on.aws/',
orchURL: 'https://orchestrator.strn.pl/nodes?maxNodes=100',
authURL: 'https://saturn.auth',
authURL: 'https://fz3dyeyxmebszwhuiky7vggmsu0rlkoy.lambda-url.us-west-2.on.aws/',
connectTimeout: 5_000,
downloadTimeout: 0
}, opts)
Expand All @@ -46,7 +47,7 @@ export class Saturn {
if (this.reportingLogs && this.hasPerformanceAPI) {
this._monitorPerformanceBuffer()
}

this.storage = this.opts.storage || memoryStorage()
this.loadNodesPromise = this._loadNodes(this.opts)
}

Expand All @@ -62,20 +63,23 @@ export class Saturn {
async fetchCID (cidPath, opts = {}) {
const [cid] = (cidPath ?? '').split('/')
CID.parse(cid)

const jwt = await getJWT(this.opts, this.storage)

const options = Object.assign({}, this.opts, { format: 'car', jwt }, opts)
const url = this.createRequestURL(cidPath, options)

const log = {
url,
startTime: new Date()
}

const controller = new AbortController()
const controller = options.controller ?? new AbortController()
const connectTimeout = setTimeout(() => {
controller.abort()
}, options.connectTimeout)

if (!this.isBrowser) {
if (!isBrowserContext) {
options.headers = {
...(options.headers || {}),
Authorization: 'Bearer ' + options.jwt
Expand Down Expand Up @@ -110,7 +114,7 @@ export class Saturn {
throw err
}

return { res, log }
return { res, controller, log }
}

/**
Expand Down Expand Up @@ -185,7 +189,7 @@ export class Saturn {
* @returns {Promise<AsyncIterable<Uint8Array>>}
*/
async * fetchContent (cidPath, opts = {}) {
const { res, log } = await this.fetchCID(cidPath, opts)
const { res, controller, log } = await this.fetchCID(cidPath, opts)

async function * metricsIterable (itr) {
log.numBytesSent = 0
Expand All @@ -201,6 +205,8 @@ export class Saturn {
yield * extractVerifiedContent(cidPath, itr)
} catch (err) {
log.error = err.message
controller.abort()

throw err
} finally {
this._finalizeLog(log)
Expand All @@ -214,17 +220,12 @@ export class Saturn {
* @param {('car'|'raw')} [opts.format]
* @param {number} [opts.connectTimeout=5000]
* @param {number} [opts.downloadTimeout=0]
* @param origin
* @returns {Promise<Uint8Array>}
*/
async fetchContentBuffer (cidPath, opts = {}) {
return await asyncIteratorToBuffer(this.fetchContent(cidPath, opts))
}

async * extractVerifiedContent (cidPath, carStream) {
yield * extractVerifiedContent(cidPath, carStream)
}

/**
*
* @param {string} cidPath
Expand All @@ -241,7 +242,7 @@ export class Saturn {
url.searchParams.set('dag-scope', 'entity')
}

if (this.isBrowser) {
if (isBrowserContext) {
url.searchParams.set('jwt', opts.jwt)
}

Expand Down Expand Up @@ -404,5 +405,3 @@ export class Saturn {
cacheNodesListPromise && this.storage?.set(this.nodesListKey, nodes)
}
}

export default Saturn
2 changes: 1 addition & 1 deletion src/storage/indexed-db-storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const DEFAULT_SATURN_STORAGE_NAME = 'saturn-client'
* @returns {import('./index.js').Storage}
*/
export function indexedDbStorage () {
const indexedDbExists = (typeof window !== 'undefined') && window?.indexedDB
const indexedDbExists = (typeof self !== 'undefined') && self?.indexedDB
let dbPromise
if (indexedDbExists) {
dbPromise = openDB(DEFAULT_IDB_STORAGE_NAME, DEFAULT_IDB_VERSION, {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/jwt.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function isJwtValid (jwt) {
* @param {object} opts
* @param {string} opts.clientKey
* @param {string} opts.authURL
* @param {import('./utils/storage.js').Storage} storage
* @param {import('../storage/index.js').Storage} storage
* @returns {Promise<string>}
*/
export async function getJWT (opts, storage) {
Expand Down
15 changes: 15 additions & 0 deletions src/utils/runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const isBrowser =
typeof window !== 'undefined' && typeof window.document !== 'undefined'

export const isServiceWorker = typeof ServiceWorkerGlobalScope !== 'undefined'

export const isWebWorker = typeof DedicatedWorkerGlobalScope !== 'undefined'

export const isSharedWorker = typeof SharedWorkerGlobalScope !== 'undefined'

export const isBrowserContext = isBrowser || isServiceWorker || isWebWorker || isSharedWorker

export const isNode =
typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null
1 change: 0 additions & 1 deletion strn.min.js

This file was deleted.

13 changes: 7 additions & 6 deletions test/fallback.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { concatChunks, generateNodes, getMockServer, mockJWT, mockNodesHandlers,

const TEST_DEFAULT_ORCH = 'https://orchestrator.strn.pl/nodes?maxNodes=100'
const TEST_NODES_LIST_KEY = 'saturn-nodes'
const TEST_AUTH = 'https://fz3dyeyxmebszwhuiky7vggmsu0rlkoy.lambda-url.us-west-2.on.aws/'
const TEST_ORIGIN_DOMAIN = 'saturn.ms'
const CLIENT_KEY = 'key'
describe('Client Fallback', () => {
Expand Down Expand Up @@ -108,7 +109,7 @@ describe('Client Fallback', () => {
test('Content Fallback fetches a cid properly', async (t) => {
const handlers = [
mockOrchHandler(2, TEST_DEFAULT_ORCH, 'saturn.ms'),
mockJWT('saturn.auth'),
mockJWT(TEST_AUTH),
mockSaturnOriginHandler(TEST_ORIGIN_DOMAIN, 0, true),
...mockNodesHandlers(2, TEST_ORIGIN_DOMAIN)
]
Expand Down Expand Up @@ -141,7 +142,7 @@ describe('Client Fallback', () => {
test('should fetch content from the first node successfully', async () => {
const handlers = [
mockOrchHandler(2, TEST_DEFAULT_ORCH, 'saturn.ms'),
mockJWT('saturn.auth'),
mockJWT(TEST_AUTH),
...mockNodesHandlers(2, TEST_ORIGIN_DOMAIN)
]

Expand Down Expand Up @@ -169,7 +170,7 @@ describe('Client Fallback', () => {
const numNodes = 3
const handlers = [
mockOrchHandler(numNodes, TEST_DEFAULT_ORCH, 'saturn.ms'),
mockJWT('saturn.auth'),
mockJWT(TEST_AUTH),
...mockNodesHandlers(numNodes, TEST_ORIGIN_DOMAIN)
]

Expand All @@ -192,15 +193,15 @@ describe('Client Fallback', () => {
assert(error)
assert.strictEqual(error.message, 'All attempts to fetch content have failed. Last error: Fetch error')
assert.strictEqual(fetchContentMock.mock.calls.length, numNodes + 1)
server.close()
mock.reset()
server.close()
})

test('Handles fallback with chunk overlap correctly', async () => {
const numNodes = 3
const handlers = [
mockOrchHandler(numNodes, TEST_DEFAULT_ORCH, 'saturn.ms'),
mockJWT('saturn.auth'),
mockJWT(TEST_AUTH),
...mockNodesHandlers(numNodes, TEST_ORIGIN_DOMAIN)
]

Expand Down Expand Up @@ -239,7 +240,7 @@ describe('Client Fallback', () => {
const numNodes = 3
const handlers = [
mockOrchHandler(numNodes, TEST_DEFAULT_ORCH, 'saturn.ms'),
mockJWT('saturn.auth'),
mockJWT(TEST_AUTH),
...mockNodesHandlers(numNodes, TEST_ORIGIN_DOMAIN)
]

Expand Down
22 changes: 17 additions & 5 deletions test/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import assert from 'node:assert/strict'
import { randomUUID } from 'node:crypto'
import { describe, it, before, after } from 'node:test'

import Saturn from '#src/index.js'
import { getMockServer, mockJWT, MSW_SERVER_OPTS } from './test-utils.js'
import { Saturn } from '#src/index.js'

const TEST_CID = 'QmXjYBY478Cno4jzdCcPy4NcJYFrwHZ51xaCP8vUwN9MGm'

const TEST_AUTH = 'https://fz3dyeyxmebszwhuiky7vggmsu0rlkoy.lambda-url.us-west-2.on.aws/'
const clientKey = 'abc123'

describe('Saturn client', () => {
Expand Down Expand Up @@ -41,7 +40,7 @@ describe('Saturn client', () => {
describe('Fetch a CID', () => {
const client = new Saturn({ clientKey })
const handlers = [
mockJWT('saturn.auth')
mockJWT(TEST_AUTH)
]
const server = getMockServer(handlers)

Expand All @@ -64,14 +63,27 @@ describe('Saturn client', () => {
await assert.rejects(client.fetchCID(TEST_CID, { connectTimeout: 1 }))
})

it('should use external abort controller', async () => {
const controller = new AbortController()
setTimeout(() => controller.abort(), 5)

await assert.rejects(
client.fetchCID(TEST_CID, { controller }),
{
name: 'AbortError',
message: 'This operation was aborted'
}
)
})

it.skip('should fail when exceeding download timeout', async () => {
await assert.rejects(client.fetchCID(`${TEST_CID}/blah`, { downloadTimeout: 1 }))
})
})

describe('Logging', () => {
const handlers = [
mockJWT('saturn.auth')
mockJWT(TEST_AUTH)
]
const server = getMockServer(handlers)
const client = new Saturn({ clientKey, clientId: 'tesd' })
Expand Down
5 changes: 2 additions & 3 deletions webpack.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ module.exports = {
target: 'web',
entry: './src/index.js',
output: {
filename: 'strn.min.js',
filename: 'dist/strn.min.js',
path: path.resolve(__dirname),
library: {
name: 'Saturn',
name: 'SaturnModule',
type: 'var',
export: 'default',
}
}
};

0 comments on commit a1ecd50

Please sign in to comment.