@palbase/backend 22.0.1 → 23.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.
Files changed (73) hide show
  1. package/dist/bin/palbase-backend.cjs +764 -25
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +4 -4
  4. package/dist/{chunk-POYAFBLF.js → chunk-FSGSB42K.js} +13 -6
  5. package/dist/chunk-FSGSB42K.js.map +1 -0
  6. package/dist/{chunk-SSGAMC26.js → chunk-HAF67F2H.js} +114 -5
  7. package/dist/chunk-HAF67F2H.js.map +1 -0
  8. package/dist/{chunk-QMVK4X3V.js → chunk-OMRTHM4X.js} +98 -98
  9. package/dist/chunk-OMRTHM4X.js.map +1 -0
  10. package/dist/{chunk-QYOHMVUW.js → chunk-REZU6UKT.js} +755 -24
  11. package/dist/chunk-REZU6UKT.js.map +1 -0
  12. package/dist/{chunk-YL4C5NRY.js → chunk-Y5HXVUMP.js} +2 -2
  13. package/dist/{chunk-N32VDWKH.js → chunk-ZC6Q2BRD.js} +4 -59
  14. package/dist/chunk-ZC6Q2BRD.js.map +1 -0
  15. package/dist/db/env.cjs.map +1 -1
  16. package/dist/db/env.d.cts +3 -21
  17. package/dist/db/env.d.ts +3 -21
  18. package/dist/db/index.cjs +117 -6
  19. package/dist/db/index.cjs.map +1 -1
  20. package/dist/db/index.d.cts +3 -2
  21. package/dist/db/index.d.ts +3 -2
  22. package/dist/db/index.js +7 -3
  23. package/dist/{endpoint-B0LpZixz.d.cts → endpoint-BavvbW4P.d.ts} +116 -14
  24. package/dist/{endpoint-B0LpZixz.d.ts → endpoint-i8TTCohk.d.cts} +116 -14
  25. package/dist/engine/index.cjs +764 -25
  26. package/dist/engine/index.cjs.map +1 -1
  27. package/dist/engine/index.d.cts +6 -5
  28. package/dist/engine/index.d.ts +6 -5
  29. package/dist/engine/index.js +3 -3
  30. package/dist/{index-g-EzitI-.d.ts → index-B3jmmItD.d.ts} +203 -7
  31. package/dist/{index-BCNtlG1w.d.ts → index-B7YBEG5w.d.ts} +117 -93
  32. package/dist/{index-BGSCWlUa.d.cts → index-Bmvx1EvJ.d.cts} +203 -7
  33. package/dist/{index-B4W6d2VJ.d.cts → index-E7OscPJT.d.cts} +117 -93
  34. package/dist/index.cjs +509 -1085
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +203 -1133
  37. package/dist/index.d.ts +203 -1133
  38. package/dist/index.js +288 -900
  39. package/dist/index.js.map +1 -1
  40. package/dist/openapi/index.cjs +3 -57
  41. package/dist/openapi/index.cjs.map +1 -1
  42. package/dist/openapi/index.d.cts +3 -2
  43. package/dist/openapi/index.d.ts +3 -2
  44. package/dist/openapi/index.js +5 -22
  45. package/dist/openapi/index.js.map +1 -1
  46. package/dist/{registry-3BLYv4si.d.ts → registry-C3H2uPeZ.d.cts} +13 -169
  47. package/dist/{registry-Cw0YEYCg.d.cts → registry-DY3d9l1k.d.ts} +13 -169
  48. package/dist/{purchases/keys.cjs → stack.cjs} +4 -4
  49. package/dist/stack.cjs.map +1 -0
  50. package/dist/stack.d.cts +76 -0
  51. package/dist/stack.d.ts +76 -0
  52. package/dist/stack.js +1 -0
  53. package/docs/README.md +7 -6
  54. package/docs/endpoints.md +1 -1
  55. package/docs/errors.md +9 -0
  56. package/docs/llms-full.txt +17 -261
  57. package/docs/llms.txt +0 -2
  58. package/package.json +7 -7
  59. package/template/package.json +1 -1
  60. package/dist/chunk-N32VDWKH.js.map +0 -1
  61. package/dist/chunk-POYAFBLF.js.map +0 -1
  62. package/dist/chunk-QMVK4X3V.js.map +0 -1
  63. package/dist/chunk-QYOHMVUW.js.map +0 -1
  64. package/dist/chunk-SSGAMC26.js.map +0 -1
  65. package/dist/purchases/keys.cjs.map +0 -1
  66. package/dist/purchases/keys.d.cts +0 -42
  67. package/dist/purchases/keys.d.ts +0 -42
  68. package/dist/purchases/keys.js +0 -1
  69. package/docs/config.md +0 -147
  70. package/docs/resources.md +0 -97
  71. package/template/config/secrets.ts +0 -24
  72. /package/dist/{chunk-YL4C5NRY.js.map → chunk-Y5HXVUMP.js.map} +0 -0
  73. /package/dist/{purchases/keys.js.map → stack.js.map} +0 -0
