@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.
- package/dist/bin/palbase-backend.cjs +764 -25
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +4 -4
- package/dist/{chunk-POYAFBLF.js → chunk-FSGSB42K.js} +13 -6
- package/dist/chunk-FSGSB42K.js.map +1 -0
- package/dist/{chunk-SSGAMC26.js → chunk-HAF67F2H.js} +114 -5
- package/dist/chunk-HAF67F2H.js.map +1 -0
- package/dist/{chunk-QMVK4X3V.js → chunk-OMRTHM4X.js} +98 -98
- package/dist/chunk-OMRTHM4X.js.map +1 -0
- package/dist/{chunk-QYOHMVUW.js → chunk-REZU6UKT.js} +755 -24
- package/dist/chunk-REZU6UKT.js.map +1 -0
- package/dist/{chunk-YL4C5NRY.js → chunk-Y5HXVUMP.js} +2 -2
- package/dist/{chunk-N32VDWKH.js → chunk-ZC6Q2BRD.js} +4 -59
- package/dist/chunk-ZC6Q2BRD.js.map +1 -0
- package/dist/db/env.cjs.map +1 -1
- package/dist/db/env.d.cts +3 -21
- package/dist/db/env.d.ts +3 -21
- package/dist/db/index.cjs +117 -6
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +3 -2
- package/dist/db/index.d.ts +3 -2
- package/dist/db/index.js +7 -3
- package/dist/{endpoint-B0LpZixz.d.cts → endpoint-BavvbW4P.d.ts} +116 -14
- package/dist/{endpoint-B0LpZixz.d.ts → endpoint-i8TTCohk.d.cts} +116 -14
- package/dist/engine/index.cjs +764 -25
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +6 -5
- package/dist/engine/index.d.ts +6 -5
- package/dist/engine/index.js +3 -3
- package/dist/{index-g-EzitI-.d.ts → index-B3jmmItD.d.ts} +203 -7
- package/dist/{index-BCNtlG1w.d.ts → index-B7YBEG5w.d.ts} +117 -93
- package/dist/{index-BGSCWlUa.d.cts → index-Bmvx1EvJ.d.cts} +203 -7
- package/dist/{index-B4W6d2VJ.d.cts → index-E7OscPJT.d.cts} +117 -93
- package/dist/index.cjs +509 -1085
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +203 -1133
- package/dist/index.d.ts +203 -1133
- package/dist/index.js +288 -900
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs +3 -57
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.d.cts +3 -2
- package/dist/openapi/index.d.ts +3 -2
- package/dist/openapi/index.js +5 -22
- package/dist/openapi/index.js.map +1 -1
- package/dist/{registry-3BLYv4si.d.ts → registry-C3H2uPeZ.d.cts} +13 -169
- package/dist/{registry-Cw0YEYCg.d.cts → registry-DY3d9l1k.d.ts} +13 -169
- package/dist/{purchases/keys.cjs → stack.cjs} +4 -4
- package/dist/stack.cjs.map +1 -0
- package/dist/stack.d.cts +76 -0
- package/dist/stack.d.ts +76 -0
- package/dist/stack.js +1 -0
- package/docs/README.md +7 -6
- package/docs/endpoints.md +1 -1
- package/docs/errors.md +9 -0
- package/docs/llms-full.txt +17 -261
- package/docs/llms.txt +0 -2
- package/package.json +7 -7
- package/template/package.json +1 -1
- package/dist/chunk-N32VDWKH.js.map +0 -1
- package/dist/chunk-POYAFBLF.js.map +0 -1
- package/dist/chunk-QMVK4X3V.js.map +0 -1
- package/dist/chunk-QYOHMVUW.js.map +0 -1
- package/dist/chunk-SSGAMC26.js.map +0 -1
- package/dist/purchases/keys.cjs.map +0 -1
- package/dist/purchases/keys.d.cts +0 -42
- package/dist/purchases/keys.d.ts +0 -42
- package/dist/purchases/keys.js +0 -1
- package/docs/config.md +0 -147
- package/docs/resources.md +0 -97
- package/template/config/secrets.ts +0 -24
- /package/dist/{chunk-YL4C5NRY.js.map → chunk-Y5HXVUMP.js.map} +0 -0
- /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
|
|
File without changes
|