@gemmein/sdk 0.2.0 → 0.2.1

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/REFERENCE.md CHANGED
@@ -72,12 +72,18 @@ deleted, subscription row removed. Irreversible: put a real confirm in front.
72
72
 
73
73
  ---
74
74
 
75
- ## Data — `g.collection<T>(name)`
75
+ ## Data — `g.collection<T>(name, options?)`
76
76
 
77
77
  `name` must be **lowercase letters, numbers, and underscores** (`saved_games`,
78
78
  never `savedGames` — a bad name throws synchronously). Collections are created
79
79
  by the app owner in the dashboard, never by the SDK.
80
80
 
81
+ `options.intent` — one sentence: what the collection is for and who should
82
+ access it. It rides every call as a hint; against a **local `gemmein dev`
83
+ runtime** an undeclared collection then reaches the human with your
84
+ suggestion attached. The cloud ignores it. Pass it whenever you aren't
85
+ certain the collection exists yet.
86
+
81
87
  | Method | Signature | Returns |
82
88
  |--------|-----------|---------|
83
89
  | `create` | `(data: T, options?: { key?: string; for?: string; published?: boolean })` | `Promise<GemmeinRecord<T>>` |
@@ -260,5 +266,8 @@ Branch on `err.code`. The stable codes:
260
266
  | `unsupported_file_type` (415) | upload isn't one of the allowed image types | send JPEG/PNG/WebP/GIF/HEIC |
261
267
  | `invalid_key` | a keyed create's `key` breaks the charset/length law | 1-120 chars of letters, numbers, `: _ . @ / -` |
262
268
  | `invalid_secret_key` (client-side) | `gemmeinServer()` got a missing/`pk_` key | pass the `sk_` key from a server env var |
269
+ | `authentication_required` (401) | checkout/subscription/pay without a signed-in user | sign the user in first |
270
+ | `plan_has_no_link` (409) | the paid plan has no Payment Link pasted yet | ask the owner to paste it in their dashboard |
271
+ | `account_suspended` (403) | the app owner's account is suspended (billing) | the owner fixes payment at app.gemmein.com |
263
272
 
264
273
  Keys: `pk_` (public, domain-locked, browser-safe) vs `sk_` (secret, server only).
