Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,21 @@ on:
jobs:
publish-npm:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
attestations: write
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 24
registry-url: https://registry.npmjs.org/
cache: 'pnpm'
node-version-file: .node-version

- run: make

- run: pnpm publish --access=public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
- run: pnpm publish --access=public --no-git-checks --provenance
11 changes: 8 additions & 3 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ on:

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
version: [24]
os: [ubuntu-latest]

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
registry-url: https://registry.npmjs.org/
node-version: ${{ matrix.version }}
cache: 'pnpm'
node-version-file: .node-version

- run: make test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
dist
tsconfig.tsbuildinfo
2 changes: 1 addition & 1 deletion .node-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22
24
23 changes: 6 additions & 17 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
SRCS = $(wildcard src/**)

all: dist
SRCS = $(wildcard src/** lib/**)

.PHONY: deps
deps: node_modules

.PHONY: clean
clean:
pnpm exec tsc -b --clean
rm -rf dist
all: typecheck

.PHONY: distclean
distclean: clean
distclean:
rm -rf node_modules

.PHONY: test
test: node_modules dist
test: node_modules
pnpm exec vitest

.PHONY: test.update
Expand All @@ -25,14 +18,10 @@ test.update: node_modules
node_modules: package.json
pnpm install

dist: node_modules tsconfig.json $(SRCS)
.PHONY: typecheck
typecheck: node_modules tsconfig.json $(SRCS)
pnpm exec tsc

.PHONY: dev
dev: node_modules
pnpm exec tsc -w

.PHONY: pretty
pretty: node_modules
pnpm exec eslint --fix .
pnpm exec prettier --write .
87 changes: 39 additions & 48 deletions lib/command.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,58 @@
import type { Jsonifiable } from 'type-fest';
import type { JsonifiableObject } from 'type-fest/source/jsonifiable.js';
import type { HttpMethod } from './types.js';
import type { Jsonifiable } from "type-fest";
import type { HttpMethod } from "./types.ts";

type JsonifiableObject = {[Key in string]?: Jsonifiable} | {toJSON: () => Jsonifiable};

function maybeWithNullProto<T>(val: T): T {
return typeof val === 'object'
? Object.assign(Object.create(null), val)
: val;
return typeof val === "object" && val !== null
? Object.assign(Object.create(null), val)
: val;
}

type Middleware<
CommandInput extends JsonifiableObject | unknown = unknown,
CommandOutput extends Jsonifiable | unknown = unknown,
CommandInput extends JsonifiableObject | unknown = unknown,
CommandOutput extends Jsonifiable | unknown = unknown,
> = (input: CommandInput, output: CommandOutput) => CommandOutput;

type Body = BodyInit | null | Uint8Array

export abstract class Command<
// WARN: this must be kept compatible with the Client Input and Output types
CommandInput extends JsonifiableObject | unknown = unknown,
CommandOutput extends Jsonifiable | unknown = unknown,
CommandBody extends Jsonifiable | unknown = unknown,
CommandQuery extends JsonifiableObject | unknown = unknown,
// WARN: this must be kept compatible with the Client Input and Output types
CommandInput extends JsonifiableObject | unknown = unknown,
CommandOutput extends Jsonifiable | unknown = unknown,
CommandQuery extends JsonifiableObject | unknown = unknown,
> {
public readonly method: HttpMethod = 'get';

readonly #pathname: string;

readonly #body: CommandBody | undefined;
public readonly method: HttpMethod = "get";

readonly #query: CommandQuery | undefined;
public readonly pathname: string;

protected middleware: Middleware<CommandInput, CommandOutput>[] = [];
public readonly body: Body | null;

constructor(pathname: string, body?: CommandBody, query?: CommandQuery) {
this.#pathname = pathname;
this.#body = maybeWithNullProto(body);
this.#query = maybeWithNullProto(query);
}
public readonly query: CommandQuery | undefined;

public serialize() {
return JSON.stringify(this.toJSON());
}
protected middleware: Middleware<CommandInput, CommandOutput>[] = [];

public toString() {
return this.serialize();
}
constructor(pathname: string, body: Body | null = null, query?: CommandQuery) {
this.pathname = pathname;
this.body = body;
this.query = maybeWithNullProto(query);
}

public toJSON() {
return maybeWithNullProto({
method: this.method,
pathname: this.pathname,
body: this.body,
query: this.query,
});
}
public serialize() {
return JSON.stringify(this.toJSON());
}

public get body() {
return this.#body;
}
public toString() {
return this.serialize();
}

public get query() {
return this.#query;
}
public toJSON() {
return maybeWithNullProto({
method: this.method,
pathname: this.pathname,
body: this.body,
query: this.query,
});
}

public get pathname() {
return this.#pathname;
}
}
2 changes: 1 addition & 1 deletion lib/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ResolvableHeaders } from './types.js';
import type { ResolvableHeaders } from './types.ts';

export async function resolveHeaders(
headers: ResolvableHeaders | undefined,
Expand Down
39 changes: 38 additions & 1 deletion lib/errors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
/* eslint-disable max-classes-per-file */
import { CustomError } from '@block65/custom-error';
import type * as v from 'valibot';

export class ServiceError extends CustomError {}
export class ServiceError extends CustomError {

public response: Response;

constructor(message: string, response: Response) {
super(message);
this.response = response;
}
}

export class ServiceResponseError extends CustomError {

public response: Response;

constructor(response: Response) {
super(response.statusText);
this.response = response;
}
}

export class PublicValibotHonoError extends CustomError {
static from(
err: v.ValiError<
| v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>
| v.BaseSchemaAsync<unknown, unknown, v.BaseIssue<unknown>>
>,
) {
return new PublicValibotHonoError(err.message, err).addDetail({
violations: err.issues.map((issue) => ({
field: issue.path?.[0]?.key?.toString() || "",
description: issue.message,
})),
description: err.message,
});
}
}
Loading