Skip to content

Commit 6b1a3c3

Browse files
committed
添加 @objectstack/client-sdk 包及其相关配置,包含基本客户端实现和示例;更新依赖项以支持新功能
1 parent abebba9 commit 6b1a3c3

File tree

7 files changed

+272
-1
lines changed

7 files changed

+272
-1
lines changed

examples/todo/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"build": "tsc"
88
},
99
"dependencies": {
10-
"@objectstack/spec": "workspace:*"
10+
"@objectstack/spec": "workspace:*",
11+
"@objectstack/client-sdk": "workspace:*"
1112
},
1213
"devDependencies": {
1314
"typescript": "^5.0.0"

examples/todo/src/client-test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { ObjectStackClient } from '@objectstack/client-sdk';
2+
3+
async function main() {
4+
console.log('🚀 Starting Client SDK Test...');
5+
6+
// 1. Initialize Client
7+
const client = new ObjectStackClient({
8+
baseUrl: 'http://localhost:3004'
9+
});
10+
11+
try {
12+
// 2. Connect (Auto-Discovery)
13+
console.log('🔌 Connecting to server...');
14+
await client.connect();
15+
console.log('✅ Connected! Discovery complete.');
16+
17+
// 3. Get Metadata
18+
console.log('🔍 Fetching Object Metadata for "todo_task"...');
19+
const objectMeta = await client.meta.getObject('todo_task');
20+
console.log(`📋 Object Label: ${objectMeta.label}`);
21+
22+
// 4. Query Data
23+
console.log('💾 Querying Data...');
24+
const result = await client.data.find('todo_task', {});
25+
26+
console.log(`🎉 Found ${result.data.length} tasks:`);
27+
result.data.forEach((task: any) => {
28+
console.log(` - [${task.status}] ${task.title}`);
29+
});
30+
31+
} catch (error) {
32+
console.error('❌ Error during test:', error);
33+
}
34+
}
35+
36+
main();

packages/client-sdk/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# @objectstack/client-sdk
2+
3+
The official TypeScript client for ObjectStack.
4+
5+
## Features
6+
7+
- **Auto-Discovery**: Connects to your ObjectStack server and automatically configures API endpoints.
8+
- **Typed Metadata**: Retrieve Object and View definitions with full type support.
9+
- **Unified Data Access**: Simple CRUD operations for any object in your schema.
10+
11+
## Installation
12+
13+
```bash
14+
pnpm add @objectstack/client-sdk
15+
```
16+
17+
## Usage
18+
19+
```typescript
20+
import { ObjectStackClient } from '@objectstack/client-sdk';
21+
22+
// 1. Initialize
23+
const client = new ObjectStackClient({
24+
baseUrl: 'http://localhost:3004' // Your ObjectStack Server URL
25+
});
26+
27+
async function main() {
28+
// 2. Connect (Fetches system capabilities)
29+
await client.connect();
30+
31+
// 3. Metadata Access
32+
const todoSchema = await client.meta.getObject('todo_task');
33+
console.log('Fields:', todoSchema.fields);
34+
35+
// 4. Data Access
36+
const tasks = await client.data.find('todo_task', {
37+
status: 'pending' // Simple filtering
38+
});
39+
40+
// 5. Create Data
41+
await client.data.create('todo_task', {
42+
title: 'New Task',
43+
status: 'todo'
44+
});
45+
}
46+
```
47+
48+
## API Reference
49+
50+
### `client.connect()`
51+
Initializes the client by fetching the system discovery manifest from `/api/v1`.
52+
53+
### `client.meta`
54+
- `getObject(name: string)`: Get object schema.
55+
- `getView(name: string)`: Get view configuration.
56+
57+
### `client.data`
58+
- `find(object: string, query?: Record<string, any>)`: List records.
59+
- `get(object: string, id: string)`: Get single record.
60+
- `create(object: string, data: any)`: Create record.
61+
- `update(object: string, id: string, data: any)`: Update record.
62+
- `delete(object: string, id: string)`: Delete record.

packages/client-sdk/package.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "@objectstack/client-sdk",
3+
"version": "0.1.0",
4+
"description": "Official Client SDK for ObjectStack Protocol",
5+
"main": "dist/index.js",
6+
"types": "dist/index.d.ts",
7+
"scripts": {
8+
"build": "tsc"
9+
},
10+
"dependencies": {
11+
"@objectstack/spec": "workspace:*"
12+
},
13+
"devDependencies": {
14+
"typescript": "^5.0.0"
15+
}
16+
}