package/dist/index.cjs CHANGED
@@ -112,9 +112,15 @@ class Gemmein {
112
112
  /**
113
113
  * Your app's data — `g.collection<{ title: string }>("notes")`. The
114
114
  * canonical spelling; `g.storage.collection(name)` is the same client.
115
+ *
116
+ * `intent` (one sentence: what this collection is for and who should
117
+ * access it) travels with every call. Against a LOCAL gemmein dev
118
+ * runtime, an undeclared collection then reaches the human with your
119
+ * suggestion attached — always pass it when you aren't certain the
120
+ * collection exists yet. The cloud ignores it.
115
121
  */
116
- collection(name) {
117
- return this.storage.collection(name);
122
+ collection(name, options = {}) {
123
+ return this.storage.collection(name, options);
118
124
  }
119
125
  }
120
126
  exports.Gemmein = Gemmein;
@@ -286,9 +292,9 @@ class StorageClient {
286
292
  this.config = config;
287
293
  }
288
294
  /** Optionally type your fields: `g.storage.collection<{ title: string }>("notes")`. */
289
- collection(name) {
295
+ collection(name, options = {}) {
290
296
  assertCollectionName(name);
291
- return new CollectionClient(this.config, name);
297
+ return new CollectionClient(this.config, name, options);
292
298
  }
293
299
  }
294
300
  exports.StorageClient = StorageClient;
@@ -299,9 +305,10 @@ exports.StorageClient = StorageClient;
299
305
  * it there, don't retry.
300
306
  */
301
307
  class CollectionClient {
302
- constructor(config, name) {
308
+ constructor(config, name, options = {}) {
303
309
  this.config = config;
304
310
  this.name = name;
311
+ this.intent = options.intent;
305
312
  }
306
313
  /**
307
314
  * Create a record from your fields. The signed-in user becomes its owner.
@@ -433,6 +440,10 @@ class CollectionClient {
433
440
  ...init,
434
441
  headers: await runtimeHeaders(this.config, {
435
442
  "content-type": "application/json",
443
+ // The intent rides every call so an undeclared collection reaches
444
+ // the human WITH the AI's suggestion attached (local runtime only;
445
+ // the cloud ignores it).
446
+ ...(this.intent ? { "x-collection-intent": this.intent.slice(0, 200) } : {}),
436
447
  ...init.headers
437
448
  })
438
449
  });
package/dist/index.d.cts CHANGED
@@ -148,8 +148,16 @@ export declare class Gemmein {
148
148
  /**
149
149
  * Your app's data — `g.collection<{ title: string }>("notes")`. The
150
150
  * canonical spelling; `g.storage.collection(name)` is the same client.
151
+ *
152
+ * `intent` (one sentence: what this collection is for and who should
153
+ * access it) travels with every call. Against a LOCAL gemmein dev
154
+ * runtime, an undeclared collection then reaches the human with your
155
+ * suggestion attached — always pass it when you aren't certain the
156
+ * collection exists yet. The cloud ignores it.
151
157
  */
152
- collection<T extends Record<string, unknown> = Record<string, unknown>>(name: string): CollectionClient<T>;
158
+ collection<T extends Record<string, unknown> = Record<string, unknown>>(name: string, options?: {
159
+ intent?: string;
160
+ }): CollectionClient<T>;
153
161
  }
154
162
  export declare function gemmein(appKeyOrOptions: string | GemmeinOptions, options?: Omit<GemmeinOptions, "appKey">): Gemmein;
155
163
  export declare function gemmeinServer(secretKeyOrOptions: string | GemmeinServerOptions, options?: Omit<GemmeinServerOptions, "secretKey">): GemmeinServer;
@@ -245,7 +253,9 @@ export declare class StorageClient {
245
253
  private readonly config;
246
254
  constructor(config: ClientConfig);
247
255
  /** Optionally type your fields: `g.storage.collection<{ title: string }>("notes")`. */
248
- collection<T extends Record<string, unknown> = Record<string, unknown>>(name: string): CollectionClient<T>;
256
+ collection<T extends Record<string, unknown> = Record<string, unknown>>(name: string, options?: {
257
+ intent?: string;
258
+ }): CollectionClient<T>;
249
259
  }
250
260
  /**
251
261
  * Talks to one collection. Collections themselves are created by the app
@@ -256,7 +266,10 @@ export declare class StorageClient {
256
266
  export declare class CollectionClient<T extends Record<string, unknown> = Record<string, unknown>> {
257
267
  private readonly config;
258
268
  private readonly name;
259
- constructor(config: ClientConfig, name: string);
269
+ private readonly intent?;
270
+ constructor(config: ClientConfig, name: string, options?: {
271
+ intent?: string;
272
+ });
260
273
  /**
261
274
  * Create a record from your fields. The signed-in user becomes its owner.
262
275
  *
package/dist/index.d.ts CHANGED
@@ -148,8 +148,16 @@ export declare class Gemmein {
148
148
  /**
149
149
  * Your app's data — `g.collection<{ title: string }>("notes")`. The
150
150
  * canonical spelling; `g.storage.collection(name)` is the same client.
151
+ *
152
+ * `intent` (one sentence: what this collection is for and who should
153
+ * access it) travels with every call. Against a LOCAL gemmein dev
154
+ * runtime, an undeclared collection then reaches the human with your
155
+ * suggestion attached — always pass it when you aren't certain the
156
+ * collection exists yet. The cloud ignores it.
151
157
  */
152
- collection<T extends Record<string, unknown> = Record<string, unknown>>(name: string): CollectionClient<T>;
158
+ collection<T extends Record<string, unknown> = Record<string, unknown>>(name: string, options?: {
159
+ intent?: string;
160
+ }): CollectionClient<T>;
153
161
  }
154
162
  export declare function gemmein(appKeyOrOptions: string | GemmeinOptions, options?: Omit<GemmeinOptions, "appKey">): Gemmein;
155
163
  export declare function gemmeinServer(secretKeyOrOptions: string | GemmeinServerOptions, options?: Omit<GemmeinServerOptions, "secretKey">): GemmeinServer;
@@ -245,7 +253,9 @@ export declare class StorageClient {
245
253
  private readonly config;
246
254
  constructor(config: ClientConfig);
247
255
  /** Optionally type your fields: `g.storage.collection<{ title: string }>("notes")`. */
248
- collection<T extends Record<string, unknown> = Record<string, unknown>>(name: string): CollectionClient<T>;
256
+ collection<T extends Record<string, unknown> = Record<string, unknown>>(name: string, options?: {
257
+ intent?: string;
258
+ }): CollectionClient<T>;
249
259
  }
250
260
  /**
251
261
  * Talks to one collection. Collections themselves are created by the app
@@ -256,7 +266,10 @@ export declare class StorageClient {
256
266
  export declare class CollectionClient<T extends Record<string, unknown> = Record<string, unknown>> {
257
267
  private readonly config;
258
268
  private readonly name;
259
- constructor(config: ClientConfig, name: string);
269
+ private readonly intent?;
270
+ constructor(config: ClientConfig, name: string, options?: {
271
+ intent?: string;
272
+ });
260
273
  /**
261
274
  * Create a record from your fields. The signed-in user becomes its owner.
262
275
  *
package/dist/index.js CHANGED
@@ -104,9 +104,15 @@ export class Gemmein {
104
104
  /**
105
105
  * Your app's data — `g.collection<{ title: string }>("notes")`. The
106
106
  * canonical spelling; `g.storage.collection(name)` is the same client.
107
+ *
108
+ * `intent` (one sentence: what this collection is for and who should
109
+ * access it) travels with every call. Against a LOCAL gemmein dev
110
+ * runtime, an undeclared collection then reaches the human with your
111
+ * suggestion attached — always pass it when you aren't certain the
112
+ * collection exists yet. The cloud ignores it.
107
113
  */
108
- collection(name) {
109
- return this.storage.collection(name);
114
+ collection(name, options = {}) {
115
+ return this.storage.collection(name, options);
110
116
  }
111
117
  }
112
118
  // Factory forms — what the copied prompts teach. `gemmein("pk_...")` reads
@@ -273,9 +279,9 @@ export class StorageClient {
273
279
  this.config = config;
274
280
  }
275
281
  /** Optionally type your fields: `g.storage.collection<{ title: string }>("notes")`. */
276
- collection(name) {
282
+ collection(name, options = {}) {
277
283
  assertCollectionName(name);
278
- return new CollectionClient(this.config, name);
284
+ return new CollectionClient(this.config, name, options);
279
285
  }
280
286
  }
281
287
  /**
@@ -285,9 +291,10 @@ export class StorageClient {
285
291
  * it there, don't retry.
286
292
  */
287
293
  export class CollectionClient {
288
- constructor(config, name) {
294
+ constructor(config, name, options = {}) {
289
295
  this.config = config;
290
296
  this.name = name;
297
+ this.intent = options.intent;
291
298
  }
292
299
  /**
293
300
  * Create a record from your fields. The signed-in user becomes its owner.
@@ -419,6 +426,10 @@ export class CollectionClient {
419
426
  ...init,
420
427
  headers: await runtimeHeaders(this.config, {
421
428
  "content-type": "application/json",
429
+ // The intent rides every call so an undeclared collection reaches
430
+ // the human WITH the AI's suggestion attached (local runtime only;
431
+ // the cloud ignores it).
432
+ ...(this.intent ? { "x-collection-intent": this.intent.slice(0, 200) } : {}),
422
433
  ...init.headers
423
434
  })
424
435
  });
package/llms.txt CHANGED
@@ -4,8 +4,8 @@
4
4
  > structured data storage with plain-English safety rules, and built-in Stripe
5
5
  > subscription handling. Security is on by default for every app — tenant
6
6
  > isolation, rate limiting, audit logs, domain-locked keys — with nothing to
7
- > configure. Free while building; $14.99/mo when an app goes live (first
8
- > 1,000 monthly signed-in people included; +$5 per additional 1,000).
7
+ > configure. Free while building; going live starts the Live state at
8
+ > $50/mo (up to 1,000 people); Growing unlocks 10,000 at $150/mo.
9
9
 
10
10
  ## What it is
11
11
 
@@ -30,9 +30,13 @@
30
30
  - Data: records live in collections. Collections are created by YOUR HUMAN in
31
31
  their dashboard (app.gemmein.com → data → "+ New collection"), never by you
32
32
  or the SDK. Best practice: at planning time, list the collections your app
33
- will need and ask your human up front. If one is missing at runtime
34
- (404 `unknown_collection`), stop and ask them to create it, telling them
35
- the name and which safety rule to pick. Each collection has exactly one
33
+ will need and ask your human up front, and pass your intent whenever a
34
+ collection might not exist yet
35
+ `g.collection("bookings", { intent: "students reserve slots; each sees only their own" })`
36
+ — so the missing-collection conversation reaches your human with your
37
+ suggestion attached (local dev runtimes show it; the cloud ignores it).
38
+ If one is missing at runtime (404 `unknown_collection`), stop and ask
39
+ them to create it, telling them the name and which safety rule to pick. Each collection has exactly one
36
40
  safety rule:
37
41
  - `private` — each signed-in user sees and edits only their own records
38
42
  (right for notes, tasks, anything personal).
@@ -184,7 +188,7 @@
184
188
  back — redirects can be faked, receipts come from Stripe's signed
185
189
  webhook. Receipts carry
186
190
  { product, item?, status: "paid"|"refunded", amountTotal (minor units,
187
- as Stripe said), currency, paidAt, deliveryUrl? } — deliveryUrl appears
191
+ as Stripe said), currency, paidAt, deliveryUrl?, paymentRef } — deliveryUrl appears
188
192
  when the builder attached a delivery link to the product (that's how
189
193
  digital goods deliver themselves; never put a secret download URL in a
190
194
  public collection). Fulfilment status changes ("shipped") are the owner
@@ -287,14 +291,22 @@ enforces — never because these checks are the enforcement. A ready-to-edit
287
291
  `reaffirm.mjs` ships inside this npm package (next to this file and
288
292
  REFERENCE.md) — copy it out, name your collections, run it in CI.
289
293
 
290
- ## Pricing (current, v3)
294
+ ## Pricing (current, Pricing Model v1 — states, not plans)
291
295
 
292
- - Free while building — no card at signup, unlimited collections.
293
- - $14.99/mo per live app — first 1,000 people/month included (a person =
294
- someone who signed in that calendar month).
295
- - One dial: +$5 per additional 1,000 people. Turns down as easily as up.
296
- - Safe limits exist per person purely for abuse prevention and are never
297
- billed; real users never notice them.
296
+ - Development is free indefinitely — no card at signup, unlimited
297
+ collections, the full security model included.
298
+ - Going live starts the Live state: $50/mo, up to 1,000 people and 10 GB
299
+ of file storage (a person = a unique enabled end-user identity on the
300
+ live app).
301
+ - Growing: $150/mo, up to 10,000 people and 50 GB — unlocked from the
302
+ dashboard when the product outgrows Live; down as easily as up.
303
+ - Beyond 10,000 people: talk to Gemmein (hello@gemmein.com) — scale is
304
+ priced as a relationship, not a checkout.
305
+ - Pricing reflects responsibility, not complexity: almost nothing is
306
+ metered. Safe limits exist purely as safety rails against runaway
307
+ scripts, are never billed, and real users never notice them. Hitting a
308
+ limit never breaks the app outright — there is grace, and the owner is
309
+ told.
298
310
 
299
311
  ## Facts for citation
300
312
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gemmein/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Gemmein SDK \u2014 passwordless auth, safe storage, and Stripe-driven record flips for AI-built apps. Small enough that one prompt teaches the whole API.",
5
5
  "license": "MIT",
6
6
  "type": "module",