Skip to content

Commit

Permalink
feat: TypeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
tpluscode committed Feb 20, 2024
1 parent 5216aa8 commit 2643a0a
Show file tree
Hide file tree
Showing 24 changed files with 1,952 additions and 1,874 deletions.
5 changes: 5 additions & 0 deletions .changeset/moody-eyes-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hydra-box": minor
---

Source changes to TypeScript
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
root = true

[*]
indent_size = 2
indent_style = space
insert_final_newline = true
4 changes: 2 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"extends": [ "@tpluscode/eslint-config/js" ],
"extends": [ "@tpluscode" ],
"env": {
"mocha": true
},
"overrides": [
{
"files": ["examples/**/*.js"],
"files": ["examples/**/*.ts"],
"rules": {
"no-console": "off",
"import/no-extraneous-dependencies": "off"
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.nyc_output
coverage
node_modules
*.d.ts
*.js
!test/**/*.js
!examples/**/*.js
48 changes: 38 additions & 10 deletions Api.js → Api.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
/* eslint-disable camelcase */
import rdf from '@zazuko/env-node'
import EcmaScriptLoader from 'rdf-loader-code/ecmaScript.js'
import LoaderRegistry from 'rdf-loaders-registry'
import LoaderRegistryImpl, { LoaderRegistry } from 'rdf-loaders-registry'
import EcmaScriptModuleLoader from 'rdf-loader-code/ecmaScriptModule.js'
import EcmaScriptLiteralLoader from 'rdf-loader-code/ecmaScriptLiteral.js'
import type { NamedNode, DatasetCore, Quad_Graph } from '@rdfjs/types'
import { replaceDatasetIRI } from './lib/replaceIRI.js'

interface ApiInit<D extends DatasetCore = DatasetCore> {
term?: NamedNode
dataset?: D
graph?: NamedNode
path?: string
codePath?: string
}

class Api {
constructor({ term, dataset, graph, path, codePath } = {}) {
this.term = term
this.dataset = dataset
initialized: boolean
path: string
codePath: string
graph?: Quad_Graph | undefined
dataset: DatasetCore
private _term: NamedNode | undefined
loaderRegistry: LoaderRegistry
private _initialization?: Promise<void>
readonly tasks: Array<() => Promise<void>>

constructor({ term, dataset, graph, path = '/api', codePath = process.cwd() }: ApiInit = { }) {
this._term = term
this.dataset = dataset || rdf.dataset()
this.graph = graph
this.path = path
this.codePath = codePath
this.loaderRegistry = new LoaderRegistry()
this.loaderRegistry = new LoaderRegistryImpl()
this.tasks = []
this.initialized = false

Expand All @@ -21,6 +41,14 @@ class Api {
EcmaScriptLiteralLoader.register(this.loaderRegistry)
}

get term() {
return this._term!
}

set term(term: NamedNode) {
this._term = term
}

async init() {
if (!this._initialization) {
this._initialization = this._beginInit()
Expand All @@ -29,23 +57,23 @@ class Api {
return this._initialization
}

fromFile(filePath) {
fromFile(filePath: string) {
this.tasks.push(async () => {
this.dataset.addAll(await rdf.dataset().import(rdf.fromFile(filePath)))
rdf.dataset().addAll.call(this.dataset, await rdf.dataset().import(rdf.fromFile(filePath)))
})

return this
}

rebase(fromBaseIRI, toBaseIRI) {
rebase(fromBaseIRI: string | NamedNode, toBaseIRI: string | NamedNode) {
this.tasks.push(async () => {
this.dataset = replaceDatasetIRI(fromBaseIRI, toBaseIRI, this.dataset)
})

return this
}

static fromFile(filePath, options) {
static fromFile(filePath: string, options?: ApiInit) {
const api = new Api(options)

return api.fromFile(filePath)
Expand All @@ -65,7 +93,7 @@ class Api {
if (apiDoc.has(rdf.ns.rdf.type, rdf.ns.hydra.ApiDocumentation).terms.length === 0) {
apiDoc.addOut(rdf.ns.rdf.type, rdf.ns.hydra.ApiDocumentation)

apiDoc.node().has(rdf.ns.rdf.type, rdf.ns.hydra.Class).forEach(supportedClass => {
apiDoc.any().has(rdf.ns.rdf.type, rdf.ns.hydra.Class).forEach(supportedClass => {
apiDoc.addOut(rdf.ns.hydra.supportedClass, supportedClass)
})
}
Expand Down
50 changes: 0 additions & 50 deletions StoreResourceLoader.js

This file was deleted.

63 changes: 63 additions & 0 deletions StoreResourceLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { NamedNode, Store } from '@rdfjs/types'
import rdf from '@zazuko/env-node'
import { isNamedNode } from 'is-graph-pointer'
import { PropertyResource, Resource, ResourceLoader } from './index.js'

export default class StoreResourceLoader implements ResourceLoader {
readonly store: Store

constructor({ store }: { store: Store }) {
this.store = store
}

async load(term: NamedNode): Promise<Resource | null> {
const dataset = await rdf.dataset().import(this.store.match(null, null, null, term))

if (dataset.size === 0) {
return null
}

const types = rdf.clownface({ dataset, term })
.out(rdf.ns.rdf.type)
.filter(isNamedNode)

return {
term,
prefetchDataset: dataset,
async dataset() {
return dataset
},
quadStream() {
return dataset.toStream()
},
types: rdf.termSet(types.terms),
}
}

async forClassOperation(term: NamedNode) {
const resource = await this.load(term)

return resource ? [resource] : []
}

async forPropertyOperation(term: NamedNode): Promise<PropertyResource[]> {
const dataset = await rdf.dataset().import(this.store.match(null, null, term, null))
const result: PropertyResource[] = []

for (const quad of dataset) {
if (quad.subject.termType !== 'NamedNode') continue
if (quad.object.termType !== 'NamedNode') continue

const loaded = await this.load(quad.subject)
if (!loaded) continue

result.push({
property: quad.predicate,
object: quad.object,
...loaded,
})
}

return result
}
}
7 changes: 0 additions & 7 deletions index.js

This file was deleted.

44 changes: 44 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { Readable } from 'stream'
import type * as RDF from '@rdfjs/types'
import type { GraphPointer } from 'clownface'
import type { Request } from 'express'
import type { Dataset } from '@zazuko/env/lib/Dataset.js'
import middleware from './middleware.js'
import Api from './Api.js'

export interface Resource {
term: RDF.NamedNode
prefetchDataset: RDF.DatasetCore
dataset(): Promise<Dataset>
quadStream(): RDF.Stream & Readable
types: Set<RDF.NamedNode>
}

export interface PropertyResource extends Resource {
property: RDF.Quad_Predicate
object: RDF.NamedNode
}

export interface PotentialOperation {
resource: Resource | PropertyResource
operation: GraphPointer
}

export interface HydraBox {
api: Api
term: RDF.NamedNode
store: RDF.Store
resource: Resource & { clownface(): Promise<GraphPointer<RDF.NamedNode, Dataset>> }
operation: GraphPointer
operations: PotentialOperation[]
}

export interface ResourceLoader {
forClassOperation(term: RDF.NamedNode, req: Request): Promise<Resource[]>
forPropertyOperation(term: RDF.NamedNode, req: Request): Promise<PropertyResource[]>
}

export default {
middleware,
Api,
}
2 changes: 1 addition & 1 deletion lib/log.js → lib/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import debug0 from 'debug'

const debug = debug0('hydra-box')

export default (ns) => {
export default (ns: string) => {
if (ns) {
return {
debug: debug.extend(ns),
Expand Down
17 changes: 0 additions & 17 deletions lib/middleware/apiHeader.js

This file was deleted.

20 changes: 20 additions & 0 deletions lib/middleware/apiHeader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Router } from 'express'
import Api from '../../Api.js'

export default function factory(api: Api) {
const router = Router()

router.use((req, res, next) => {
if (api.term) {
res.setLink(api.term.value, 'http://www.w3.org/ns/hydra/core#apiDocumentation')
}

next()
})

router.get(api.path, (req, res, next) => {
res.dataset(api.dataset).catch(next)
})

return router
}
Loading

0 comments on commit 2643a0a

Please sign in to comment.