@palbase/backend 14.3.0 → 15.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.
@@ -1,4 +1,4 @@
1
- import { D as DBClient, P as PBRequest, a as PalbaseModuleClients, L as Logger, C as CacheClient, U as User } from '../endpoint-BG-JVUPL.cjs';
1
+ import { D as DBClient, P as PBRequest, a as PalbaseModuleClients, L as Logger, C as CacheClient, U as User } from '../endpoint-BW8bn1y-.cjs';
2
2
  import 'zod';
3
3
 
4
4
  /** Mock DB client with tracking and seed data support. */
@@ -1,4 +1,4 @@
1
- import { D as DBClient, P as PBRequest, a as PalbaseModuleClients, L as Logger, C as CacheClient, U as User } from '../endpoint-BG-JVUPL.js';
1
+ import { D as DBClient, P as PBRequest, a as PalbaseModuleClients, L as Logger, C as CacheClient, U as User } from '../endpoint-BW8bn1y-.js';
2
2
  import 'zod';
3
3
 
4
4
  /** Mock DB client with tracking and seed data support. */
package/docs/auth.md CHANGED
@@ -128,6 +128,43 @@ Or declare it on the route and let the runtime enforce it:
128
128
  Unverified callers get `403 email_not_verified`. Use the route flag to fence a
129
129
  whole controller, and the inline check when only part of a handler cares.
130
130
 
131
+ ## Social sign-in and account linking
132
+
133
+ Signing in with Google or Apple is a client flow — the Web and iOS SDK docs
134
+ cover the redirect and code-exchange calls. The part your backend cares about
135
+ is which `user.id` comes out of it, because that id is what every row you wrote
136
+ is keyed on.
137
+
138
+ **One address, one account.** If someone signs up with `a@x.com` + password and
139
+ later signs in with Google on the same address, they land on the **same
140
+ `user.id`**, in the same session, with all their data. Palbase links the Google
141
+ identity onto the existing account instead of creating a second one. A second
142
+ account on the same address is not merely avoided — it is impossible: the email
143
+ is uniquely indexed.
144
+
145
+ The link happens only when the provider asserts the address is verified
146
+ (`email_verified`). If it does not, and the address already belongs to an
147
+ account, sign-in is refused with `409 email_already_registered` rather than
148
+ silently landing somewhere new.
149
+
150
+ **Linking into an unverified account clears its password.** An account whose
151
+ email was never verified could have been registered by anyone — including
152
+ someone who does not own the address. When a provider proves ownership and
153
+ links in, the untrusted password is removed and any session opened with it is
154
+ revoked. `@User()` still resolves to the same id, so your data keeps working;
155
+ the user simply no longer has a password until they set one.
156
+
157
+ **Going the other way** — a user who started with Google and now wants a
158
+ password — is the password *reset* flow, not `updatePassword`. There is no
159
+ current password to supply, so `updatePassword` fails; `resetPassword(email)`
160
+ followed by `confirmPasswordReset` sets the first one. Note that completing it
161
+ signs the user out everywhere.
162
+
163
+ To render the right form, ask the client SDK which credentials the account has
164
+ (`pb.auth.getSignInMethods()` in `@palbase/web`) — it reports whether a local
165
+ password exists and which providers are linked, so a Google-only user is never
166
+ shown a password field they cannot fill.
167
+
131
168
  ## Password reset and magic links
132
169
 
133
170
  Both are client-driven and need no backend code: the client SDK calls the auth
@@ -772,6 +772,43 @@ Or declare it on the route and let the runtime enforce it:
772
772
  Unverified callers get `403 email_not_verified`. Use the route flag to fence a
773
773
  whole controller, and the inline check when only part of a handler cares.
774
774
 
775
+ ## Social sign-in and account linking
776
+
777
+ Signing in with Google or Apple is a client flow — the Web and iOS SDK docs
778
+ cover the redirect and code-exchange calls. The part your backend cares about
779
+ is which `user.id` comes out of it, because that id is what every row you wrote
780
+ is keyed on.
781
+
782
+ **One address, one account.** If someone signs up with `a@x.com` + password and
783
+ later signs in with Google on the same address, they land on the **same
784
+ `user.id`**, in the same session, with all their data. Palbase links the Google
785
+ identity onto the existing account instead of creating a second one. A second
786
+ account on the same address is not merely avoided — it is impossible: the email
787
+ is uniquely indexed.
788
+
789
+ The link happens only when the provider asserts the address is verified
790
+ (`email_verified`). If it does not, and the address already belongs to an
791
+ account, sign-in is refused with `409 email_already_registered` rather than
792
+ silently landing somewhere new.
793
+
794
+ **Linking into an unverified account clears its password.** An account whose
795
+ email was never verified could have been registered by anyone — including
796
+ someone who does not own the address. When a provider proves ownership and
797
+ links in, the untrusted password is removed and any session opened with it is
798
+ revoked. `@User()` still resolves to the same id, so your data keeps working;
799
+ the user simply no longer has a password until they set one.
800
+
801
+ **Going the other way** — a user who started with Google and now wants a
802
+ password — is the password *reset* flow, not `updatePassword`. There is no
803
+ current password to supply, so `updatePassword` fails; `resetPassword(email)`
804
+ followed by `confirmPasswordReset` sets the first one. Note that completing it
805
+ signs the user out everywhere.
806
+
807
+ To render the right form, ask the client SDK which credentials the account has
808
+ (`pb.auth.getSignInMethods()` in `@palbase/web`) — it reports whether a local
809
+ password exists and which providers are linked, so a Google-only user is never
810
+ shown a password field they cannot fill.
811
+
775
812
  ## Password reset and magic links
