@tinycloud/sdk-services 2.0.1 → 2.0.2-beta.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.
- package/dist/{types.d.ts → BaseService-D9BFm_rV.d.cts} +179 -27
- package/dist/BaseService-D9BFm_rV.d.ts +440 -0
- package/dist/index.cjs +3221 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1843 -0
- package/dist/index.d.ts +1826 -41
- package/dist/index.js +3136 -58
- package/dist/index.js.map +1 -1
- package/dist/kv/index.cjs +909 -0
- package/dist/kv/index.cjs.map +1 -0
- package/dist/kv/index.d.cts +748 -0
- package/dist/kv/index.d.ts +745 -7
- package/dist/kv/index.js +877 -9
- package/dist/kv/index.js.map +1 -1
- package/dist/sql/index.cjs +596 -0
- package/dist/sql/index.cjs.map +1 -0
- package/dist/sql/index.d.cts +228 -0
- package/dist/sql/index.d.ts +225 -7
- package/dist/sql/index.js +566 -8
- package/dist/sql/index.js.map +1 -1
- package/package.json +7 -6
- package/dist/base/BaseService.d.ts +0 -151
- package/dist/base/BaseService.d.ts.map +0 -1
- package/dist/base/BaseService.js +0 -221
- package/dist/base/BaseService.js.map +0 -1
- package/dist/base/index.d.ts +0 -6
- package/dist/base/index.d.ts.map +0 -1
- package/dist/base/index.js +0 -6
- package/dist/base/index.js.map +0 -1
- package/dist/base/types.d.ts +0 -36
- package/dist/base/types.d.ts.map +0 -1
- package/dist/base/types.js +0 -7
- package/dist/base/types.js.map +0 -1
- package/dist/context.d.ts +0 -142
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -218
- package/dist/context.js.map +0 -1
- package/dist/duckdb/DuckDbDatabaseHandle.d.ts +0 -23
- package/dist/duckdb/DuckDbDatabaseHandle.d.ts.map +0 -1
- package/dist/duckdb/DuckDbDatabaseHandle.js +0 -36
- package/dist/duckdb/DuckDbDatabaseHandle.js.map +0 -1
- package/dist/duckdb/DuckDbService.d.ts +0 -50
- package/dist/duckdb/DuckDbService.d.ts.map +0 -1
- package/dist/duckdb/DuckDbService.js +0 -285
- package/dist/duckdb/DuckDbService.js.map +0 -1
- package/dist/duckdb/IDuckDbService.d.ts +0 -84
- package/dist/duckdb/IDuckDbService.d.ts.map +0 -1
- package/dist/duckdb/IDuckDbService.js +0 -7
- package/dist/duckdb/IDuckDbService.js.map +0 -1
- package/dist/duckdb/index.d.ts +0 -10
- package/dist/duckdb/index.d.ts.map +0 -1
- package/dist/duckdb/index.js +0 -9
- package/dist/duckdb/index.js.map +0 -1
- package/dist/duckdb/types.d.ts +0 -148
- package/dist/duckdb/types.d.ts.map +0 -1
- package/dist/duckdb/types.js +0 -19
- package/dist/duckdb/types.js.map +0 -1
- package/dist/errors.d.ts +0 -62
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -149
- package/dist/errors.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/kv/IKVService.d.ts +0 -148
- package/dist/kv/IKVService.d.ts.map +0 -1
- package/dist/kv/IKVService.js +0 -8
- package/dist/kv/IKVService.js.map +0 -1
- package/dist/kv/KVService.d.ts +0 -155
- package/dist/kv/KVService.d.ts.map +0 -1
- package/dist/kv/KVService.js +0 -419
- package/dist/kv/KVService.js.map +0 -1
- package/dist/kv/PrefixedKVService.d.ts +0 -246
- package/dist/kv/PrefixedKVService.d.ts.map +0 -1
- package/dist/kv/PrefixedKVService.js +0 -145
- package/dist/kv/PrefixedKVService.js.map +0 -1
- package/dist/kv/index.d.ts.map +0 -1
- package/dist/kv/types.d.ts +0 -204
- package/dist/kv/types.d.ts.map +0 -1
- package/dist/kv/types.js +0 -16
- package/dist/kv/types.js.map +0 -1
- package/dist/quota/TinyCloudQuota.d.ts +0 -27
- package/dist/quota/TinyCloudQuota.d.ts.map +0 -1
- package/dist/quota/TinyCloudQuota.js +0 -31
- package/dist/quota/TinyCloudQuota.js.map +0 -1
- package/dist/quota/index.d.ts +0 -3
- package/dist/quota/index.d.ts.map +0 -1
- package/dist/quota/index.js +0 -2
- package/dist/quota/index.js.map +0 -1
- package/dist/sql/DatabaseHandle.d.ts +0 -20
- package/dist/sql/DatabaseHandle.d.ts.map +0 -1
- package/dist/sql/DatabaseHandle.js +0 -27
- package/dist/sql/DatabaseHandle.js.map +0 -1
- package/dist/sql/ISQLService.d.ts +0 -67
- package/dist/sql/ISQLService.d.ts.map +0 -1
- package/dist/sql/ISQLService.js +0 -7
- package/dist/sql/ISQLService.js.map +0 -1
- package/dist/sql/SQLService.d.ts +0 -44
- package/dist/sql/SQLService.d.ts.map +0 -1
- package/dist/sql/SQLService.js +0 -216
- package/dist/sql/SQLService.js.map +0 -1
- package/dist/sql/index.d.ts.map +0 -1
- package/dist/sql/types.d.ts +0 -102
- package/dist/sql/types.d.ts.map +0 -1
- package/dist/sql/types.js +0 -21
- package/dist/sql/types.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -94
- package/dist/types.js.map +0 -1
- package/dist/types.schema.d.ts +0 -712
- package/dist/types.schema.d.ts.map +0 -1
- package/dist/types.schema.js +0 -342
- package/dist/types.schema.js.map +0 -1
- package/dist/types.schema.test.d.ts +0 -5
- package/dist/types.schema.test.d.ts.map +0 -1
- package/dist/types.schema.test.js +0 -677
- package/dist/types.schema.test.js.map +0 -1
- package/dist/vault/DataVaultService.d.ts +0 -267
- package/dist/vault/DataVaultService.d.ts.map +0 -1
- package/dist/vault/DataVaultService.js +0 -1040
- package/dist/vault/DataVaultService.js.map +0 -1
- package/dist/vault/IDataVaultService.d.ts +0 -158
- package/dist/vault/IDataVaultService.d.ts.map +0 -1
- package/dist/vault/IDataVaultService.js +0 -8
- package/dist/vault/IDataVaultService.js.map +0 -1
- package/dist/vault/SignatureCache.d.ts +0 -20
- package/dist/vault/SignatureCache.d.ts.map +0 -1
- package/dist/vault/SignatureCache.js +0 -167
- package/dist/vault/SignatureCache.js.map +0 -1
- package/dist/vault/createVaultCrypto.d.ts +0 -16
- package/dist/vault/createVaultCrypto.d.ts.map +0 -1
- package/dist/vault/createVaultCrypto.js +0 -12
- package/dist/vault/createVaultCrypto.js.map +0 -1
- package/dist/vault/index.d.ts +0 -11
- package/dist/vault/index.d.ts.map +0 -1
- package/dist/vault/index.js +0 -12
- package/dist/vault/index.js.map +0 -1
- package/dist/vault/types.d.ts +0 -141
- package/dist/vault/types.d.ts.map +0 -1
- package/dist/vault/types.js +0 -31
- package/dist/vault/types.js.map +0 -1
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/sql/index.ts
|
|
21
|
+
var sql_exports = {};
|
|
22
|
+
__export(sql_exports, {
|
|
23
|
+
DatabaseHandle: () => DatabaseHandle,
|
|
24
|
+
SQLAction: () => SQLAction,
|
|
25
|
+
SQLService: () => SQLService
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(sql_exports);
|
|
28
|
+
|
|
29
|
+
// src/types.ts
|
|
30
|
+
var ErrorCodes = {
|
|
31
|
+
// Common errors
|
|
32
|
+
NOT_FOUND: "NOT_FOUND",
|
|
33
|
+
AUTH_EXPIRED: "AUTH_EXPIRED",
|
|
34
|
+
AUTH_REQUIRED: "AUTH_REQUIRED",
|
|
35
|
+
AUTH_UNAUTHORIZED: "AUTH_UNAUTHORIZED",
|
|
36
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
37
|
+
TIMEOUT: "TIMEOUT",
|
|
38
|
+
ABORTED: "ABORTED",
|
|
39
|
+
INVALID_INPUT: "INVALID_INPUT",
|
|
40
|
+
PERMISSION_DENIED: "PERMISSION_DENIED",
|
|
41
|
+
// KV-specific errors
|
|
42
|
+
KV_NOT_FOUND: "KV_NOT_FOUND",
|
|
43
|
+
KV_WRITE_FAILED: "KV_WRITE_FAILED",
|
|
44
|
+
// SQL-specific errors
|
|
45
|
+
SQL_ERROR: "SQL_ERROR",
|
|
46
|
+
SQL_PERMISSION_DENIED: "SQL_PERMISSION_DENIED",
|
|
47
|
+
SQL_DATABASE_NOT_FOUND: "SQL_DATABASE_NOT_FOUND",
|
|
48
|
+
SQL_RESPONSE_TOO_LARGE: "SQL_RESPONSE_TOO_LARGE",
|
|
49
|
+
SQL_QUOTA_EXCEEDED: "SQL_QUOTA_EXCEEDED",
|
|
50
|
+
SQL_INVALID_STATEMENT: "SQL_INVALID_STATEMENT",
|
|
51
|
+
SQL_SCHEMA_ERROR: "SQL_SCHEMA_ERROR",
|
|
52
|
+
SQL_READONLY_VIOLATION: "SQL_READONLY_VIOLATION",
|
|
53
|
+
// Storage quota errors
|
|
54
|
+
STORAGE_QUOTA_EXCEEDED: "STORAGE_QUOTA_EXCEEDED",
|
|
55
|
+
STORAGE_LIMIT_REACHED: "STORAGE_LIMIT_REACHED",
|
|
56
|
+
// DuckDB-specific errors
|
|
57
|
+
DUCKDB_ERROR: "DUCKDB_ERROR",
|
|
58
|
+
DUCKDB_PERMISSION_DENIED: "DUCKDB_PERMISSION_DENIED",
|
|
59
|
+
DUCKDB_DATABASE_NOT_FOUND: "DUCKDB_DATABASE_NOT_FOUND",
|
|
60
|
+
DUCKDB_RESPONSE_TOO_LARGE: "DUCKDB_RESPONSE_TOO_LARGE",
|
|
61
|
+
DUCKDB_QUOTA_EXCEEDED: "DUCKDB_QUOTA_EXCEEDED",
|
|
62
|
+
DUCKDB_INVALID_STATEMENT: "DUCKDB_INVALID_STATEMENT",
|
|
63
|
+
DUCKDB_SCHEMA_ERROR: "DUCKDB_SCHEMA_ERROR",
|
|
64
|
+
DUCKDB_READONLY_VIOLATION: "DUCKDB_READONLY_VIOLATION"
|
|
65
|
+
};
|
|
66
|
+
var defaultRetryPolicy = {
|
|
67
|
+
maxAttempts: 3,
|
|
68
|
+
backoff: "exponential",
|
|
69
|
+
baseDelayMs: 1e3,
|
|
70
|
+
maxDelayMs: 1e4,
|
|
71
|
+
retryableErrors: [ErrorCodes.NETWORK_ERROR, ErrorCodes.TIMEOUT]
|
|
72
|
+
};
|
|
73
|
+
var TelemetryEvents = {
|
|
74
|
+
SERVICE_REQUEST: "service.request",
|
|
75
|
+
SERVICE_RESPONSE: "service.response",
|
|
76
|
+
SERVICE_ERROR: "service.error",
|
|
77
|
+
SERVICE_RETRY: "service.retry",
|
|
78
|
+
SESSION_CHANGED: "session.changed",
|
|
79
|
+
SESSION_EXPIRED: "session.expired"
|
|
80
|
+
};
|
|
81
|
+
function ok(data) {
|
|
82
|
+
return { ok: true, data };
|
|
83
|
+
}
|
|
84
|
+
function err(error) {
|
|
85
|
+
return { ok: false, error };
|
|
86
|
+
}
|
|
87
|
+
function serviceError(code, message, service, options) {
|
|
88
|
+
return {
|
|
89
|
+
code,
|
|
90
|
+
message,
|
|
91
|
+
service,
|
|
92
|
+
cause: options?.cause,
|
|
93
|
+
meta: options?.meta
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/errors.ts
|
|
98
|
+
function authRequiredError(service) {
|
|
99
|
+
return {
|
|
100
|
+
code: ErrorCodes.AUTH_REQUIRED,
|
|
101
|
+
message: "Authentication required. Please sign in first.",
|
|
102
|
+
service
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function timeoutError(service) {
|
|
106
|
+
return {
|
|
107
|
+
code: ErrorCodes.TIMEOUT,
|
|
108
|
+
message: "Request timed out.",
|
|
109
|
+
service
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function abortedError(service) {
|
|
113
|
+
return {
|
|
114
|
+
code: ErrorCodes.ABORTED,
|
|
115
|
+
message: "Request was aborted.",
|
|
116
|
+
service
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function parseAuthError(responseText) {
|
|
120
|
+
const match = responseText.match(/^Unauthorized Action:\s*(.+?)\s*\/\s*(tinycloud\.\S+)$/m);
|
|
121
|
+
if (match) {
|
|
122
|
+
return { resource: match[1].trim(), action: match[2].trim() };
|
|
123
|
+
}
|
|
124
|
+
return {};
|
|
125
|
+
}
|
|
126
|
+
function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
|
|
127
|
+
if (error instanceof Error) {
|
|
128
|
+
if (error.name === "AbortError") {
|
|
129
|
+
return abortedError(service);
|
|
130
|
+
}
|
|
131
|
+
if (error.name === "TimeoutError" || error.message.toLowerCase().includes("timeout")) {
|
|
132
|
+
return timeoutError(service);
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
code: defaultCode,
|
|
136
|
+
message: error.message,
|
|
137
|
+
service,
|
|
138
|
+
cause: error
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
code: defaultCode,
|
|
143
|
+
message: String(error),
|
|
144
|
+
service
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/base/BaseService.ts
|
|
149
|
+
var BaseService = class {
|
|
150
|
+
constructor() {
|
|
151
|
+
/**
|
|
152
|
+
* Abort controller for this service's operations.
|
|
153
|
+
* Reset on sign-out.
|
|
154
|
+
*/
|
|
155
|
+
this.abortController = new AbortController();
|
|
156
|
+
/**
|
|
157
|
+
* Service-specific configuration.
|
|
158
|
+
*/
|
|
159
|
+
this._config = {};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get the service configuration.
|
|
163
|
+
*/
|
|
164
|
+
get config() {
|
|
165
|
+
return this._config;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Initialize the service with context.
|
|
169
|
+
* Called by the SDK after instantiation.
|
|
170
|
+
*
|
|
171
|
+
* @param context - The service context
|
|
172
|
+
*/
|
|
173
|
+
initialize(context) {
|
|
174
|
+
this.context = context;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Called when session changes (sign-in, sign-out, refresh).
|
|
178
|
+
* Override in subclasses to handle session changes.
|
|
179
|
+
*
|
|
180
|
+
* @param session - The new session, or null if signed out
|
|
181
|
+
*/
|
|
182
|
+
onSessionChange(session) {
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Called when SDK signs out.
|
|
186
|
+
* Aborts all pending operations.
|
|
187
|
+
*/
|
|
188
|
+
onSignOut() {
|
|
189
|
+
this.abortController.abort();
|
|
190
|
+
this.abortController = new AbortController();
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get the abort signal for this service.
|
|
194
|
+
* Combines the service-level abort with context-level abort.
|
|
195
|
+
*/
|
|
196
|
+
get abortSignal() {
|
|
197
|
+
return this.abortController.signal;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Check if the service is authenticated.
|
|
201
|
+
*/
|
|
202
|
+
get isAuthenticated() {
|
|
203
|
+
return this.context?.isAuthenticated ?? false;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get the current session.
|
|
207
|
+
* Throws if not authenticated.
|
|
208
|
+
*/
|
|
209
|
+
get session() {
|
|
210
|
+
if (!this.context?.session) {
|
|
211
|
+
throw new Error("Not authenticated");
|
|
212
|
+
}
|
|
213
|
+
return this.context.session;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Check authentication and return error result if not authenticated.
|
|
217
|
+
* Use this at the start of methods that require authentication.
|
|
218
|
+
*
|
|
219
|
+
* @returns true if authenticated, false otherwise
|
|
220
|
+
*/
|
|
221
|
+
requireAuth() {
|
|
222
|
+
return this.isAuthenticated;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Emit a telemetry event.
|
|
226
|
+
*
|
|
227
|
+
* @param event - Event name
|
|
228
|
+
* @param data - Event data
|
|
229
|
+
*/
|
|
230
|
+
emit(event, data) {
|
|
231
|
+
this.context?.emit(event, data);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Emit a service request event.
|
|
235
|
+
*
|
|
236
|
+
* @param action - The action being performed
|
|
237
|
+
* @param key - Optional key/path being accessed
|
|
238
|
+
*/
|
|
239
|
+
emitRequest(action, key) {
|
|
240
|
+
this.emit(TelemetryEvents.SERVICE_REQUEST, {
|
|
241
|
+
service: this.getServiceName(),
|
|
242
|
+
action,
|
|
243
|
+
key,
|
|
244
|
+
timestamp: Date.now()
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Emit a service response event.
|
|
249
|
+
*
|
|
250
|
+
* @param action - The action that was performed
|
|
251
|
+
* @param ok - Whether the request was successful
|
|
252
|
+
* @param startTime - Start time for duration calculation
|
|
253
|
+
* @param status - Optional HTTP status code
|
|
254
|
+
*/
|
|
255
|
+
emitResponse(action, ok2, startTime, status) {
|
|
256
|
+
this.emit(TelemetryEvents.SERVICE_RESPONSE, {
|
|
257
|
+
service: this.getServiceName(),
|
|
258
|
+
action,
|
|
259
|
+
ok: ok2,
|
|
260
|
+
duration: Date.now() - startTime,
|
|
261
|
+
status
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Emit a service error event.
|
|
266
|
+
*
|
|
267
|
+
* @param error - The service error
|
|
268
|
+
*/
|
|
269
|
+
emitError(error) {
|
|
270
|
+
this.emit(TelemetryEvents.SERVICE_ERROR, {
|
|
271
|
+
service: this.getServiceName(),
|
|
272
|
+
error
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Get the service name from the static property.
|
|
277
|
+
* Subclasses must define static serviceName.
|
|
278
|
+
*/
|
|
279
|
+
getServiceName() {
|
|
280
|
+
return this.constructor.serviceName;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Create a combined abort signal from multiple sources.
|
|
284
|
+
*
|
|
285
|
+
* @param signals - Additional abort signals to combine
|
|
286
|
+
* @returns A combined abort signal
|
|
287
|
+
*/
|
|
288
|
+
combineSignals(...signals) {
|
|
289
|
+
const controller = new AbortController();
|
|
290
|
+
const allSignals = [this.abortSignal, ...signals.filter(Boolean)];
|
|
291
|
+
for (const signal of allSignals) {
|
|
292
|
+
if (signal.aborted) {
|
|
293
|
+
controller.abort(signal.reason);
|
|
294
|
+
return controller.signal;
|
|
295
|
+
}
|
|
296
|
+
signal.addEventListener("abort", () => controller.abort(signal.reason), {
|
|
297
|
+
once: true
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return controller.signal;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Wrap an operation with error handling and telemetry.
|
|
304
|
+
*
|
|
305
|
+
* @param action - The action name for telemetry
|
|
306
|
+
* @param key - Optional key for telemetry
|
|
307
|
+
* @param operation - The operation to execute
|
|
308
|
+
* @returns Result of the operation
|
|
309
|
+
*/
|
|
310
|
+
async withTelemetry(action, key, operation) {
|
|
311
|
+
const startTime = Date.now();
|
|
312
|
+
this.emitRequest(action, key);
|
|
313
|
+
try {
|
|
314
|
+
const result = await operation();
|
|
315
|
+
if (result.ok) {
|
|
316
|
+
this.emitResponse(action, true, startTime);
|
|
317
|
+
} else {
|
|
318
|
+
this.emitResponse(action, false, startTime);
|
|
319
|
+
this.emitError(result.error);
|
|
320
|
+
}
|
|
321
|
+
return result;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
const serviceError2 = wrapError(this.getServiceName(), error);
|
|
324
|
+
this.emitResponse(action, false, startTime);
|
|
325
|
+
this.emitError(serviceError2);
|
|
326
|
+
return err(serviceError2);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
// src/sql/DatabaseHandle.ts
|
|
332
|
+
var DatabaseHandle = class {
|
|
333
|
+
constructor(service, name) {
|
|
334
|
+
this.service = service;
|
|
335
|
+
this.name = name;
|
|
336
|
+
}
|
|
337
|
+
async query(sql, params, options) {
|
|
338
|
+
return this.service.queryOnDb(this.name, sql, params, options);
|
|
339
|
+
}
|
|
340
|
+
async execute(sql, params, options) {
|
|
341
|
+
return this.service.executeOnDb(this.name, sql, params, options);
|
|
342
|
+
}
|
|
343
|
+
async batch(statements, options) {
|
|
344
|
+
return this.service.batchOnDb(this.name, statements, options);
|
|
345
|
+
}
|
|
346
|
+
async executeStatement(name, params, options) {
|
|
347
|
+
return this.service.executeStatementOnDb(this.name, name, params, options);
|
|
348
|
+
}
|
|
349
|
+
async export(options) {
|
|
350
|
+
return this.service.exportDb(this.name, options);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// src/sql/types.ts
|
|
355
|
+
var SQLAction = {
|
|
356
|
+
READ: "tinycloud.sql/read",
|
|
357
|
+
WRITE: "tinycloud.sql/write",
|
|
358
|
+
ADMIN: "tinycloud.sql/admin",
|
|
359
|
+
SELECT: "tinycloud.sql/select",
|
|
360
|
+
INSERT: "tinycloud.sql/insert",
|
|
361
|
+
UPDATE: "tinycloud.sql/update",
|
|
362
|
+
DELETE: "tinycloud.sql/delete",
|
|
363
|
+
EXECUTE: "tinycloud.sql/execute",
|
|
364
|
+
EXPORT: "tinycloud.sql/export",
|
|
365
|
+
ALL: "tinycloud.sql/*"
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
// src/sql/SQLService.ts
|
|
369
|
+
var SQLService = class extends BaseService {
|
|
370
|
+
constructor(config = {}) {
|
|
371
|
+
super();
|
|
372
|
+
this._config = config;
|
|
373
|
+
}
|
|
374
|
+
get config() {
|
|
375
|
+
return this._config;
|
|
376
|
+
}
|
|
377
|
+
get defaultDbName() {
|
|
378
|
+
return this._config.defaultDatabase ?? "default";
|
|
379
|
+
}
|
|
380
|
+
get host() {
|
|
381
|
+
return this.context.hosts[0];
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Get a handle to a named database.
|
|
385
|
+
*/
|
|
386
|
+
db(name) {
|
|
387
|
+
return new DatabaseHandle(this, name ?? this.defaultDbName);
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Shortcut: query the default database.
|
|
391
|
+
*/
|
|
392
|
+
async query(sql, params, options) {
|
|
393
|
+
return this.queryOnDb(this.defaultDbName, sql, params, options);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Shortcut: execute on the default database.
|
|
397
|
+
*/
|
|
398
|
+
async execute(sql, params, options) {
|
|
399
|
+
return this.executeOnDb(this.defaultDbName, sql, params, options);
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Shortcut: batch on the default database.
|
|
403
|
+
*/
|
|
404
|
+
async batch(statements, options) {
|
|
405
|
+
return this.batchOnDb(this.defaultDbName, statements, options);
|
|
406
|
+
}
|
|
407
|
+
// === Internal methods called by DatabaseHandle ===
|
|
408
|
+
async queryOnDb(dbName, sql, params, options) {
|
|
409
|
+
return this.withTelemetry("query", dbName, async () => {
|
|
410
|
+
if (!this.requireAuth()) {
|
|
411
|
+
return err(authRequiredError("sql"));
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
const response = await this.invokeSQL(
|
|
415
|
+
dbName,
|
|
416
|
+
SQLAction.READ,
|
|
417
|
+
{ action: "query", sql, params: params ?? [] },
|
|
418
|
+
options?.signal
|
|
419
|
+
);
|
|
420
|
+
if (!response.ok) {
|
|
421
|
+
return this.handleErrorResponse(response, "query");
|
|
422
|
+
}
|
|
423
|
+
const data = await response.json();
|
|
424
|
+
return ok(data);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
return err(wrapError("sql", error));
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
async executeOnDb(dbName, sql, params, options) {
|
|
431
|
+
return this.withTelemetry("execute", dbName, async () => {
|
|
432
|
+
if (!this.requireAuth()) {
|
|
433
|
+
return err(authRequiredError("sql"));
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
const body = {
|
|
437
|
+
action: "execute",
|
|
438
|
+
sql,
|
|
439
|
+
params: params ?? []
|
|
440
|
+
};
|
|
441
|
+
if (options?.schema) {
|
|
442
|
+
body.schema = options.schema;
|
|
443
|
+
}
|
|
444
|
+
const response = await this.invokeSQL(
|
|
445
|
+
dbName,
|
|
446
|
+
SQLAction.WRITE,
|
|
447
|
+
body,
|
|
448
|
+
options?.signal
|
|
449
|
+
);
|
|
450
|
+
if (!response.ok) {
|
|
451
|
+
return this.handleErrorResponse(response, "execute");
|
|
452
|
+
}
|
|
453
|
+
const data = await response.json();
|
|
454
|
+
return ok(data);
|
|
455
|
+
} catch (error) {
|
|
456
|
+
return err(wrapError("sql", error));
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
async batchOnDb(dbName, statements, options) {
|
|
461
|
+
return this.withTelemetry("batch", dbName, async () => {
|
|
462
|
+
if (!this.requireAuth()) {
|
|
463
|
+
return err(authRequiredError("sql"));
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
const response = await this.invokeSQL(
|
|
467
|
+
dbName,
|
|
468
|
+
SQLAction.WRITE,
|
|
469
|
+
{ action: "batch", statements },
|
|
470
|
+
options?.signal
|
|
471
|
+
);
|
|
472
|
+
if (!response.ok) {
|
|
473
|
+
return this.handleErrorResponse(response, "batch");
|
|
474
|
+
}
|
|
475
|
+
const data = await response.json();
|
|
476
|
+
return ok(data);
|
|
477
|
+
} catch (error) {
|
|
478
|
+
return err(wrapError("sql", error));
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
async executeStatementOnDb(dbName, name, params, options) {
|
|
483
|
+
return this.withTelemetry("executeStatement", dbName, async () => {
|
|
484
|
+
if (!this.requireAuth()) {
|
|
485
|
+
return err(authRequiredError("sql"));
|
|
486
|
+
}
|
|
487
|
+
try {
|
|
488
|
+
const response = await this.invokeSQL(
|
|
489
|
+
dbName,
|
|
490
|
+
SQLAction.EXECUTE,
|
|
491
|
+
{ action: "execute_statement", name, params: params ?? [] },
|
|
492
|
+
options?.signal
|
|
493
|
+
);
|
|
494
|
+
if (!response.ok) {
|
|
495
|
+
return this.handleErrorResponse(response, "executeStatement");
|
|
496
|
+
}
|
|
497
|
+
const data = await response.json();
|
|
498
|
+
return ok(data);
|
|
499
|
+
} catch (error) {
|
|
500
|
+
return err(wrapError("sql", error));
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
async exportDb(dbName, options) {
|
|
505
|
+
return this.withTelemetry("export", dbName, async () => {
|
|
506
|
+
if (!this.requireAuth()) {
|
|
507
|
+
return err(authRequiredError("sql"));
|
|
508
|
+
}
|
|
509
|
+
try {
|
|
510
|
+
const response = await this.invokeSQL(
|
|
511
|
+
dbName,
|
|
512
|
+
SQLAction.EXPORT,
|
|
513
|
+
{ action: "export" },
|
|
514
|
+
options?.signal
|
|
515
|
+
);
|
|
516
|
+
if (!response.ok) {
|
|
517
|
+
return this.handleErrorResponse(response, "export");
|
|
518
|
+
}
|
|
519
|
+
const resp = response;
|
|
520
|
+
if (typeof resp.blob === "function") {
|
|
521
|
+
const blob = await resp.blob();
|
|
522
|
+
return ok(blob);
|
|
523
|
+
}
|
|
524
|
+
const text = await response.text();
|
|
525
|
+
return ok(text);
|
|
526
|
+
} catch (error) {
|
|
527
|
+
return err(wrapError("sql", error));
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
// === Private helpers ===
|
|
532
|
+
async invokeSQL(dbName, action, body, signal) {
|
|
533
|
+
const session = this.context.session;
|
|
534
|
+
const headers = this.context.invoke(session, "sql", dbName, action);
|
|
535
|
+
return this.context.fetch(`${this.host}/invoke`, {
|
|
536
|
+
method: "POST",
|
|
537
|
+
headers: {
|
|
538
|
+
...headers,
|
|
539
|
+
"Content-Type": "application/json"
|
|
540
|
+
},
|
|
541
|
+
body: JSON.stringify(body),
|
|
542
|
+
signal: this.combineSignals(signal)
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
async handleErrorResponse(response, operation) {
|
|
546
|
+
const errorText = await response.text();
|
|
547
|
+
let errorBody = {};
|
|
548
|
+
try {
|
|
549
|
+
errorBody = JSON.parse(errorText);
|
|
550
|
+
} catch {
|
|
551
|
+
}
|
|
552
|
+
const errorCode = this.mapHttpStatusToErrorCode(
|
|
553
|
+
response.status,
|
|
554
|
+
errorBody.error
|
|
555
|
+
);
|
|
556
|
+
const message = errorBody.message || `SQL ${operation} failed: ${response.status} - ${errorText}`;
|
|
557
|
+
const meta = { status: response.status, statusText: response.statusText };
|
|
558
|
+
if (response.status === 401) {
|
|
559
|
+
const { resource, action } = parseAuthError(errorText);
|
|
560
|
+
if (action) meta.requiredAction = action;
|
|
561
|
+
if (resource) meta.resource = resource;
|
|
562
|
+
}
|
|
563
|
+
return err(
|
|
564
|
+
serviceError(errorCode, message, "sql", { meta })
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
mapHttpStatusToErrorCode(status, serverError) {
|
|
568
|
+
switch (status) {
|
|
569
|
+
case 400:
|
|
570
|
+
return ErrorCodes.SQL_ERROR;
|
|
571
|
+
case 401:
|
|
572
|
+
return ErrorCodes.AUTH_UNAUTHORIZED;
|
|
573
|
+
case 403:
|
|
574
|
+
if (serverError === "sql_readonly_violation") {
|
|
575
|
+
return ErrorCodes.SQL_READONLY_VIOLATION;
|
|
576
|
+
}
|
|
577
|
+
return ErrorCodes.SQL_PERMISSION_DENIED;
|
|
578
|
+
case 404:
|
|
579
|
+
return ErrorCodes.SQL_DATABASE_NOT_FOUND;
|
|
580
|
+
case 413:
|
|
581
|
+
return ErrorCodes.SQL_RESPONSE_TOO_LARGE;
|
|
582
|
+
case 429:
|
|
583
|
+
return ErrorCodes.SQL_QUOTA_EXCEEDED;
|
|
584
|
+
default:
|
|
585
|
+
return ErrorCodes.NETWORK_ERROR;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
SQLService.serviceName = "sql";
|
|
590
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
591
|
+
0 && (module.exports = {
|
|
592
|
+
DatabaseHandle,
|
|
593
|
+
SQLAction,
|
|
594
|
+
SQLService
|
|
595
|
+
});
|
|
596
|
+
//# sourceMappingURL=index.cjs.map
|