@happyvertical/smrt-social 0.54.2 → 0.54.4

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.
@@ -39,6 +39,14 @@ export interface OAuthStateOptions extends SmrtObjectOptions {
39
39
  * Tenant ID for multi-tenant isolation
40
40
  */
41
41
  tenantId?: string | null;
42
+ /**
43
+ * User who started the OAuth flow
44
+ */
45
+ createdByUserId?: string | null;
46
+ /**
47
+ * Application-defined path to return to after the callback
48
+ */
49
+ returnTo?: string | null;
42
50
  }
43
51
  /**
44
52
  * Temporary OAuth state for social account connection
@@ -47,6 +55,9 @@ export interface OAuthStateOptions extends SmrtObjectOptions {
47
55
  * - Verify callback requests match initiated requests (CSRF protection)
48
56
  * - Store PKCE code verifier for code exchange
49
57
  * - Track redirect URI and scopes for verification
58
+ * - Bind the flow to the user who started it (`createdByUserId`) and record
59
+ * where the host returns afterwards (`returnTo`), so one fixed callback
60
+ * can serve every tenant
50
61
  *
51
62
  * These records should be cleaned up after successful connection
52
63
  * or after expiration.
@@ -103,6 +114,22 @@ export declare class OAuthState extends SmrtObject {
103
114
  * States should be short-lived (10 minutes typical)
104
115
  */
105
116
  expiresAt: Date;
117
+ /**
118
+ * User who started the OAuth flow.
119
+ *
120
+ * A host with one fixed callback for every tenant compares this to the
121
+ * signed-in user on callback and rejects a mismatch, so a forwarded or
122
+ * leaked state cannot be completed by someone else. A `@crossPackageRef` to
123
+ * `@happyvertical/smrt-users:User` (native uuid on PostgreSQL, no DDL FK).
124
+ * Null for states created before the column existed.
125
+ */
126
+ createdByUserId: string | null;
127
+ /**
128
+ * Application-defined path to return to after the callback, for example the
129
+ * settings page that started the flow. Opaque to the framework: the host
130
+ * that reads it must validate it as a same-origin path before redirecting.
131
+ */
132
+ returnTo: string | null;
106
133
  constructor(options?: OAuthStateOptions);
