@innacyraja/table-ts 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,105 @@
1
+ /**
2
+ * TableAccessor defines the interface for accessing table data.
3
+ * This mirrors the Go TableAccessor[T any] interface.
4
+ */
5
+ export interface TableAccessor<T> {
6
+ insert(tableId: string, data: T, options?: RequestOptions): Promise<void>;
7
+ bulkInsert(tableId: string, data: T[], options?: RequestOptions): Promise<void>;
8
+ find(tableId: string, query: Record<string, any>, options?: RequestOptions): Promise<T[]>;
9
+ delete(tableId: string, query: Record<string, any>, options?: RequestOptions): Promise<void>;
10
+ update(tableId: string, query: Record<string, any>, data: T, options?: RequestOptions): Promise<T[]>;
11
+ }
12
+ /**
13
+ * RequestOptions allows per-request overrides (analogous to Go's context).
14
+ */
15
+ export interface RequestOptions {
16
+ /** AbortSignal for request cancellation (replaces Go's context.Context). */
17
+ signal?: AbortSignal;
18
+ }
19
+ /**
20
+ * Config holds configuration options for CnipsTableAccessor.
21
+ * Mirrors the Go Config struct.
22
+ */
23
+ export interface Config {
24
+ /**
25
+ * Timeout in milliseconds for HTTP requests.
26
+ * If zero or undefined, defaults to 60000ms (60 seconds).
27
+ */
28
+ timeoutMs?: number;
29
+ /**
30
+ * Custom fetch function. Allows providing a custom HTTP client.
31
+ * If undefined, the global fetch will be used.
32
+ */
33
+ fetchFn?: typeof fetch;
34
+ }
35
+ /**
36
+ * CnipsTableAccessor is a concrete implementation of TableAccessor for cnips.
37
+ * Mirrors the Go CnipsTableAccessor[T any] struct.
38
+ */
39
+ export declare class CnipsTableAccessor<T> implements TableAccessor<T> {
40
+ private fetchFn;
41
+ private timeoutMs;
42
+ baseURL: string;
43
+ apiKey: string;
44
+ /**
45
+ * Creates a new CnipsTableAccessor instance.
46
+ * @param baseURL - The base URL of the cnips instance (mandatory).
47
+ * @param apiKey - The API key for the cnips instance (mandatory).
48
+ * @param config - Optional configuration overrides.
49
+ */
50
+ constructor(baseURL: string, apiKey: string, config?: Config);
51
+ /**
52
+ * Builds the URL for standard row operations.
53
+ * Mirrors Go's buildURL method.
54
+ */
55
+ private buildURL;
56
+ /**
57
+ * Builds the URL for the search endpoint.
58
+ * Mirrors Go's buildSearchURL method.
59
+ */
60
+ private buildSearchURL;
61
+ /**
62
+ * Returns the common headers for all requests.
63
+ * Mirrors Go's setHeaders method.
64
+ */
65
+ private getHeaders;
66
+ /**
67
+ * Performs an HTTP request and returns the response.
68
+ * Mirrors Go's doRequest method.
69
+ */
70
+ private doRequest;
71
+ /**
72
+ * Reads the response body and checks the status code.
73
+ * Mirrors Go's handleResponse method.
74
+ */
75
+ private handleResponse;
76
+ /**
77
+ * Inserts a new record into the specified table.
78
+ * The data is wrapped in an array as the server expects an array.
79
+ * Mirrors Go's Insert method.
80
+ */
81
+ insert(tableId: string, data: T, options?: RequestOptions): Promise<void>;
82
+ /**
83
+ * Inserts multiple records into the specified table.
84
+ * Mirrors Go's BulkInsert method.
85
+ */
86
+ bulkInsert(tableId: string, data: T[], options?: RequestOptions): Promise<void>;
87
+ /**
88
+ * Retrieves records from the specified table matching the query.
89
+ * Uses POST request to /search endpoint with query in the request body.
90
+ * Mirrors Go's Find method.
91
+ */
92
+ find(tableId: string, query: Record<string, any>, options?: RequestOptions): Promise<T[]>;
93
+ /**
94
+ * Removes records from the specified table matching the query.
95
+ * Uses DELETE request with query in the request body.
96
+ * Mirrors Go's Delete method.
97
+ */
98
+ delete(tableId: string, query: Record<string, any>, options?: RequestOptions): Promise<void>;
99
+ /**
100
+ * Updates records in the specified table matching the query with the provided data.
101
+ * Uses PUT request with query and data in the request body.
102
+ * Mirrors Go's Update method.
103
+ */
104
+ update(tableId: string, query: Record<string, any>, data: T, options?: RequestOptions): Promise<T[]>;
105
+ }
package/dist/index.js ADDED
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CnipsTableAccessor = void 0;
4
+ /**
5
+ * CnipsTableAccessor is a concrete implementation of TableAccessor for cnips.
6
+ * Mirrors the Go CnipsTableAccessor[T any] struct.
7
+ */
8
+ class CnipsTableAccessor {
9
+ /**
10
+ * Creates a new CnipsTableAccessor instance.
11
+ * @param baseURL - The base URL of the cnips instance (mandatory).
12
+ * @param apiKey - The API key for the cnips instance (mandatory).
13
+ * @param config - Optional configuration overrides.
14
+ */
15
+ constructor(baseURL, apiKey, config) {
16
+ this.baseURL = baseURL.replace(/\/+$/, "");
17
+ this.apiKey = apiKey;
18
+ this.timeoutMs = config?.timeoutMs || 60000;
19
+ this.fetchFn = config?.fetchFn || globalThis.fetch;
20
+ }
21
+ /**
22
+ * Builds the URL for standard row operations.
23
+ * Mirrors Go's buildURL method.
24
+ */
25
+ buildURL(tableId) {
26
+ if (!tableId) {
27
+ throw new Error("tableId cannot be empty");
28
+ }
29
+ try {
30
+ const base = new URL(this.baseURL);
31
+ base.pathname =
32
+ base.pathname.replace(/\/+$/, "") +
33
+ `/tables/${encodeURIComponent(tableId)}/rows`;
34
+ return base.toString();
35
+ }
36
+ catch {
37
+ throw new Error(`invalid base URL: ${this.baseURL}`);
38
+ }
39
+ }
40
+ /**
41
+ * Builds the URL for the search endpoint.
42
+ * Mirrors Go's buildSearchURL method.
43
+ */
44
+ buildSearchURL(tableId) {
45
+ if (!tableId) {
46
+ throw new Error("tableId cannot be empty");
47
+ }
48
+ try {
49
+ const base = new URL(this.baseURL);
50
+ base.pathname =
51
+ base.pathname.replace(/\/+$/, "") +
52
+ `/tables/${encodeURIComponent(tableId)}/rows/search`;
53
+ return base.toString();
54
+ }
55
+ catch {
56
+ throw new Error(`invalid base URL: ${this.baseURL}`);
57
+ }
58
+ }
59
+ /**
60
+ * Returns the common headers for all requests.
61
+ * Mirrors Go's setHeaders method.
62
+ */
63
+ getHeaders() {
64
+ return {
65
+ "X-API-Key": this.apiKey,
66
+ "Content-Type": "application/json",
67
+ };
68
+ }
69
+ /**
70
+ * Performs an HTTP request and returns the response.
71
+ * Mirrors Go's doRequest method.
72
+ */
73
+ async doRequest(method, url, body, options) {
74
+ const controller = new AbortController();
75
+ const externalSignal = options?.signal;
76
+ // Link external signal to our controller
77
+ if (externalSignal) {
78
+ if (externalSignal.aborted) {
79
+ controller.abort(externalSignal.reason);
80
+ }
81
+ else {
82
+ externalSignal.addEventListener("abort", () => controller.abort(externalSignal.reason));
83
+ }
84
+ }
85
+ // Set up timeout
86
+ const timeoutId = setTimeout(() => controller.abort("request timeout"), this.timeoutMs);
87
+ try {
88
+ const response = await this.fetchFn(url, {
89
+ method,
90
+ headers: this.getHeaders(),
91
+ body: body !== undefined ? JSON.stringify(body) : undefined,
92
+ signal: controller.signal,
93
+ });
94
+ return response;
95
+ }
96
+ catch (err) {
97
+ throw new Error(`request failed: ${err?.message || err}`);
98
+ }
99
+ finally {
100
+ clearTimeout(timeoutId);
101
+ }
102
+ }
103
+ /**
104
+ * Reads the response body and checks the status code.
105
+ * Mirrors Go's handleResponse method.
106
+ */
107
+ async handleResponse(resp, ...expectedStatusCodes) {
108
+ const bodyText = await resp.text();
109
+ if (expectedStatusCodes.length === 0) {
110
+ expectedStatusCodes = [200, 201];
111
+ }
112
+ if (!expectedStatusCodes.includes(resp.status)) {
113
+ let bodyStr = bodyText;
114
+ if (bodyStr.length > 500) {
115
+ bodyStr = bodyStr.substring(0, 500) + "...";
116
+ }
117
+ throw new Error(`unexpected status code ${resp.status}: ${resp.statusText} (response: ${bodyStr})`);
118
+ }
119
+ }
120
+ /**
121
+ * Inserts a new record into the specified table.
122
+ * The data is wrapped in an array as the server expects an array.
123
+ * Mirrors Go's Insert method.
124
+ */
125
+ async insert(tableId, data, options) {
126
+ if (data === null || data === undefined) {
127
+ throw new Error("data cannot be nil");
128
+ }
129
+ const requestURL = this.buildURL(tableId);
130
+ const resp = await this.doRequest("POST", requestURL, [data], options);
131
+ await this.handleResponse(resp, 201, 200);
132
+ }
133
+ /**
134
+ * Inserts multiple records into the specified table.
135
+ * Mirrors Go's BulkInsert method.
136
+ */
137
+ async bulkInsert(tableId, data, options) {
138
+ if (!data || data.length === 0) {
139
+ throw new Error("data cannot be empty");
140
+ }
141
+ const requestURL = this.buildURL(tableId);
142
+ const resp = await this.doRequest("POST", requestURL, data, options);
143
+ await this.handleResponse(resp, 201, 200);
144
+ }
145
+ /**
146
+ * Retrieves records from the specified table matching the query.
147
+ * Uses POST request to /search endpoint with query in the request body.
148
+ * Mirrors Go's Find method.
149
+ */
150
+ async find(tableId, query, options) {
151
+ const requestURL = this.buildSearchURL(tableId);
152
+ const requestBody = { filters: query };
153
+ const resp = await this.doRequest("POST", requestURL, requestBody, options);
154
+ if (resp.status !== 200) {
155
+ let bodyStr = await resp.text();
156
+ if (bodyStr.length > 500) {
157
+ bodyStr = bodyStr.substring(0, 500) + "...";
158
+ }
159
+ throw new Error(`unexpected status code ${resp.status}: ${resp.statusText} (response: ${bodyStr})`);
160
+ }
161
+ const results = await resp.json();
162
+ if (!results.success) {
163
+ throw new Error("failed to get data");
164
+ }
165
+ const resultsT = new Array(results.data.count);
166
+ for (let i = 0; i < results.data.list.length; i++) {
167
+ resultsT[i] = results.data.list[i].data;
168
+ }
169
+ return resultsT;
170
+ }
171
+ /**
172
+ * Removes records from the specified table matching the query.
173
+ * Uses DELETE request with query in the request body.
174
+ * Mirrors Go's Delete method.
175
+ */
176
+ async delete(tableId, query, options) {
177
+ const requestURL = this.buildURL(tableId);
178
+ const requestBody = { filters: query };
179
+ const resp = await this.doRequest("DELETE", requestURL, requestBody, options);
180
+ await this.handleResponse(resp, 200, 204);
181
+ }
182
+ /**
183
+ * Updates records in the specified table matching the query with the provided data.
184
+ * Uses PUT request with query and data in the request body.
185
+ * Mirrors Go's Update method.
186
+ */
187
+ async update(tableId, query, data, options) {
188
+ if (data === null || data === undefined) {
189
+ throw new Error("data cannot be nil");
190
+ }
191
+ const requestURL = this.buildURL(tableId);
192
+ const requestBody = { filters: query, data };
193
+ const resp = await this.doRequest("PUT", requestURL, requestBody, options);
194
+ if (resp.status !== 200) {
195
+ let bodyStr = await resp.text();
196
+ if (bodyStr.length > 500) {
197
+ bodyStr = bodyStr.substring(0, 500) + "...";
198
+ }
199
+ throw new Error(`unexpected status code ${resp.status}: ${resp.statusText} (response: ${bodyStr})`);
200
+ }
201
+ const results = await resp.json();
202
+ if (!results.success) {
203
+ throw new Error("failed to update data");
204
+ }
205
+ const resultsT = new Array(results.data.length);
206
+ for (let i = 0; i < results.data.length; i++) {
207
+ resultsT[i] = results.data[i].data;
208
+ }
209
+ return resultsT;
210
+ }
211
+ }
212
+ exports.CnipsTableAccessor = CnipsTableAccessor;
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@innacyraja/table-ts",
3
+ "version": "1.0.0",
4
+ "description": "cnips Table Accessor - TypeScript client for cnips table operations",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "prepare": "npm run build",
12
+ "build": "tsc",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest",
15
+ "test:coverage": "vitest run --coverage",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": [
19
+ "cnips",
20
+ "table",
21
+ "accessor"
22
+ ],
23
+ "license": "ISC",
24
+ "devDependencies": {
25
+ "typescript": "^5.7.0",
26
+ "vitest": "^3.0.0"
27
+ },
28
+ "dependencies": {
29
+ "@cnips/simplelog": "^1.0.0"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ }
34
+ }