package/docs/config.md DELETED
@@ -1,147 +0,0 @@
1
- # Module Config (config-as-code)
2
-
3
- Beyond `db/schema.ts`, four more module surfaces are git-authoritative: storage
4
- buckets, notification providers, feature-flag definitions, and the outbound-HTTP
5
- allowlist. You declare them in `config/*.ts` files (typed, imported from
6
- `@palbase/backend`) and on `git push` the deploy creates/updates them. Secrets
7
- (certs, keys, API tokens) NEVER go in git — they live in a reserved encrypted env
8
- namespace, uploaded by the guided CLI.
9
-
10
- You normally author these with `palbase <module> add …` (the CLI writes the
11
- config file + uploads any secret); the files below are what it generates.
12
-
13
- ## Storage buckets — `config/storage.ts`
14
-
15
- ```ts
16
- import { defineStorage, bucket } from "@palbase/backend";
17
-
18
- export default defineStorage({
19
- buckets: {
20
- avatars: bucket({
21
- public: true, // served without a signed URL
22
- fileSizeLimit: "5MB", // "5MB"/"20MB"/"1GB" or a byte number
23
- allowedMimeTypes: ["image/png", "image/jpeg", "image/webp"],
24
- }),
25
- invoices: bucket({ public: false, fileSizeLimit: "20MB", allowedMimeTypes: ["application/pdf"] }),
26
- },
27
- });
28
- ```
29
-
30
- Author it: `palbase storage buckets add avatars --public --max-size 5MB --mime image/png,image/jpeg`.
31
- On deploy, the buckets are created/updated. A bucket REMOVED from the file is
32
- **never auto-deleted** (its files would be lost) — drop it explicitly in Studio.
33
- The files inside a bucket are runtime state, not config.
34
-
35
- ## Notification providers — `config/notifications.ts`
36
-
37
- Providers carry secrets (APNs `.p8`, FCM service-account JSON, Twilio token).
38
- The config file is **structural** — it names the enabled providers + their
39
- non-secret fields; the secret is bound by convention to a reserved env key and
40
- NEVER appears in git.
41
-
42
- ```ts
43
- import { defineNotifications } from "@palbase/backend";
44
-
45
- export default defineNotifications({
46
- push: {
47
- apns: { enabled: true, teamId: "A1B2C3D4E5", keyId: "XYZ123", bundleId: "net.example.app" },
48
- // no p8 key here — it's in the reserved secret PB_NOTIFICATIONS_APNS_P8
49
- },
50
- sms: {
51
- twilio: { enabled: true, accountSid: "AC...", messagingServiceSid: "MG..." },
52
- },
53
- });
54
- ```
55
-
56
- Author it with the guided CLI — it knows each provider's fields and uploads the
57
- secret for you, so you never type a secret-name string:
58
-
59
- ```bash
60
- palbase notifications providers # list the catalog + what's configured
61
- palbase notifications add apns \
62
- --team-id A1B2C3D4E5 --key-id XYZ123 --bundle-id net.example.app \
63
- --p8-file ./AuthKey_XYZ123.p8 # → uploads PB_NOTIFICATIONS_APNS_P8 (encrypted)
64
- palbase notifications add twilio --account-sid AC... --messaging-sid MG...
65
- # prompts for the auth token (hidden)
66
- ```
67
-
68
- The reserved secret env keys (`PB_NOTIFICATIONS_*`) are managed by these
69
- commands — `palbase secret set PB_*` is refused. Your own custom env
70
- (`MY_API_KEY` etc.) is unaffected and still flows via `.env.local`. On deploy,
71
- each enabled provider's reserved secret is resolved and the provider is
72
- configured; a provider whose secret is missing is skipped (warned, not fatal).
73
-
74
- ## Feature flags — `config/flags.ts`
75
-
76
- Flag DEFINITIONS (key, type, default) are config; the value set for a specific
77
- user / an A/B assignment is runtime (set via the SDK/Studio, not git).
78
-
79
- ```ts
80
- import { defineFlags, flag } from "@palbase/backend";
81
-
82
- export default defineFlags({
83
- flags: {
84
- new_checkout: flag({ type: "boolean", default: false, description: "Gate the redesigned checkout" }),
85
- upload_limit: flag({ type: "number", default: 10 }),
86
- theme: flag({ type: "string", default: "system", variants: ["light", "dark", "system"] }),
87
- limits: flag({ type: "json", default: { daily: 10, burst: 50 } }),
88
- },
89
- });
90
- ```
91
-
92
- Four types: `boolean`, `number`, `string`, `json`. A `json` flag's default is an
93
- object (not an array, not a scalar) and nests at most 3 deep. `variants` is for
94
- `string` only — the flags service rejects it on every other type.
95
-
96
- Author it: `palbase flags add new_checkout --type boolean --default false`, or
97
- `palbase flags add limits --type json --default '{"daily":10}'`. On
98
- deploy, the definitions are upserted to the flags service (idempotent). A flag
99
- removed from the file is **not auto-deleted** (orphan definitions are harmless).
100
-
101
- ## Outbound HTTP — `config/egress.ts`
102
-
103
- Your backend has **no ambient network**. A `fetch()` to an external host is
104
- refused unless the host is declared here, and with no `config/egress.ts` at all
105
- there is no outbound network whatsoever.
106
-
107
- ```ts
108
- import { defineEgress } from "@palbase/backend";
109
-
110
- export default defineEgress({
111
- hosts: ["api.openai.com", ".example.com"], // leading dot also covers subdomains
112
- timeoutMs: 90_000, // per-call ceiling; omitted ⇒ 30_000
113
- });
114
- ```
115
-
116
- Hosts are bare hostnames — https on :443 only, so no scheme, port, path or
117
- wildcard. `timeoutMs` is 1_000–300_000.
118
-
119
- Unlike the three above, this one is **fail-closed**: a malformed host or an
120
- out-of-range `timeoutMs` ABORTS the deploy rather than logging a warning. An
121
- allowlist that silently dropped an entry would be a broken feature, and one that
122
- silently widened would be a hole; an out-of-range timeout is rejected rather than
123
- clamped so your config file and the running system never disagree.
124
-
125
- ### How long a call may take
126
-
127
- `timeoutMs` is a ceiling, not a grant — the call still ends when the invocation
128
- around it ends, and it covers the whole call including redirects (three hops do
129
- not get three budgets).
130
-
131
- | Where the fetch runs | What else bounds it |
132
- |---|---|
133
- | Job (`jobs/`) | Its own `@Job({ timeout })`, max 300s — the longest budget available. |
134
- | Endpoint / webhook | The gateway's request ceiling. Long work belongs in a job. |
135
-
136
- Responses are **buffered whole** (5 MB cap) before your `fetch()` resolves.
137
- Requesting a streaming response from an upstream (`stream: true`, SSE) therefore
138
- buys nothing: no partial output, no earlier first byte, and the entire stream
139
- must still finish inside `timeoutMs`.
140
-
141
- ## How it's applied
142
-
143
- All four are evaluated + applied **on deploy**, the same place `db/schema.ts`
144
- migrations run, reaching each module through your project's gateway with a
145
- service-role key. Storage/notifications/flags are fail-soft — a config error logs
146
- a warning but never aborts the deploy of your code. `config/egress.ts` is the
147
- exception and is fail-closed, for the reason above.
package/docs/resources.md DELETED
@@ -1,97 +0,0 @@
1
- # Resources
2
-
3
- A `Resource` models one external connection — a pooled datastore, a stateless
4
- API client, or a per-user factory. You put it in `resources/`, export an
5
- instance, and **do not register it**: the framework discovers it and sets it up
6
- once at boot. On top of that lifecycle you expose your own clean facade.
7
-
8
- ```ts
9
- import { Resource } from "@palbase/backend";
10
- ```
11
-
12
- ## Lifecycle (boot scope — not per request)
13
-
14
- A resource is created once at process boot — NOT per request. The framework:
15
-
16
- 1. calls `init(env)` **once**, with only the secrets the resource declared.
17
-
18
- That is the whole lifecycle — there is no teardown counterpart. The runtime
19
- recycles an environment by disposing its isolate outright, with no signal
20
- delivered into your code first, so a `shutdown()` you declare is **never
21
- called**. Do not buffer in memory intending to flush on the way out; treat a
22
- write as durable when the call that made it returns. The optional `shutdown()`
23
- member survives in the type because the older process-based runtime did invoke
24
- it on SIGTERM.
25
-
26
- The instance lives for the whole process; your facade methods are called
27
- per-request. This makes "reconnect on every request" structurally impossible.
28
-
29
- ## Pooled datastore
30
-
31
- ```ts
32
- import { Resource } from "@palbase/backend";
33
- import neo4j, { type Driver, type Session } from "neo4j-driver";
34
-
35
- export class Neo4jResource extends Resource {
36
- static secrets = ["NEO4J_URL", "NEO4J_USER", "NEO4J_PASSWORD"] as const;
37
- private driver!: Driver;
38
- async init(env: { NEO4J_URL: string; NEO4J_USER: string; NEO4J_PASSWORD: string }) {
39
- this.driver = neo4j.driver(env.NEO4J_URL, neo4j.auth.basic(env.NEO4J_USER, env.NEO4J_PASSWORD));
40
- }
41
- session(): Session {
42
- return this.driver.session();
43
- }
44
- }
45
-
46
- export const graph = new Neo4jResource();
47
- ```
48
-
49
- ## Stateless API client — `init` only
50
-
51
- ```ts
52
- import { Resource } from "@palbase/backend";
53
- import { Client } from "@googlemaps/google-maps-services-js";
54
-
55
- export class GoogleResource extends Resource {
56
- static secrets = ["GOOGLE_MAPS_KEY"] as const;
57
- private client = new Client();
58
- private key = "";
59
- init(env: { GOOGLE_MAPS_KEY: string }) {
60
- this.key = env.GOOGLE_MAPS_KEY;
61
- }
62
- nearby(lat: number, lng: number) {
63
- return this.client.placesNearby({ params: { location: { lat, lng }, radius: 1500, key: this.key } });
64
- }
65
- }
66
-
67
- export const google = new GoogleResource();
68
- ```
69
-
70
- A per-user (OAuth) resource adds a factory method on the base, e.g.
71
- `github.forUser(token)` — the same single model covers pooled, stateless, and
72
- per-user.
73
-
74
- ## Secrets
75
-
76
- `static secrets` is the contract:
77
-
78
- - It **types** the `env` passed to `init` — only the declared names are
79
- present, each a `string`. An undeclared key is a compile error.
80
- - A declared secret that is **missing at boot fails the deploy**, naming the
81
- secret. Secrets are Environment-scoped; set them with
82
- `palbase secret set NAME ...` or in Studio. A resource is initialised once at
83
- boot, so rotating a secret needs a redeploy/restart.
84
-
85
- `secrets` is optional — a resource that needs none simply omits it and gets an
86
- empty `env`.
87
-
88
- ## Using a resource
89
-
90
- Import the singleton and call your facade — services and handlers reach
91
- resources the same way they reach `Database`:
92
-
93
- ```ts
94
- import { google } from "../resources/google.js";
95
-
96
- const results = (await google.nearby(41.0, 29.0)).data.results;
97
- ```
@@ -1,24 +0,0 @@
1
- import { defineSecrets, secret } from "@palbase/backend";
2
-
3
- // The secrets this backend needs, declared next to the code that needs them.
4
- //
5
- // This file holds NAMES, never values. A value lives only in the environment's
6
- // vault and reaches the process through `Secrets.get("NAME")`; nothing here —
7
- // and nothing in the repository this is committed to — ever holds one.
8
- //
9
- // Declaring the requirement is what makes a missing secret a sentence on your
10
- // terminal instead of a 500 on the first request that touches it: `push`
11
- // compares this list against the target environment's vault and refuses,
12
- // naming what is missing, before anything ships.
13
- //
14
- // `required` defaults to true. The example below is optional because the
15
- // backend genuinely runs without it — a required secret in a fresh scaffold
16
- // would just block your first deploy. Delete it and declare your own.
17
- export default defineSecrets({
18
- secrets: [
19
- secret("SENTRY_DSN", {
20
- required: false,
21
- description: "crash reporting endpoint — errors go unreported without it",
22
- }),
23
- ],
24
- });
File without changes