107
134
  /**
108
135
  * Check if the state has expired
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-state.d.ts","sourceRoot":"","sources":["../src/oauth-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAE5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D;;OAEG;IACH,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAE9B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;OAEG;IACH,SAAS,CAAC,EAAE,IAAI,CAAC;IAEjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBASa,UAAW,SAAQ,UAAU;IACxC;;OAEG;IAEH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B;;OAEG;IACH,QAAQ,EAAE,kBAAkB,CAAa;IAEzC;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAM;IAEnB;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnC;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAM;IAEzB;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAM;IAEtB;;;OAGG;IACH,SAAS,EAAE,IAAI,CAAyC;gBAE5C,OAAO,GAAE,iBAAsB;IAc3C;;OAEG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;OAEG;IACH,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO;IAI3C;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,MAAM;IAI9B;;;OAGG;IACH,MAAM,CAAC,oBAAoB,IAAI,MAAM;IAQrC;;;OAGG;WACU,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAOtE"}
1
+ {"version":3,"file":"oauth-state.d.ts","sourceRoot":"","sources":["../src/oauth-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAmB,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAE7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D;;OAEG;IACH,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAE9B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;OAEG;IACH,SAAS,CAAC,EAAE,IAAI,CAAC;IAEjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBASa,UAAW,SAAQ,UAAU;IACxC;;OAEG;IAEH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B;;OAEG;IACH,QAAQ,EAAE,kBAAkB,CAAa;IAEzC;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAM;IAEnB;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnC;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAM;IAEzB;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAM;IAEtB;;;OAGG;IACH,SAAS,EAAE,IAAI,CAAyC;IAExD;;;;;;;;OAQG;IAEH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEtC;;;;OAIG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;gBAEnB,OAAO,GAAE,iBAAsB;IAiB3C;;OAEG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;OAEG;IACH,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO;IAI3C;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,MAAM;IAI9B;;;OAGG;IACH,MAAM,CAAC,oBAAoB,IAAI,MAAM;IAQrC;;;OAGG;WACU,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAOtE"}
@@ -3,13 +3,13 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-social",
6
- "packageVersion": "0.54.2",
6
+ "packageVersion": "0.54.4",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "a28f3c03208db2fe1673dfaf167cfaabb12b09bcdf5b5eeaade2553e9299103e",
11
- "packageJson": "966ccd306d822abbfd15617f6939e2980cf409beedacb479c173dc5cb4b1cff0",
12
- "agents": "8649047a57ee16eb46557ab5a9504315421f6f78b434ee19ed0d4df24b45f20d"
10
+ "manifest": "5d2b297b125dab894da7115d298875e7b293085967791ef4a5358104a0d1d5e9",
11
+ "packageJson": "5f6e97155301e69b9fece15031f57273ccf4db421668b3916005910a1a28aa54",
12
+ "agents": "7596f706e6513fd86254f510301ec9696520655bdf19f932ec2267ca9d5767ee"
13
13
  },
14
14
  "exports": [
15
15
  ".",
@@ -64,7 +64,7 @@
64
64
  "youtube"
65
65
  ],
66
66
  "risks": [
67
- "cross-package-refs:3",
67
+ "cross-package-refs:4",
68
68
  "sensitive-fields-excluded",
69
69
  "sti-inheritance"
70
70
  ],
@@ -134,9 +134,30 @@
134
134
  "type": "datetime",
135
135
  "required": false,
136
136
  "columnType": "TIMESTAMP"
137
+ },
138
+ {
139
+ "name": "createdByUserId",
140
+ "type": "crossPackageRef",
141
+ "required": false,
142
+ "related": "@happyvertical/smrt-users:User",
143
+ "columnType": "UUID"
144
+ },
145
+ {
146
+ "name": "returnTo",
147
+ "type": "text",
148
+ "required": false,
149
+ "columnType": "TEXT"
150
+ }
151
+ ],
152
+ "relationships": [
153
+ {
154
+ "name": "createdByUserId",
155
+ "type": "crossPackageRef",
156
+ "required": false,
157
+ "related": "@happyvertical/smrt-users:User",
158
+ "columnType": "UUID"
137
159
  }
138
160
  ],
139
- "relationships": [],
140
161
  "methods": [
141
162
  "generateCodeChallenge",
142
163
  "generateCodeVerifier",
@@ -302,6 +323,7 @@
302
323
  }
303
324
  ],
304
325
  "relationshipFeatures": [
326
+ "crossPackageRef",
305
327
  "uuidColumns"
306
328
  ],
307
329
  "tags": [],
@@ -1079,11 +1101,20 @@
1079
1101
  "fields": [],
1080
1102
  "relationships": [],
1081
1103
  "methods": [
1104
+ "consume",
1082
1105
  "deleteExpired",
1083
1106
  "findByState",
1084
1107
  "findExpired"
1085
1108
  ],
1086
1109
  "methodSignatures": [
1110
+ {
1111
+ "name": "consume",
1112
+ "async": true,
1113
+ "params": [
1114
+ "state: OAuthState"
1115
+ ],
1116
+ "returns": "Promise<boolean>"
1117
+ },
1087
1118
  {
1088
1119
  "name": "deleteExpired",
1089
1120
  "async": true,
@@ -1140,6 +1171,12 @@
1140
1171
  "operation": "findByState",
1141
1172
  "objectName": "@happyvertical/smrt-social:OAuthStateCollection"
1142
1173
  },
1174
+ {
1175
+ "kind": "cli",
1176
+ "name": "oauthstatecollection_consume",
1177
+ "operation": "consume",
1178
+ "objectName": "@happyvertical/smrt-social:OAuthStateCollection"
1179
+ },
1143
1180
  {
1144
1181
  "kind": "cli",
1145
1182
  "name": "oauthstatecollection_findExpired",
@@ -1153,6 +1190,15 @@
1153
1190
  "objectName": "@happyvertical/smrt-social:OAuthStateCollection"
1154
1191
  }
1155
1192
  ],
1193
+ "withheldSurfaces": [
1194
+ {
1195
+ "kind": "api",
1196
+ "operation": "consume",
1197
+ "code": "not-wireable",
1198
+ "reason": "not routed: parameter `state`: `OAuthState` is a model class instance, not JSON data",
1199
+ "objectName": "@happyvertical/smrt-social:OAuthStateCollection"
1200
+ }
1201
+ ],
1156
1202
  "relationshipFeatures": [
1157
1203
  "uuidColumns"
1158
1204
  ],
@@ -1968,6 +2014,12 @@
1968
2014
  "operation": "findByState",
1969
2015
  "objectName": "@happyvertical/smrt-social:OAuthStateCollection"
1970
2016
  },
2017
+ {
2018
+ "kind": "cli",
2019
+ "name": "oauthstatecollection_consume",
2020
+ "operation": "consume",
2021
+ "objectName": "@happyvertical/smrt-social:OAuthStateCollection"
2022
+ },
1971
2023
  {
1972
2024
  "kind": "cli",
1973
2025
  "name": "oauthstatecollection_findExpired",
@@ -2212,11 +2264,11 @@
2212
2264
  "prompts": [],
2213
2265
  "relationshipsV2": {
2214
2266
  "foreignKeyFields": 2,
2215
- "crossPackageRefFields": 3,
2267
+ "crossPackageRefFields": 4,
2216
2268
  "junctionCollections": 0,
2217
2269
  "hierarchicalObjects": 0,
2218
2270
  "polymorphicAssociations": 0,
2219
- "uuidColumns": 26
2271
+ "uuidColumns": 28
2220
2272
  },
2221
- "agentDoc": "# @happyvertical/smrt-social\n\nSocial media account management with OAuth and post scheduling. Supports YouTube, Threads, X (Twitter), Bluesky.\n\n## Models\n\n- **SocialAccount** (STI): `platform`, `accessToken`/`refreshToken`, `tokenExpiresAt`, `status` (connected/expired/error), `linkBehavior` (description/reply/none). `isTokenExpired` checks with 5-min buffer. `isReady` gate checks active + connected + token present + not expired.\n- **SocialPost**: `scheduledAt`, `publishedAt`, `status` (draft/scheduled/publishing/published/failed), `analytics` JSON (views/likes/comments/shares/clicks). `createdByUserId` (nullable `@crossPackageRef` to `smrt-users:User`, uuid on PostgreSQL) records the poster; pass it to `createDraft()` so a failed publish can be reported to that person. Agent/schedule posts leave it null; `recordPublishSuccess`/`Failure` never change it. Adding the column is additive: consumers run `db:migrate`.\n- **OAuthState** (STI): CSRF token + PKCE `codeVerifier` with 10-min TTL.\n\n## Gotchas\n\n- **Tokens not encrypted**: OAuth tokens stored as plaintext — TODO for smrt-secrets integration\n- **No auto-publishing**: `scheduledAt` is metadata only — app must implement job runner to trigger publishing\n- **Analytics manual**: `analytics` field must be updated by platform sync, not auto-populated\n- **Platform enum hardcoded**: youtube/threads/x/bluesky — extending requires code changes\n- **OAuthState TTL**: 10 minutes, app must clean up expired states\n- **Optional tenancy** on all models\n- **Relationship targets**: `SocialPost.socialAccountId` and\n `SocialPostAnalyticsSnapshot.socialPostId` are same-package `@foreignKey`\n columns; `SocialPost.videoContentId` is a `@crossPackageRef` to\n `@happyvertical/smrt-video:VideoContent` (no value import back into this\n package). They previously used `@foreignKey(() => X)`, whose target resolved\n to `''`, so none of them carried a relationship edge (#2379).\n"
2273
+ "agentDoc": "# @happyvertical/smrt-social\n\nSocial media account management with OAuth and post scheduling. Supports YouTube, Threads, X (Twitter), Bluesky.\n\n## Models\n\n- **SocialAccount** (STI): `platform`, `accessToken`/`refreshToken`, `tokenExpiresAt`, `status` (connected/expired/error), `linkBehavior` (description/reply/none). `isTokenExpired` checks with 5-min buffer. `isReady` gate checks active + connected + token present + not expired.\n- **SocialPost**: `scheduledAt`, `publishedAt`, `status` (draft/scheduled/publishing/published/failed), `analytics` JSON (views/likes/comments/shares/clicks). `createdByUserId` (nullable `@crossPackageRef` to `smrt-users:User`, uuid on PostgreSQL) records the poster; pass it to `createDraft()` so a failed publish can be reported to that person. Agent/schedule posts leave it null; `recordPublishSuccess`/`Failure` never change it. Adding the column is additive: consumers run `db:migrate`.\n- **OAuthState** (STI): CSRF token + PKCE `codeVerifier` with 10-min TTL. `createdByUserId` (nullable `@crossPackageRef` to `smrt-users:User`) binds the flow to its initiator and `returnTo` stores an opaque host path; a host with one fixed callback for every tenant must reject a different user and validate `returnTo` as same-origin. `OAuthStateCollection.consume(state)` is the single-use arbiter (revision-guarded delete; `false` when another caller already consumed it). Both columns are additive: consumers run `db:migrate`.\n\n## Gotchas\n\n- **Tokens not encrypted**: OAuth tokens stored as plaintext — TODO for smrt-secrets integration\n- **No auto-publishing**: `scheduledAt` is metadata only — app must implement job runner to trigger publishing\n- **Analytics manual**: `analytics` field must be updated by platform sync, not auto-populated\n- **Platform enum hardcoded**: youtube/threads/x/bluesky — extending requires code changes\n- **OAuthState TTL**: 10 minutes, app must clean up expired states\n- **Optional tenancy** on all models\n- **Relationship targets**: `SocialPost.socialAccountId` and\n `SocialPostAnalyticsSnapshot.socialPostId` are same-package `@foreignKey`\n columns; `SocialPost.videoContentId` is a `@crossPackageRef` to\n `@happyvertical/smrt-video:VideoContent` (no value import back into this\n package). They previously used `@foreignKey(() => X)`, whose target resolved\n to `''`, so none of them carried a relationship edge (#2379).\n"
2222
2274
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-social",
3
- "version": "0.54.2",
3
+ "version": "0.54.4",
4
4
  "type": "module",
5
5
  "smrtRawPrimitives": "strict",
6
6
  "description": "Social media account management for multi-platform publishing in the SMRT ecosystem",
@@ -39,19 +39,19 @@
39
39
  "AGENTS.md"
40
40
  ],
41
41
  "dependencies": {
42
- "@happyvertical/smrt-config": "0.54.2",
43
- "@happyvertical/smrt-content": "0.54.2",
44
- "@happyvertical/smrt-core": "0.54.2",
45
- "@happyvertical/smrt-secrets": "0.54.2",
46
- "@happyvertical/smrt-tenancy": "0.54.2",
47
- "@happyvertical/smrt-ui": "0.54.2",
48
- "@happyvertical/smrt-video": "0.54.2",
49
- "@happyvertical/utils": "^0.100.3"
42
+ "@happyvertical/smrt-config": "0.54.4",
43
+ "@happyvertical/smrt-content": "0.54.4",
44
+ "@happyvertical/smrt-core": "0.54.4",
45
+ "@happyvertical/smrt-secrets": "0.54.4",
46
+ "@happyvertical/smrt-tenancy": "0.54.4",
47
+ "@happyvertical/smrt-ui": "0.54.4",
48
+ "@happyvertical/smrt-video": "0.54.4",
49
+ "@happyvertical/utils": "^0.102.1"
50
50
  },
51
51
  "devDependencies": {
52
- "@happyvertical/logger": "^0.100.3",
53
- "@happyvertical/smrt-vitest": "0.54.2",
54
- "@happyvertical/sql": "^0.100.3",
52
+ "@happyvertical/logger": "^0.102.1",
53
+ "@happyvertical/smrt-vitest": "0.54.4",
54
+ "@happyvertical/sql": "^0.102.1",
55
55
  "@sveltejs/package": "^2.5.8",
56
56
  "@types/node": "24.13.6",
57
57
  "svelte": "^5.57.1",