mirror of
https://github.com/Warky-Devs/ResolveSpec.git
synced 2025-05-18 20:57:29 +00:00
More clientside ideas
This commit is contained in:
parent
f284e55a5c
commit
6dd6abb2e8
71
resolvespec-js/package.json
Normal file
71
resolvespec-js/package.json
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"name": "@warkypublic/resolvespec-js",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Client side library for the ResolveSpec API",
|
||||||
|
"type": "module",
|
||||||
|
"main": "./src/index.ts",
|
||||||
|
"module": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public",
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"module": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"bin",
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "vite build",
|
||||||
|
"clean": "rm -rf dist",
|
||||||
|
"prepublishOnly": "npm run build",
|
||||||
|
"test": "vitest run",
|
||||||
|
"lint": "eslint src"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"string",
|
||||||
|
"blob",
|
||||||
|
"dependencies",
|
||||||
|
"workspace",
|
||||||
|
"package",
|
||||||
|
"cli",
|
||||||
|
"tools",
|
||||||
|
"npm",
|
||||||
|
"yarn",
|
||||||
|
"pnpm"
|
||||||
|
],
|
||||||
|
"author": "Hein (Warkanum) Puth",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"semver": "^7.6.3",
|
||||||
|
"uuid": "^11.0.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@changesets/cli": "^2.27.10",
|
||||||
|
"@eslint/js": "^9.16.0",
|
||||||
|
"@types/jsdom": "^21.1.7",
|
||||||
|
"eslint": "^9.16.0",
|
||||||
|
"globals": "^15.13.0",
|
||||||
|
"jsdom": "^25.0.1",
|
||||||
|
"typescript": "^5.7.2",
|
||||||
|
"typescript-eslint": "^8.17.0",
|
||||||
|
"vite": "^6.0.2",
|
||||||
|
"vite-plugin-dts": "^4.3.0",
|
||||||
|
"vitest": "^2.1.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.16"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/Warky-Devs/ResolveSpec"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/Warky-Devs/ResolveSpec/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/Warky-Devs/ResolveSpec#readme",
|
||||||
|
"packageManager": "pnpm@9.6.0+sha512.38dc6fba8dba35b39340b9700112c2fe1e12f10b17134715a4aa98ccf7bb035e76fd981cf0bb384dfa98f8d6af5481c2bef2f4266a24bfa20c34eb7147ce0b5e"
|
||||||
|
}
|
||||||
|
|
132
resolvespec-js/src/api.ts
Normal file
132
resolvespec-js/src/api.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { ClientConfig, APIResponse, TableMetadata, Options, RequestBody } from "./types";
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
const getHeaders = (options?: Record<string,any>): HeadersInit => {
|
||||||
|
const headers: HeadersInit = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options?.token) {
|
||||||
|
headers['Authorization'] = `Bearer ${options.token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildUrl = (config: ClientConfig, schema: string, entity: string, id?: string): string => {
|
||||||
|
let url = `${config.baseUrl}/${schema}/${entity}`;
|
||||||
|
if (id) {
|
||||||
|
url += `/${id}`;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchWithError = async <T>(url: string, options: RequestInit): Promise<APIResponse<T>> => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error?.message || 'An error occurred');
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// API Functions
|
||||||
|
export const getMetadata = async (
|
||||||
|
config: ClientConfig,
|
||||||
|
schema: string,
|
||||||
|
entity: string
|
||||||
|
): Promise<APIResponse<TableMetadata>> => {
|
||||||
|
const url = buildUrl(config, schema, entity);
|
||||||
|
return fetchWithError<TableMetadata>(url, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: getHeaders(config),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const read = async <T = any>(
|
||||||
|
config: ClientConfig,
|
||||||
|
schema: string,
|
||||||
|
entity: string,
|
||||||
|
id?: string,
|
||||||
|
options?: Options
|
||||||
|
): Promise<APIResponse<T>> => {
|
||||||
|
const url = buildUrl(config, schema, entity, id);
|
||||||
|
const body: RequestBody = {
|
||||||
|
operation: 'read',
|
||||||
|
options,
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetchWithError<T>(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getHeaders(config),
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const create = async <T = any>(
|
||||||
|
config: ClientConfig,
|
||||||
|
schema: string,
|
||||||
|
entity: string,
|
||||||
|
data: any | any[],
|
||||||
|
options?: Options
|
||||||
|
): Promise<APIResponse<T>> => {
|
||||||
|
const url = buildUrl(config, schema, entity);
|
||||||
|
const body: RequestBody = {
|
||||||
|
operation: 'create',
|
||||||
|
data,
|
||||||
|
options,
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetchWithError<T>(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getHeaders(config),
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const update = async <T = any>(
|
||||||
|
config: ClientConfig,
|
||||||
|
schema: string,
|
||||||
|
entity: string,
|
||||||
|
data: any | any[],
|
||||||
|
id?: string | string[],
|
||||||
|
options?: Options
|
||||||
|
): Promise<APIResponse<T>> => {
|
||||||
|
const url = buildUrl(config, schema, entity, typeof id === 'string' ? id : undefined);
|
||||||
|
const body: RequestBody = {
|
||||||
|
operation: 'update',
|
||||||
|
id: typeof id === 'string' ? undefined : id,
|
||||||
|
data,
|
||||||
|
options,
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetchWithError<T>(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getHeaders(config),
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteEntity = async (
|
||||||
|
config: ClientConfig,
|
||||||
|
schema: string,
|
||||||
|
entity: string,
|
||||||
|
id: string
|
||||||
|
): Promise<APIResponse<void>> => {
|
||||||
|
const url = buildUrl(config, schema, entity, id);
|
||||||
|
const body: RequestBody = {
|
||||||
|
operation: 'delete',
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetchWithError<void>(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getHeaders(config),
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
};
|
68
resolvespec-js/src/examples.ts
Normal file
68
resolvespec-js/src/examples.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { getMetadata, read, create, update, deleteEntity } from "./api";
|
||||||
|
import { ClientConfig } from "./types";
|
||||||
|
|
||||||
|
// Usage Examples
|
||||||
|
const config: ClientConfig = {
|
||||||
|
baseUrl: 'http://api.example.com/v1',
|
||||||
|
token: 'your-token-here'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Example usage
|
||||||
|
const examples = async () => {
|
||||||
|
// Get metadata
|
||||||
|
const metadata = await getMetadata(config, 'test', 'employees');
|
||||||
|
|
||||||
|
|
||||||
|
// Read with relations
|
||||||
|
const employees = await read(config, 'test', 'employees', undefined, {
|
||||||
|
preload: [
|
||||||
|
{
|
||||||
|
relation: 'department',
|
||||||
|
columns: ['id', 'name']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
column: 'status',
|
||||||
|
operator: 'eq',
|
||||||
|
value: 'active'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create single record
|
||||||
|
const newEmployee = await create(config, 'test', 'employees', {
|
||||||
|
first_name: 'John',
|
||||||
|
last_name: 'Doe',
|
||||||
|
email: 'john@example.com'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bulk create
|
||||||
|
const newEmployees = await create(config, 'test', 'employees', [
|
||||||
|
{
|
||||||
|
first_name: 'Jane',
|
||||||
|
last_name: 'Smith',
|
||||||
|
email: 'jane@example.com'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
first_name: 'Bob',
|
||||||
|
last_name: 'Johnson',
|
||||||
|
email: 'bob@example.com'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Update single record
|
||||||
|
const updatedEmployee = await update(config, 'test', 'employees',
|
||||||
|
{ status: 'inactive' },
|
||||||
|
'emp123'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Bulk update
|
||||||
|
const updatedEmployees = await update(config, 'test', 'employees',
|
||||||
|
{ department_id: 'dept2' },
|
||||||
|
['emp1', 'emp2', 'emp3']
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
await deleteEntity(config, 'test', 'employees', 'emp123');
|
||||||
|
};
|
0
resolvespec-js/src/index.ts
Normal file
0
resolvespec-js/src/index.ts
Normal file
86
resolvespec-js/src/types.ts
Normal file
86
resolvespec-js/src/types.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Types
|
||||||
|
export type Operator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'in';
|
||||||
|
export type Operation = 'read' | 'create' | 'update' | 'delete';
|
||||||
|
export type SortDirection = 'asc' | 'desc';
|
||||||
|
|
||||||
|
export interface PreloadOption {
|
||||||
|
relation: string;
|
||||||
|
columns?: string[];
|
||||||
|
filters?: FilterOption[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterOption {
|
||||||
|
column: string;
|
||||||
|
operator: Operator;
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SortOption {
|
||||||
|
column: string;
|
||||||
|
direction: SortDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomOperator {
|
||||||
|
name: string;
|
||||||
|
sql: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComputedColumn {
|
||||||
|
name: string;
|
||||||
|
expression: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
preload?: PreloadOption[];
|
||||||
|
columns?: string[];
|
||||||
|
filters?: FilterOption[];
|
||||||
|
sort?: SortOption[];
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
customOperators?: CustomOperator[];
|
||||||
|
computedColumns?: ComputedColumn[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RequestBody {
|
||||||
|
operation: Operation;
|
||||||
|
id?: string | string[];
|
||||||
|
data?: any | any[];
|
||||||
|
options?: Options;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface APIResponse<T = any> {
|
||||||
|
success: boolean;
|
||||||
|
data: T;
|
||||||
|
metadata?: {
|
||||||
|
total: number;
|
||||||
|
filtered: number;
|
||||||
|
limit: number;
|
||||||
|
offset: number;
|
||||||
|
};
|
||||||
|
error?: {
|
||||||
|
code: string;
|
||||||
|
message: string;
|
||||||
|
details?: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Column {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
is_nullable: boolean;
|
||||||
|
is_primary: boolean;
|
||||||
|
is_unique: boolean;
|
||||||
|
has_index: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableMetadata {
|
||||||
|
schema: string;
|
||||||
|
table: string;
|
||||||
|
columns: Column[];
|
||||||
|
relations: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ClientConfig {
|
||||||
|
baseUrl: string;
|
||||||
|
token?: string;
|
||||||
|
}
|
5
resolvespec-python/todo.md
Normal file
5
resolvespec-python/todo.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Python Implementation of the ResolveSpec API
|
||||||
|
|
||||||
|
# Server
|
||||||
|
|
||||||
|
# Client
|
Loading…
Reference in New Issue
Block a user