@pylonsync/sdk 0.22.27 → 0.22.29

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.d.ts CHANGED
@@ -1185,40 +1185,63 @@ export type ManifestLlmConfig = {
1185
1185
  /**
1186
1186
  * Developer-facing config for `defineConnection({...})`. Each entry
1187
1187
  * adds a `ctx.connections.<name>` surface to mutation + action ctx
1188
- * (server-side OAuth tokens, never visible to the browser).
1188
+ * (server-side tokens and keys, never visible to the browser).
1189
1189
  *
1190
- * `provider` selects the OAuth client wire shape from pylon-auth's
1191
- * built-in list (`google`, `github`, `slack`, `microsoft`, etc.).
1192
- * `name` is the app-facing key — different connections can target
1193
- * the same provider with different scopes
1190
+ * `provider` selects the provider: a sign-in provider from pylon-auth's
1191
+ * built-in list (`google`, `github`, `slack`, `microsoft`, etc.) or a
1192
+ * business integration (`square`, `stripe`, `clover`, `calendly`,
1193
+ * `mailchimp`). `name` is the app-facing key — different connections
1194
+ * can target the same provider with different scopes
1194
1195
  * (e.g. `google-calendar` vs `google-drive`).
1195
1196
  *
1197
+ * `owner: "app"` makes one connection for the whole app — the
1198
+ * business's own Square or Stripe account — that any server function
1199
+ * reads (including for an anonymous visitor) and only an admin changes.
1200
+ * The default `owner: "user"` links each signed-in user's own account.
1201
+ *
1202
+ * `kind: "api_key"` stores key fields instead of running OAuth (Toast's
1203
+ * Standard API access, a Stripe restricted key). `fields` names them;
1204
+ * the default is the provider's own list (`stripe`: `secretKey`,
1205
+ * `square`: `accessToken`, `toast`: `clientId`, `clientSecret`,
1206
+ * `restaurantGuid`), else `apiKey`.
1207
+ *
1196
1208
  * Configuration: per-provider client id + secret come from env
1197
1209
  * (`PYLON_OAUTH_<PROVIDER>_CLIENT_ID`, `PYLON_OAUTH_<PROVIDER>_CLIENT_SECRET`).
1198
1210
  * Callback URL is derived from `PYLON_PUBLIC_URL` +
1199
- * `/api/connections/<name>/callback`.
1211
+ * `/api/connections/<name>/callback`. On a platform that brokers
1212
+ * connections (`PYLON_CONNECTIONS_BROKER_URL`), the platform runs the
1213
+ * OAuth flow for app connections and the app needs no client secret.
1200
1214
  *
1201
1215
  * Storage: the framework auto-creates a `_Connection` entity at
1202
- * boot when any connection is declared; token fields are AEAD-
1216
+ * boot when any connection is declared; token and key fields are AEAD-
1203
1217
  * encrypted at rest (`PYLON_ENCRYPTION_KEY` is REQUIRED — boot
1204
1218
  * fails without it when connections are declared).
1205
1219
  */
1206
1220
  export type ConnectionConfig = {
1207
1221
  /** App-facing key. `ctx.connections.get(name)` matches on this. */
1208
1222
  name: string;
1209
- /** Provider identifier matching pylon-auth's OAuth client. */
1223
+ /** Provider identifier: a pylon-auth OAuth provider or an integration. */
1210
1224
  provider: string;
1211
1225
  /** Whitespace-separated scopes. Empty = provider default. */
1212
1226
  scopes?: string;
1227
+ /** `"user"` (default) or `"app"`. */
1228
+ owner?: "user" | "app";
1229
+ /** `"oauth"` (default) or `"api_key"`. */
1230
+ kind?: "oauth" | "api_key";
1231
+ /** API-key field names, for `kind: "api_key"`. */
1232
+ fields?: string[];
1213
1233
  };
1214
1234
  export type ManifestConnection = {
1215
1235
  name: string;
1216
1236
  provider: string;
1217
1237
  scopes?: string;
1238
+ owner?: "user" | "app";
1239
+ kind?: "oauth" | "api_key";
1240
+ fields?: string[];
1218
1241
  };
1219
1242
  /**
1220
- * Declare a server-side OAuth integration. Returns the manifest
1221
- * entry the runtime parses. Re-exported through `app.ts`:
1243
+ * Declare a server-side integration. Returns the manifest entry the
1244
+ * runtime parses. Re-exported through `app.ts`:
1222
1245
  *
1223
1246
  * ```ts
1224
1247
  * import { defineConnection } from "@pylonsync/sdk";
@@ -1228,11 +1251,19 @@ export type ManifestConnection = {
1228
1251
  * provider: "google",
1229
1252
  * scopes: "email profile https://www.googleapis.com/auth/calendar.readonly",
1230
1253
  * });
1254
+ *
1255
+ * // The business's Square account, used by the store's checkout.
1256
+ * export const square = defineConnection({
1257
+ * name: "square",
1258
+ * provider: "square",
1259
+ * owner: "app",
1260
+ * scopes: "ITEMS_READ ITEMS_WRITE INVENTORY_READ INVENTORY_WRITE ORDERS_WRITE PAYMENTS_WRITE",
1261
+ * });
1231
1262
  * ```
1232
1263
  *
1233
- * `buildManifest({ connections: [googleConn] })` carries this into
1234
- * the manifest; the runtime auto-creates the `_Connection` entity
1235
- * and exposes `ctx.connections.get("google")` to actions.
1264
+ * `buildManifest({ connections: [googleConn, square] })` carries these
1265
+ * into the manifest; the runtime auto-creates the `_Connection` entity
1266
+ * and exposes `ctx.connections.get("square")` to actions.
1236
1267
  */
1237
1268
  export declare function defineConnection(cfg: ConnectionConfig): ManifestConnection;
1238
1269
  /**
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.22.27",
6
+ "version": "0.22.29",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "types": "./dist/index.d.ts",
@@ -0,0 +1,35 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { defineConnection } from "./index";
3
+
4
+ describe("defineConnection", () => {
5
+ test("leaves defaults out of the manifest", () => {
6
+ expect(defineConnection({ name: "google", provider: "google", owner: "user", kind: "oauth" })).toEqual({
7
+ name: "google",
8
+ provider: "google",
9
+ });
10
+ });
11
+
12
+ test("carries an app connection and its key fields", () => {
13
+ expect(
14
+ defineConnection({
15
+ name: "toast",
16
+ provider: "toast",
17
+ owner: "app",
18
+ kind: "api_key",
19
+ fields: ["clientId", "clientSecret", "restaurantGuid"],
20
+ }),
21
+ ).toEqual({
22
+ name: "toast",
23
+ provider: "toast",
24
+ owner: "app",
25
+ kind: "api_key",
26
+ fields: ["clientId", "clientSecret", "restaurantGuid"],
27
+ });
28
+ });
29
+
30
+ test("refuses fields on an OAuth connection", () => {
31
+ expect(() => defineConnection({ name: "sq", provider: "square", fields: ["x"] })).toThrow(
32
+ /fields apply to kind "api_key" only/,
33
+ );
34
+ });
35
+ });
package/src/index.ts CHANGED
@@ -2145,42 +2145,65 @@ export type ManifestLlmConfig = {
2145
2145
  /**
2146
2146
  * Developer-facing config for `defineConnection({...})`. Each entry
2147
2147
  * adds a `ctx.connections.<name>` surface to mutation + action ctx
2148
- * (server-side OAuth tokens, never visible to the browser).
2148
+ * (server-side tokens and keys, never visible to the browser).
2149
2149
  *
2150
- * `provider` selects the OAuth client wire shape from pylon-auth's
2151
- * built-in list (`google`, `github`, `slack`, `microsoft`, etc.).
2152
- * `name` is the app-facing key — different connections can target
2153
- * the same provider with different scopes
2150
+ * `provider` selects the provider: a sign-in provider from pylon-auth's
2151
+ * built-in list (`google`, `github`, `slack`, `microsoft`, etc.) or a
2152
+ * business integration (`square`, `stripe`, `clover`, `calendly`,
2153
+ * `mailchimp`). `name` is the app-facing key — different connections
2154
+ * can target the same provider with different scopes
2154
2155
  * (e.g. `google-calendar` vs `google-drive`).
2155
2156
  *
2157
+ * `owner: "app"` makes one connection for the whole app — the
2158
+ * business's own Square or Stripe account — that any server function
2159
+ * reads (including for an anonymous visitor) and only an admin changes.
2160
+ * The default `owner: "user"` links each signed-in user's own account.
2161
+ *
2162
+ * `kind: "api_key"` stores key fields instead of running OAuth (Toast's
2163
+ * Standard API access, a Stripe restricted key). `fields` names them;
2164
+ * the default is the provider's own list (`stripe`: `secretKey`,
2165
+ * `square`: `accessToken`, `toast`: `clientId`, `clientSecret`,
2166
+ * `restaurantGuid`), else `apiKey`.
2167
+ *
2156
2168
  * Configuration: per-provider client id + secret come from env
2157
2169
  * (`PYLON_OAUTH_<PROVIDER>_CLIENT_ID`, `PYLON_OAUTH_<PROVIDER>_CLIENT_SECRET`).
2158
2170
  * Callback URL is derived from `PYLON_PUBLIC_URL` +
2159
- * `/api/connections/<name>/callback`.
2171
+ * `/api/connections/<name>/callback`. On a platform that brokers
2172
+ * connections (`PYLON_CONNECTIONS_BROKER_URL`), the platform runs the
2173
+ * OAuth flow for app connections and the app needs no client secret.
2160
2174
  *
2161
2175
  * Storage: the framework auto-creates a `_Connection` entity at
2162
- * boot when any connection is declared; token fields are AEAD-
2176
+ * boot when any connection is declared; token and key fields are AEAD-
2163
2177
  * encrypted at rest (`PYLON_ENCRYPTION_KEY` is REQUIRED — boot
2164
2178
  * fails without it when connections are declared).
2165
2179
  */
2166
2180
  export type ConnectionConfig = {
2167
2181
  /** App-facing key. `ctx.connections.get(name)` matches on this. */
2168
2182
  name: string;
2169
- /** Provider identifier matching pylon-auth's OAuth client. */
2183
+ /** Provider identifier: a pylon-auth OAuth provider or an integration. */
2170
2184
  provider: string;
2171
2185
  /** Whitespace-separated scopes. Empty = provider default. */
2172
2186
  scopes?: string;
2187
+ /** `"user"` (default) or `"app"`. */
2188
+ owner?: "user" | "app";
2189
+ /** `"oauth"` (default) or `"api_key"`. */
2190
+ kind?: "oauth" | "api_key";
2191
+ /** API-key field names, for `kind: "api_key"`. */
2192
+ fields?: string[];
2173
2193
  };
2174
2194
 
2175
2195
  export type ManifestConnection = {
2176
2196
  name: string;
2177
2197
  provider: string;
2178
2198
  scopes?: string;
2199
+ owner?: "user" | "app";
2200
+ kind?: "oauth" | "api_key";
2201
+ fields?: string[];
2179
2202
  };
2180
2203
 
2181
2204
  /**
2182
- * Declare a server-side OAuth integration. Returns the manifest
2183
- * entry the runtime parses. Re-exported through `app.ts`:
2205
+ * Declare a server-side integration. Returns the manifest entry the
2206
+ * runtime parses. Re-exported through `app.ts`:
2184
2207
  *
2185
2208
  * ```ts
2186
2209
  * import { defineConnection } from "@pylonsync/sdk";
@@ -2190,17 +2213,31 @@ export type ManifestConnection = {
2190
2213
  * provider: "google",
2191
2214
  * scopes: "email profile https://www.googleapis.com/auth/calendar.readonly",
2192
2215
  * });
2216
+ *
2217
+ * // The business's Square account, used by the store's checkout.
2218
+ * export const square = defineConnection({
2219
+ * name: "square",
2220
+ * provider: "square",
2221
+ * owner: "app",
2222
+ * scopes: "ITEMS_READ ITEMS_WRITE INVENTORY_READ INVENTORY_WRITE ORDERS_WRITE PAYMENTS_WRITE",
2223
+ * });
2193
2224
  * ```
2194
2225
  *
2195
- * `buildManifest({ connections: [googleConn] })` carries this into
2196
- * the manifest; the runtime auto-creates the `_Connection` entity
2197
- * and exposes `ctx.connections.get("google")` to actions.
2226
+ * `buildManifest({ connections: [googleConn, square] })` carries these
2227
+ * into the manifest; the runtime auto-creates the `_Connection` entity
2228
+ * and exposes `ctx.connections.get("square")` to actions.
2198
2229
  */
2199
2230
  export function defineConnection(cfg: ConnectionConfig): ManifestConnection {
2231
+ if (cfg.fields && cfg.fields.length > 0 && cfg.kind !== "api_key") {
2232
+ throw new Error(`defineConnection("${cfg.name}"): fields apply to kind "api_key" only`);
2233
+ }
2200
2234
  return {
2201
2235
  name: cfg.name,
2202
2236
  provider: cfg.provider,
2203
2237
  ...(cfg.scopes ? { scopes: cfg.scopes } : {}),
2238
+ ...(cfg.owner && cfg.owner !== "user" ? { owner: cfg.owner } : {}),
2239
+ ...(cfg.kind && cfg.kind !== "oauth" ? { kind: cfg.kind } : {}),
2240
+ ...(cfg.fields && cfg.fields.length > 0 ? { fields: [...cfg.fields] } : {}),
2204
2241
  };
2205
2242
  }
2206
2243