776
813
 
777
814
  Both are client-driven and need no backend code: the client SDK calls the auth
@@ -1504,13 +1541,46 @@ const { data: doc } = await Documents.doc("rooms/abc").get();
1504
1541
 
1505
1542
  ## Notifications
1506
1543
 
1544
+ **No provider setup is required.** Every project starts with a managed sender, so
1545
+ email and SMS deliver on a brand-new project with no provider, no API key, and no
1546
+ `config/notifications.ts`. You configure your own provider only to send from your
1547
+ own domain — see [Config](./config.md).
1548
+
1507
1549
  ```ts
1508
1550
  import { Notifications } from "@palbase/backend";
1509
- await Notifications.email.send({ /* PalbaseEmailSendParams */ });
1510
- await Notifications.push.send({ /* PalbasePushSendParams */ });
1511
- await Notifications.sms.send({ /* PalbaseSmsSendParams */ });
1551
+
1552
+ // Email — `to` takes one address or many.
1553
+ await Notifications.email.send({
1554
+ to: user.email,
1555
+ subject: "Your receipt",
1556
+ html: "<p>Thanks!</p>", // or `text`, or a server template:
1557
+ });
1558
+ await Notifications.email.send({
1559
+ to: user.email,
1560
+ templateSlug: "receipt", // defined in config/notifications.ts
1561
+ locale: "tr", // without this the extra locale rows are unreachable
1562
+ variables: { total: "₺240" },
1563
+ });
1564
+
1565
+ // Push — `to` is a device token, a list of them, or { topic }.
1566
+ await Notifications.push.send({
1567
+ to: { topic: "announcements" },
1568
+ title: "Deploy finished", // string, or a { "en": …, "tr": … } locale map
1569
+ body: "Your backend is live.",
1570
+ deep_link: "todoapp://deploys",
1571
+ });
1572
+
1573
+ // SMS — `body` and `templateSlug` are mutually exclusive.
1574
+ await Notifications.sms.send({ to: user.phone, body: "Your code is 123456" });
1512
1575
  ```
1513
1576
 
1577
+ A send that no provider can carry is rejected on the request itself with
1578
+ `provider_not_configured` — it is never accepted and then dropped, so a
1579
+ successful call means a real sender took the message.
1580
+
1581
+ `Notifications` also carries `verify` (phone OTP send/check), `inbox` (in-app
1582
+ messages), and `registerDevice` / `unregisterDevice` for push tokens.
1583
+
1514
1584
  ## Flags
1515
1585
 
1516
1586
  ```ts
package/docs/services.md CHANGED
@@ -75,13 +75,46 @@ const { data: doc } = await Documents.doc("rooms/abc").get();
75
75
 
76
76
  ## Notifications
77
77
 
78
+ **No provider setup is required.** Every project starts with a managed sender, so
79
+ email and SMS deliver on a brand-new project with no provider, no API key, and no
80
+ `config/notifications.ts`. You configure your own provider only to send from your
81
+ own domain — see [Config](./config.md).
82
+
78
83
  ```ts
79
84
  import { Notifications } from "@palbase/backend";
80
- await Notifications.email.send({ /* PalbaseEmailSendParams */ });
81
- await Notifications.push.send({ /* PalbasePushSendParams */ });
82
- await Notifications.sms.send({ /* PalbaseSmsSendParams */ });
85
+
86
+ // Email — `to` takes one address or many.
87
+ await Notifications.email.send({
88
+ to: user.email,
89
+ subject: "Your receipt",
90
+ html: "<p>Thanks!</p>", // or `text`, or a server template:
91
+ });
92
+ await Notifications.email.send({
93
+ to: user.email,
94
+ templateSlug: "receipt", // defined in config/notifications.ts
95
+ locale: "tr", // without this the extra locale rows are unreachable
96
+ variables: { total: "₺240" },
97
+ });
98
+
99
+ // Push — `to` is a device token, a list of them, or { topic }.
100
+ await Notifications.push.send({
101
+ to: { topic: "announcements" },
102
+ title: "Deploy finished", // string, or a { "en": …, "tr": … } locale map
103
+ body: "Your backend is live.",
104
+ deep_link: "todoapp://deploys",
105
+ });
106
+
107
+ // SMS — `body` and `templateSlug` are mutually exclusive.
108
+ await Notifications.sms.send({ to: user.phone, body: "Your code is 123456" });
83
109
  ```
84
110
 
111
+ A send that no provider can carry is rejected on the request itself with
112
+ `provider_not_configured` — it is never accepted and then dropped, so a
113
+ successful call means a real sender took the message.
114
+
115
+ `Notifications` also carries `verify` (phone OTP send/check), `inbox` (in-app
116
+ messages), and `registerDevice` / `unregisterDevice` for push tokens.
117
+
85
118
  ## Flags
86
119
 
87
120
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@palbase/backend",
3
- "version": "14.3.0",
3
+ "version": "15.0.0",
4
4
  "description": "Palbase Backend SDK — class controllers (@Controller/@Get/@Post + @Body/@QueryParams/@Param), error classes, schema DSL",
5
5
  "license": "MIT",
6
6
  "repository": {