mneme-sdk 0.0.1 → 0.3.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/index.cjs +122 -0
- package/dist/index.d.cts +121 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.js +122 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -74,6 +74,24 @@ function randomNonce() {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// src/client.ts
|
|
77
|
+
async function toUint8Array(input) {
|
|
78
|
+
if (input instanceof Uint8Array) return input;
|
|
79
|
+
if (input instanceof ArrayBuffer) return new Uint8Array(input);
|
|
80
|
+
if (typeof Blob !== "undefined" && input instanceof Blob) {
|
|
81
|
+
return new Uint8Array(await input.arrayBuffer());
|
|
82
|
+
}
|
|
83
|
+
if (typeof input === "string") return new TextEncoder().encode(input);
|
|
84
|
+
throw new Error("unsupported file type for storage.upload");
|
|
85
|
+
}
|
|
86
|
+
function uint8ToBase64(bytes) {
|
|
87
|
+
const CHUNK = 32768;
|
|
88
|
+
const parts = [];
|
|
89
|
+
for (let i = 0; i < bytes.byteLength; i += CHUNK) {
|
|
90
|
+
parts.push(String.fromCharCode(...bytes.subarray(i, i + CHUNK)));
|
|
91
|
+
}
|
|
92
|
+
if (typeof btoa !== "undefined") return btoa(parts.join(""));
|
|
93
|
+
return Buffer.from(parts.join(""), "binary").toString("base64");
|
|
94
|
+
}
|
|
77
95
|
var MnemeError = class extends Error {
|
|
78
96
|
constructor(status, message) {
|
|
79
97
|
super(message);
|
|
@@ -122,6 +140,69 @@ var Mneme = class {
|
|
|
122
140
|
from(table) {
|
|
123
141
|
return new Collection(this, table);
|
|
124
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Run raw SQL against your project's Postgres schema.
|
|
145
|
+
*
|
|
146
|
+
* Safety guards (server-side):
|
|
147
|
+
* - 5-second statement timeout
|
|
148
|
+
* - search_path pinned to your schema (unqualified table refs resolve to YOUR schema)
|
|
149
|
+
* - cross-tenant schema references are blocked
|
|
150
|
+
* - 1000-row result cap
|
|
151
|
+
* - single statement only
|
|
152
|
+
*
|
|
153
|
+
* Returns rows, column metadata, and elapsed time.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* await m.sql("SELECT count(*) FROM memories");
|
|
157
|
+
* await m.sql("CREATE INDEX ON books (rating)");
|
|
158
|
+
*/
|
|
159
|
+
sql(query) {
|
|
160
|
+
return this.request("POST", "/v1/sql", { query });
|
|
161
|
+
}
|
|
162
|
+
// ─── Storage (Cloudflare R2 backend, $MNEME burn quota) ──────────────────
|
|
163
|
+
storage = {
|
|
164
|
+
upload: async (args) => {
|
|
165
|
+
const bytes = await toUint8Array(args.file);
|
|
166
|
+
const content_base64 = uint8ToBase64(bytes);
|
|
167
|
+
return this.request("POST", "/v1/storage/upload", {
|
|
168
|
+
key: args.key,
|
|
169
|
+
visibility: args.visibility ?? "private",
|
|
170
|
+
content_type: args.contentType ?? "application/octet-stream",
|
|
171
|
+
content_base64
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
list: (args) => {
|
|
175
|
+
const q = new URLSearchParams();
|
|
176
|
+
if (args?.visibility) q.set("visibility", args.visibility);
|
|
177
|
+
if (args?.prefix) q.set("prefix", args.prefix);
|
|
178
|
+
return this.request("GET", `/v1/storage/list${q.toString() ? `?${q}` : ""}`);
|
|
179
|
+
},
|
|
180
|
+
delete: (args) => {
|
|
181
|
+
const q = new URLSearchParams({ key: args.key });
|
|
182
|
+
if (args.visibility) q.set("visibility", args.visibility);
|
|
183
|
+
return this.request(
|
|
184
|
+
"DELETE",
|
|
185
|
+
`/v1/storage/object?${q}`
|
|
186
|
+
);
|
|
187
|
+
},
|
|
188
|
+
url: (args) => {
|
|
189
|
+
const q = new URLSearchParams({ key: args.key });
|
|
190
|
+
if (args.visibility) q.set("visibility", args.visibility);
|
|
191
|
+
if (args.expiresIn != null) q.set("expires_in", String(args.expiresIn));
|
|
192
|
+
return this.request(
|
|
193
|
+
"GET",
|
|
194
|
+
`/v1/storage/url?${q}`
|
|
195
|
+
);
|
|
196
|
+
},
|
|
197
|
+
quota: () => this.request("GET", "/v1/storage/quota"),
|
|
198
|
+
/**
|
|
199
|
+
* Credit a $MNEME burn tx → extend storage capacity.
|
|
200
|
+
* 100 $MNEME → 1 GB / 30d
|
|
201
|
+
* 1000 $MNEME → 10 GB / 30d
|
|
202
|
+
* 10000 $MNEME → 100 GB / 30d
|
|
203
|
+
*/
|
|
204
|
+
burn: (args) => this.request("POST", "/v1/storage/burn", { tx_hash: args.tx_hash })
|
|
205
|
+
};
|
|
125
206
|
async request(method, path, body) {
|
|
126
207
|
const bodyText = body === void 0 ? "" : JSON.stringify(body);
|
|
127
208
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -158,6 +239,7 @@ var Collection = class {
|
|
|
158
239
|
}
|
|
159
240
|
mneme;
|
|
160
241
|
table;
|
|
242
|
+
/** Insert one row or an array of rows. Returns the inserted rows. */
|
|
161
243
|
insert(rows) {
|
|
162
244
|
return this.mneme.request(
|
|
163
245
|
"POST",
|
|
@@ -165,14 +247,54 @@ var Collection = class {
|
|
|
165
247
|
rows
|
|
166
248
|
);
|
|
167
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* List rows. Supports filtering with `where` (PostgREST-style col.op.value),
|
|
252
|
+
* ordering with `order` (e.g. "created_at.desc"), and pagination.
|
|
253
|
+
*
|
|
254
|
+
* Ops: eq, neq, gt, gte, lt, lte, like, ilike, in, is.
|
|
255
|
+
* Example: `m.from("todos").list({ where: ["done.eq.false"], order: "created_at.desc" })`
|
|
256
|
+
*/
|
|
168
257
|
list(opts) {
|
|
169
258
|
const q = new URLSearchParams();
|
|
170
259
|
if (opts?.limit != null) q.set("limit", String(opts.limit));
|
|
171
260
|
if (opts?.offset != null) q.set("offset", String(opts.offset));
|
|
172
261
|
if (opts?.order) q.set("order", opts.order);
|
|
262
|
+
if (opts?.where) {
|
|
263
|
+
const ws = Array.isArray(opts.where) ? opts.where : [opts.where];
|
|
264
|
+
for (const w of ws) q.append("where", w);
|
|
265
|
+
}
|
|
173
266
|
const qs = q.toString() ? `?${q}` : "";
|
|
174
267
|
return this.mneme.request("GET", `/v1/rows/${this.table}${qs}`);
|
|
175
268
|
}
|
|
269
|
+
/** Update one row by id. Returns the updated row. */
|
|
270
|
+
update(id, updates) {
|
|
271
|
+
return this.mneme.request(
|
|
272
|
+
"PATCH",
|
|
273
|
+
`/v1/rows/${this.table}/${id}`,
|
|
274
|
+
updates
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
/** Delete one row by id. */
|
|
278
|
+
delete(id) {
|
|
279
|
+
return this.mneme.request(
|
|
280
|
+
"DELETE",
|
|
281
|
+
`/v1/rows/${this.table}/${id}`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Bulk delete by filter. Refuses to delete the whole table — at least one
|
|
286
|
+
* where clause is required. Returns the count of deleted rows.
|
|
287
|
+
*/
|
|
288
|
+
deleteWhere(where) {
|
|
289
|
+
const ws = Array.isArray(where) ? where : [where];
|
|
290
|
+
if (ws.length === 0) throw new Error("deleteWhere requires at least one filter");
|
|
291
|
+
const q = new URLSearchParams();
|
|
292
|
+
for (const w of ws) q.append("where", w);
|
|
293
|
+
return this.mneme.request(
|
|
294
|
+
"DELETE",
|
|
295
|
+
`/v1/rows/${this.table}?${q}`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
176
298
|
};
|
|
177
299
|
|
|
178
300
|
// src/types.ts
|
package/dist/index.d.cts
CHANGED
|
@@ -112,22 +112,143 @@ declare class Mneme {
|
|
|
112
112
|
}>;
|
|
113
113
|
stats(): Promise<StatsResponse>;
|
|
114
114
|
from<T = unknown>(table: string): Collection<T>;
|
|
115
|
+
/**
|
|
116
|
+
* Run raw SQL against your project's Postgres schema.
|
|
117
|
+
*
|
|
118
|
+
* Safety guards (server-side):
|
|
119
|
+
* - 5-second statement timeout
|
|
120
|
+
* - search_path pinned to your schema (unqualified table refs resolve to YOUR schema)
|
|
121
|
+
* - cross-tenant schema references are blocked
|
|
122
|
+
* - 1000-row result cap
|
|
123
|
+
* - single statement only
|
|
124
|
+
*
|
|
125
|
+
* Returns rows, column metadata, and elapsed time.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* await m.sql("SELECT count(*) FROM memories");
|
|
129
|
+
* await m.sql("CREATE INDEX ON books (rating)");
|
|
130
|
+
*/
|
|
131
|
+
sql<T = Record<string, unknown>>(query: string): Promise<{
|
|
132
|
+
rows: T[];
|
|
133
|
+
row_count: number;
|
|
134
|
+
columns: string[];
|
|
135
|
+
truncated: boolean;
|
|
136
|
+
elapsed_ms: number;
|
|
137
|
+
}>;
|
|
138
|
+
readonly storage: {
|
|
139
|
+
upload: (args: {
|
|
140
|
+
key: string;
|
|
141
|
+
file: Blob | Uint8Array | ArrayBuffer | string;
|
|
142
|
+
visibility?: "public" | "private";
|
|
143
|
+
contentType?: string;
|
|
144
|
+
}) => Promise<{
|
|
145
|
+
ok: true;
|
|
146
|
+
key: string;
|
|
147
|
+
visibility: "public" | "private";
|
|
148
|
+
size: number;
|
|
149
|
+
content_type: string;
|
|
150
|
+
public_url?: string;
|
|
151
|
+
}>;
|
|
152
|
+
list: (args?: {
|
|
153
|
+
visibility?: "public" | "private";
|
|
154
|
+
prefix?: string;
|
|
155
|
+
}) => Promise<{
|
|
156
|
+
visibility: "public" | "private";
|
|
157
|
+
count: number;
|
|
158
|
+
objects: Array<{
|
|
159
|
+
key: string;
|
|
160
|
+
size: number;
|
|
161
|
+
last_modified: string;
|
|
162
|
+
public_url?: string;
|
|
163
|
+
}>;
|
|
164
|
+
}>;
|
|
165
|
+
delete: (args: {
|
|
166
|
+
key: string;
|
|
167
|
+
visibility?: "public" | "private";
|
|
168
|
+
}) => Promise<{
|
|
169
|
+
ok: true;
|
|
170
|
+
key: string;
|
|
171
|
+
freed_bytes: number;
|
|
172
|
+
}>;
|
|
173
|
+
url: (args: {
|
|
174
|
+
key: string;
|
|
175
|
+
visibility?: "public" | "private";
|
|
176
|
+
expiresIn?: number;
|
|
177
|
+
}) => Promise<{
|
|
178
|
+
url: string;
|
|
179
|
+
expires_in: number;
|
|
180
|
+
}>;
|
|
181
|
+
quota: () => Promise<{
|
|
182
|
+
wallet: string;
|
|
183
|
+
bytes_used: number;
|
|
184
|
+
bytes_limit: number;
|
|
185
|
+
bytes_available: number;
|
|
186
|
+
free_tier_bytes: number;
|
|
187
|
+
bonus_expires_at: string | null;
|
|
188
|
+
}>;
|
|
189
|
+
/**
|
|
190
|
+
* Credit a $MNEME burn tx → extend storage capacity.
|
|
191
|
+
* 100 $MNEME → 1 GB / 30d
|
|
192
|
+
* 1000 $MNEME → 10 GB / 30d
|
|
193
|
+
* 10000 $MNEME → 100 GB / 30d
|
|
194
|
+
*/
|
|
195
|
+
burn: (args: {
|
|
196
|
+
tx_hash: string;
|
|
197
|
+
}) => Promise<{
|
|
198
|
+
ok: true;
|
|
199
|
+
tx_hash: string;
|
|
200
|
+
burned_tokens: number;
|
|
201
|
+
tier: string;
|
|
202
|
+
bytes_added: number;
|
|
203
|
+
days_added: number;
|
|
204
|
+
new_expires_at: string;
|
|
205
|
+
}>;
|
|
206
|
+
};
|
|
115
207
|
request<T = unknown>(method: string, path: string, body?: unknown): Promise<T>;
|
|
116
208
|
}
|
|
209
|
+
/** PostgREST-ish where clause. e.g. `["status.eq.done", "score.gt.10"]`. */
|
|
210
|
+
type WhereClause = string;
|
|
117
211
|
declare class Collection<T = unknown> {
|
|
118
212
|
private mneme;
|
|
119
213
|
readonly table: string;
|
|
120
214
|
constructor(mneme: Mneme, table: string);
|
|
215
|
+
/** Insert one row or an array of rows. Returns the inserted rows. */
|
|
121
216
|
insert(rows: T | T[]): Promise<{
|
|
122
217
|
inserted: number;
|
|
123
218
|
rows: T[];
|
|
124
219
|
}>;
|
|
220
|
+
/**
|
|
221
|
+
* List rows. Supports filtering with `where` (PostgREST-style col.op.value),
|
|
222
|
+
* ordering with `order` (e.g. "created_at.desc"), and pagination.
|
|
223
|
+
*
|
|
224
|
+
* Ops: eq, neq, gt, gte, lt, lte, like, ilike, in, is.
|
|
225
|
+
* Example: `m.from("todos").list({ where: ["done.eq.false"], order: "created_at.desc" })`
|
|
226
|
+
*/
|
|
125
227
|
list(opts?: {
|
|
126
228
|
limit?: number;
|
|
127
229
|
offset?: number;
|
|
128
230
|
order?: string;
|
|
231
|
+
where?: WhereClause | WhereClause[];
|
|
129
232
|
}): Promise<{
|
|
130
233
|
rows: T[];
|
|
234
|
+
count: number;
|
|
235
|
+
}>;
|
|
236
|
+
/** Update one row by id. Returns the updated row. */
|
|
237
|
+
update(id: string | number, updates: Partial<T>): Promise<{
|
|
238
|
+
updated: 1;
|
|
239
|
+
row: T;
|
|
240
|
+
}>;
|
|
241
|
+
/** Delete one row by id. */
|
|
242
|
+
delete(id: string | number): Promise<{
|
|
243
|
+
deleted: 1;
|
|
244
|
+
id: number | string;
|
|
245
|
+
}>;
|
|
246
|
+
/**
|
|
247
|
+
* Bulk delete by filter. Refuses to delete the whole table — at least one
|
|
248
|
+
* where clause is required. Returns the count of deleted rows.
|
|
249
|
+
*/
|
|
250
|
+
deleteWhere(where: WhereClause | WhereClause[]): Promise<{
|
|
251
|
+
deleted: number;
|
|
131
252
|
}>;
|
|
132
253
|
}
|
|
133
254
|
|
package/dist/index.d.ts
CHANGED
|
@@ -112,22 +112,143 @@ declare class Mneme {
|
|
|
112
112
|
}>;
|
|
113
113
|
stats(): Promise<StatsResponse>;
|
|
114
114
|
from<T = unknown>(table: string): Collection<T>;
|
|
115
|
+
/**
|
|
116
|
+
* Run raw SQL against your project's Postgres schema.
|
|
117
|
+
*
|
|
118
|
+
* Safety guards (server-side):
|
|
119
|
+
* - 5-second statement timeout
|
|
120
|
+
* - search_path pinned to your schema (unqualified table refs resolve to YOUR schema)
|
|
121
|
+
* - cross-tenant schema references are blocked
|
|
122
|
+
* - 1000-row result cap
|
|
123
|
+
* - single statement only
|
|
124
|
+
*
|
|
125
|
+
* Returns rows, column metadata, and elapsed time.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* await m.sql("SELECT count(*) FROM memories");
|
|
129
|
+
* await m.sql("CREATE INDEX ON books (rating)");
|
|
130
|
+
*/
|
|
131
|
+
sql<T = Record<string, unknown>>(query: string): Promise<{
|
|
132
|
+
rows: T[];
|
|
133
|
+
row_count: number;
|
|
134
|
+
columns: string[];
|
|
135
|
+
truncated: boolean;
|
|
136
|
+
elapsed_ms: number;
|
|
137
|
+
}>;
|
|
138
|
+
readonly storage: {
|
|
139
|
+
upload: (args: {
|
|
140
|
+
key: string;
|
|
141
|
+
file: Blob | Uint8Array | ArrayBuffer | string;
|
|
142
|
+
visibility?: "public" | "private";
|
|
143
|
+
contentType?: string;
|
|
144
|
+
}) => Promise<{
|
|
145
|
+
ok: true;
|
|
146
|
+
key: string;
|
|
147
|
+
visibility: "public" | "private";
|
|
148
|
+
size: number;
|
|
149
|
+
content_type: string;
|
|
150
|
+
public_url?: string;
|
|
151
|
+
}>;
|
|
152
|
+
list: (args?: {
|
|
153
|
+
visibility?: "public" | "private";
|
|
154
|
+
prefix?: string;
|
|
155
|
+
}) => Promise<{
|
|
156
|
+
visibility: "public" | "private";
|
|
157
|
+
count: number;
|
|
158
|
+
objects: Array<{
|
|
159
|
+
key: string;
|
|
160
|
+
size: number;
|
|
161
|
+
last_modified: string;
|
|
162
|
+
public_url?: string;
|
|
163
|
+
}>;
|
|
164
|
+
}>;
|
|
165
|
+
delete: (args: {
|
|
166
|
+
key: string;
|
|
167
|
+
visibility?: "public" | "private";
|
|
168
|
+
}) => Promise<{
|
|
169
|
+
ok: true;
|
|
170
|
+
key: string;
|
|
171
|
+
freed_bytes: number;
|
|
172
|
+
}>;
|
|
173
|
+
url: (args: {
|
|
174
|
+
key: string;
|
|
175
|
+
visibility?: "public" | "private";
|
|
176
|
+
expiresIn?: number;
|
|
177
|
+
}) => Promise<{
|
|
178
|
+
url: string;
|
|
179
|
+
expires_in: number;
|
|
180
|
+
}>;
|
|
181
|
+
quota: () => Promise<{
|
|
182
|
+
wallet: string;
|
|
183
|
+
bytes_used: number;
|
|
184
|
+
bytes_limit: number;
|
|
185
|
+
bytes_available: number;
|
|
186
|
+
free_tier_bytes: number;
|
|
187
|
+
bonus_expires_at: string | null;
|
|
188
|
+
}>;
|
|
189
|
+
/**
|
|
190
|
+
* Credit a $MNEME burn tx → extend storage capacity.
|
|
191
|
+
* 100 $MNEME → 1 GB / 30d
|
|
192
|
+
* 1000 $MNEME → 10 GB / 30d
|
|
193
|
+
* 10000 $MNEME → 100 GB / 30d
|
|
194
|
+
*/
|
|
195
|
+
burn: (args: {
|
|
196
|
+
tx_hash: string;
|
|
197
|
+
}) => Promise<{
|
|
198
|
+
ok: true;
|
|
199
|
+
tx_hash: string;
|
|
200
|
+
burned_tokens: number;
|
|
201
|
+
tier: string;
|
|
202
|
+
bytes_added: number;
|
|
203
|
+
days_added: number;
|
|
204
|
+
new_expires_at: string;
|
|
205
|
+
}>;
|
|
206
|
+
};
|
|
115
207
|
request<T = unknown>(method: string, path: string, body?: unknown): Promise<T>;
|
|
116
208
|
}
|
|
209
|
+
/** PostgREST-ish where clause. e.g. `["status.eq.done", "score.gt.10"]`. */
|
|
210
|
+
type WhereClause = string;
|
|
117
211
|
declare class Collection<T = unknown> {
|
|
118
212
|
private mneme;
|
|
119
213
|
readonly table: string;
|
|
120
214
|
constructor(mneme: Mneme, table: string);
|
|
215
|
+
/** Insert one row or an array of rows. Returns the inserted rows. */
|
|
121
216
|
insert(rows: T | T[]): Promise<{
|
|
122
217
|
inserted: number;
|
|
123
218
|
rows: T[];
|
|
124
219
|
}>;
|
|
220
|
+
/**
|
|
221
|
+
* List rows. Supports filtering with `where` (PostgREST-style col.op.value),
|
|
222
|
+
* ordering with `order` (e.g. "created_at.desc"), and pagination.
|
|
223
|
+
*
|
|
224
|
+
* Ops: eq, neq, gt, gte, lt, lte, like, ilike, in, is.
|
|
225
|
+
* Example: `m.from("todos").list({ where: ["done.eq.false"], order: "created_at.desc" })`
|
|
226
|
+
*/
|
|
125
227
|
list(opts?: {
|
|
126
228
|
limit?: number;
|
|
127
229
|
offset?: number;
|
|
128
230
|
order?: string;
|
|
231
|
+
where?: WhereClause | WhereClause[];
|
|
129
232
|
}): Promise<{
|
|
130
233
|
rows: T[];
|
|
234
|
+
count: number;
|
|
235
|
+
}>;
|
|
236
|
+
/** Update one row by id. Returns the updated row. */
|
|
237
|
+
update(id: string | number, updates: Partial<T>): Promise<{
|
|
238
|
+
updated: 1;
|
|
239
|
+
row: T;
|
|
240
|
+
}>;
|
|
241
|
+
/** Delete one row by id. */
|
|
242
|
+
delete(id: string | number): Promise<{
|
|
243
|
+
deleted: 1;
|
|
244
|
+
id: number | string;
|
|
245
|
+
}>;
|
|
246
|
+
/**
|
|
247
|
+
* Bulk delete by filter. Refuses to delete the whole table — at least one
|
|
248
|
+
* where clause is required. Returns the count of deleted rows.
|
|
249
|
+
*/
|
|
250
|
+
deleteWhere(where: WhereClause | WhereClause[]): Promise<{
|
|
251
|
+
deleted: number;
|
|
131
252
|
}>;
|
|
132
253
|
}
|
|
133
254
|
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,24 @@ function randomNonce() {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// src/client.ts
|
|
45
|
+
async function toUint8Array(input) {
|
|
46
|
+
if (input instanceof Uint8Array) return input;
|
|
47
|
+
if (input instanceof ArrayBuffer) return new Uint8Array(input);
|
|
48
|
+
if (typeof Blob !== "undefined" && input instanceof Blob) {
|
|
49
|
+
return new Uint8Array(await input.arrayBuffer());
|
|
50
|
+
}
|
|
51
|
+
if (typeof input === "string") return new TextEncoder().encode(input);
|
|
52
|
+
throw new Error("unsupported file type for storage.upload");
|
|
53
|
+
}
|
|
54
|
+
function uint8ToBase64(bytes) {
|
|
55
|
+
const CHUNK = 32768;
|
|
56
|
+
const parts = [];
|
|
57
|
+
for (let i = 0; i < bytes.byteLength; i += CHUNK) {
|
|
58
|
+
parts.push(String.fromCharCode(...bytes.subarray(i, i + CHUNK)));
|
|
59
|
+
}
|
|
60
|
+
if (typeof btoa !== "undefined") return btoa(parts.join(""));
|
|
61
|
+
return Buffer.from(parts.join(""), "binary").toString("base64");
|
|
62
|
+
}
|
|
45
63
|
var MnemeError = class extends Error {
|
|
46
64
|
constructor(status, message) {
|
|
47
65
|
super(message);
|
|
@@ -90,6 +108,69 @@ var Mneme = class {
|
|
|
90
108
|
from(table) {
|
|
91
109
|
return new Collection(this, table);
|
|
92
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Run raw SQL against your project's Postgres schema.
|
|
113
|
+
*
|
|
114
|
+
* Safety guards (server-side):
|
|
115
|
+
* - 5-second statement timeout
|
|
116
|
+
* - search_path pinned to your schema (unqualified table refs resolve to YOUR schema)
|
|
117
|
+
* - cross-tenant schema references are blocked
|
|
118
|
+
* - 1000-row result cap
|
|
119
|
+
* - single statement only
|
|
120
|
+
*
|
|
121
|
+
* Returns rows, column metadata, and elapsed time.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* await m.sql("SELECT count(*) FROM memories");
|
|
125
|
+
* await m.sql("CREATE INDEX ON books (rating)");
|
|
126
|
+
*/
|
|
127
|
+
sql(query) {
|
|
128
|
+
return this.request("POST", "/v1/sql", { query });
|
|
129
|
+
}
|
|
130
|
+
// ─── Storage (Cloudflare R2 backend, $MNEME burn quota) ──────────────────
|
|
131
|
+
storage = {
|
|
132
|
+
upload: async (args) => {
|
|
133
|
+
const bytes = await toUint8Array(args.file);
|
|
134
|
+
const content_base64 = uint8ToBase64(bytes);
|
|
135
|
+
return this.request("POST", "/v1/storage/upload", {
|
|
136
|
+
key: args.key,
|
|
137
|
+
visibility: args.visibility ?? "private",
|
|
138
|
+
content_type: args.contentType ?? "application/octet-stream",
|
|
139
|
+
content_base64
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
list: (args) => {
|
|
143
|
+
const q = new URLSearchParams();
|
|
144
|
+
if (args?.visibility) q.set("visibility", args.visibility);
|
|
145
|
+
if (args?.prefix) q.set("prefix", args.prefix);
|
|
146
|
+
return this.request("GET", `/v1/storage/list${q.toString() ? `?${q}` : ""}`);
|
|
147
|
+
},
|
|
148
|
+
delete: (args) => {
|
|
149
|
+
const q = new URLSearchParams({ key: args.key });
|
|
150
|
+
if (args.visibility) q.set("visibility", args.visibility);
|
|
151
|
+
return this.request(
|
|
152
|
+
"DELETE",
|
|
153
|
+
`/v1/storage/object?${q}`
|
|
154
|
+
);
|
|
155
|
+
},
|
|
156
|
+
url: (args) => {
|
|
157
|
+
const q = new URLSearchParams({ key: args.key });
|
|
158
|
+
if (args.visibility) q.set("visibility", args.visibility);
|
|
159
|
+
if (args.expiresIn != null) q.set("expires_in", String(args.expiresIn));
|
|
160
|
+
return this.request(
|
|
161
|
+
"GET",
|
|
162
|
+
`/v1/storage/url?${q}`
|
|
163
|
+
);
|
|
164
|
+
},
|
|
165
|
+
quota: () => this.request("GET", "/v1/storage/quota"),
|
|
166
|
+
/**
|
|
167
|
+
* Credit a $MNEME burn tx → extend storage capacity.
|
|
168
|
+
* 100 $MNEME → 1 GB / 30d
|
|
169
|
+
* 1000 $MNEME → 10 GB / 30d
|
|
170
|
+
* 10000 $MNEME → 100 GB / 30d
|
|
171
|
+
*/
|
|
172
|
+
burn: (args) => this.request("POST", "/v1/storage/burn", { tx_hash: args.tx_hash })
|
|
173
|
+
};
|
|
93
174
|
async request(method, path, body) {
|
|
94
175
|
const bodyText = body === void 0 ? "" : JSON.stringify(body);
|
|
95
176
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -126,6 +207,7 @@ var Collection = class {
|
|
|
126
207
|
}
|
|
127
208
|
mneme;
|
|
128
209
|
table;
|
|
210
|
+
/** Insert one row or an array of rows. Returns the inserted rows. */
|
|
129
211
|
insert(rows) {
|
|
130
212
|
return this.mneme.request(
|
|
131
213
|
"POST",
|
|
@@ -133,14 +215,54 @@ var Collection = class {
|
|
|
133
215
|
rows
|
|
134
216
|
);
|
|
135
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* List rows. Supports filtering with `where` (PostgREST-style col.op.value),
|
|
220
|
+
* ordering with `order` (e.g. "created_at.desc"), and pagination.
|
|
221
|
+
*
|
|
222
|
+
* Ops: eq, neq, gt, gte, lt, lte, like, ilike, in, is.
|
|
223
|
+
* Example: `m.from("todos").list({ where: ["done.eq.false"], order: "created_at.desc" })`
|
|
224
|
+
*/
|
|
136
225
|
list(opts) {
|
|
137
226
|
const q = new URLSearchParams();
|
|
138
227
|
if (opts?.limit != null) q.set("limit", String(opts.limit));
|
|
139
228
|
if (opts?.offset != null) q.set("offset", String(opts.offset));
|
|
140
229
|
if (opts?.order) q.set("order", opts.order);
|
|
230
|
+
if (opts?.where) {
|
|
231
|
+
const ws = Array.isArray(opts.where) ? opts.where : [opts.where];
|
|
232
|
+
for (const w of ws) q.append("where", w);
|
|
233
|
+
}
|
|
141
234
|
const qs = q.toString() ? `?${q}` : "";
|
|
142
235
|
return this.mneme.request("GET", `/v1/rows/${this.table}${qs}`);
|
|
143
236
|
}
|
|
237
|
+
/** Update one row by id. Returns the updated row. */
|
|
238
|
+
update(id, updates) {
|
|
239
|
+
return this.mneme.request(
|
|
240
|
+
"PATCH",
|
|
241
|
+
`/v1/rows/${this.table}/${id}`,
|
|
242
|
+
updates
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
/** Delete one row by id. */
|
|
246
|
+
delete(id) {
|
|
247
|
+
return this.mneme.request(
|
|
248
|
+
"DELETE",
|
|
249
|
+
`/v1/rows/${this.table}/${id}`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Bulk delete by filter. Refuses to delete the whole table — at least one
|
|
254
|
+
* where clause is required. Returns the count of deleted rows.
|
|
255
|
+
*/
|
|
256
|
+
deleteWhere(where) {
|
|
257
|
+
const ws = Array.isArray(where) ? where : [where];
|
|
258
|
+
if (ws.length === 0) throw new Error("deleteWhere requires at least one filter");
|
|
259
|
+
const q = new URLSearchParams();
|
|
260
|
+
for (const w of ws) q.append("where", w);
|
|
261
|
+
return this.mneme.request(
|
|
262
|
+
"DELETE",
|
|
263
|
+
`/v1/rows/${this.table}?${q}`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
144
266
|
};
|
|
145
267
|
|
|
146
268
|
// src/types.ts
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mneme-sdk",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "TypeScript SDK for Mneme — agent-native Postgres
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "TypeScript SDK for Mneme — agent-native Postgres + R2 storage on Base. Wallet-auth, runtime DDL, full CRUD with WHERE filters, raw SQL, pgvector, wallet-bound files with $MNEME burn quota.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|