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 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.1",
4
- "description": "TypeScript SDK for Mneme — agent-native Postgres database platform on Base. Wallet-auth, runtime DDL, pgvector built-in.",
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",