packages/client-sdk/src/index.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
export interface ClientConfig {
2+
baseUrl: string;
3+
token?: string;
4+
}
5+
6+
export interface DiscoveryResult {
7+
routes: {
8+
discovery: string;
9+
metadata: string;
10+
data: string;
11+
auth: string;
12+
};
13+
capabilities?: Record<string, boolean>;
14+
}
15+
16+
export class ObjectStackClient {
17+
private baseUrl: string;
18+
private token?: string;
19+
private routes?: DiscoveryResult['routes'];
20+
21+
constructor(config: ClientConfig) {
22+
this.baseUrl = config.baseUrl.replace(/\/$/, ''); // Remove trailing slash
23+
this.token = config.token;
24+
}
25+
26+
/**
27+
* Initialize the client by discovering server capabilities and routes.
28+
*/
29+
async connect() {
30+
try {
31+
const res = await this.fetch(`${this.baseUrl}/api/v1`);
32+
const data = await res.json();
33+
this.routes = data.routes;
34+
return data;
35+
} catch (e) {
36+
console.error('Failed to connect to ObjectStack Server', e);
37+
throw e;
38+
}
39+
}
40+
41+
/**
42+
* Metadata Operations
43+
*/
44+
meta = {
45+
getObject: async (name: string) => {
46+
const route = this.getRoute('metadata');
47+
const res = await this.fetch(`${this.baseUrl}${route}/object/${name}`);
48+
return res.json();
49+
},
50+
51+
getView: async (object: string, type: 'list' | 'form' = 'list') => {
52+
// UI routes might not be in discovery map yet, assume convention or add to server
53+
// Convention from server/src/index.ts: /api/v1/ui/view/:object
54+
const res = await this.fetch(`${this.baseUrl}/api/v1/ui/view/${object}?type=${type}`);
55+
return res.json();
56+
}
57+
};
58+
59+
/**
60+
* Data Operations
61+
*/
62+
data = {
63+
find: async (object: string, query: any = {}) => {
64+
const route = this.getRoute('data');
65+
const queryString = new URLSearchParams(query).toString();
66+
const res = await this.fetch(`${this.baseUrl}${route}/${object}?${queryString}`);
67+
return res.json();
68+
},
69+
70+
get: async (object: string, id: string) => {
71+
const route = this.getRoute('data');
72+
const res = await this.fetch(`${this.baseUrl}${route}/${object}/${id}`);
73+
return res.json();
74+
},
75+
76+
create: async (object: string, data: any) => {
77+
const route = this.getRoute('data');
78+
const res = await this.fetch(`${this.baseUrl}${route}/${object}`, {
79+
method: 'POST',
80+
body: JSON.stringify(data)
81+
});
82+
return res.json();
83+
},
84+
85+
update: async (object: string, id: string, data: any) => {
86+
const route = this.getRoute('data');
87+
const res = await this.fetch(`${this.baseUrl}${route}/${object}/${id}`, {
88+
method: 'PATCH',
89+
body: JSON.stringify(data)
90+
});
91+
return res.json();
92+
},
93+
94+
delete: async (object: string, id: string) => {
95+
const route = this.getRoute('data');
96+
const res = await this.fetch(`${this.baseUrl}${route}/${object}/${id}`, {
97+
method: 'DELETE'
98+
});
99+
return res.json();
100+
}
101+
};
102+
103+
private getRoute(key: keyof DiscoveryResult['routes']): string {
104+
if (!this.routes) {
105+
throw new Error('Client not connected. Call client.connect() first.');
106+
}
107+
return this.routes[key];
108+
}
109+
110+
private async fetch(url: string, options: RequestInit = {}) {
111+
const headers: Record<string, string> = {
112+
'Content-Type': 'application/json',
113+
...(options.headers as any || {})
114+
};
115+
116+
if (this.token) {
117+
headers['Authorization'] = `Bearer ${this.token}`;
118+
}
119+
120+
const res = await fetch(url, {
121+
...options,
122+
headers
123+
});
124+
125+
if (res.status >= 400) {
126+
throw new Error(`API Error ${res.status}: ${res.statusText}`);
127+
}
128+
129+
return res;
130+
}
131+
}

packages/client-sdk/tsconfig.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"module": "commonjs",
5+
"declaration": true,
6+
"outDir": "./dist",
7+
"strict": true,
8+
"esModuleInterop": true,
9+
"skipLibCheck": true
10+
},
11+
"include": ["src/**/*"]
12+
}

pnpm-lock.yaml

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)