@uniforge/platform-shopify 0.1.0-alpha.2

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 (61) hide show
  1. package/dist/auth/index.d.cts +246 -0
  2. package/dist/auth/index.d.ts +246 -0
  3. package/dist/auth/index.js +623 -0
  4. package/dist/auth/index.js.map +1 -0
  5. package/dist/auth/index.mjs +586 -0
  6. package/dist/auth/index.mjs.map +1 -0
  7. package/dist/billing/index.d.cts +58 -0
  8. package/dist/billing/index.d.ts +58 -0
  9. package/dist/billing/index.js +226 -0
  10. package/dist/billing/index.js.map +1 -0
  11. package/dist/billing/index.mjs +196 -0
  12. package/dist/billing/index.mjs.map +1 -0
  13. package/dist/graphql/index.d.cts +17 -0
  14. package/dist/graphql/index.d.ts +17 -0
  15. package/dist/graphql/index.js +67 -0
  16. package/dist/graphql/index.js.map +1 -0
  17. package/dist/graphql/index.mjs +40 -0
  18. package/dist/graphql/index.mjs.map +1 -0
  19. package/dist/index.d.cts +16 -0
  20. package/dist/index.d.ts +16 -0
  21. package/dist/index.js +37 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/index.mjs +11 -0
  24. package/dist/index.mjs.map +1 -0
  25. package/dist/multi-store/index.d.cts +28 -0
  26. package/dist/multi-store/index.d.ts +28 -0
  27. package/dist/multi-store/index.js +181 -0
  28. package/dist/multi-store/index.js.map +1 -0
  29. package/dist/multi-store/index.mjs +152 -0
  30. package/dist/multi-store/index.mjs.map +1 -0
  31. package/dist/performance/index.d.cts +22 -0
  32. package/dist/performance/index.d.ts +22 -0
  33. package/dist/performance/index.js +64 -0
  34. package/dist/performance/index.js.map +1 -0
  35. package/dist/performance/index.mjs +35 -0
  36. package/dist/performance/index.mjs.map +1 -0
  37. package/dist/platform/index.d.cts +16 -0
  38. package/dist/platform/index.d.ts +16 -0
  39. package/dist/platform/index.js +150 -0
  40. package/dist/platform/index.js.map +1 -0
  41. package/dist/platform/index.mjs +121 -0
  42. package/dist/platform/index.mjs.map +1 -0
  43. package/dist/rbac/index.d.cts +38 -0
  44. package/dist/rbac/index.d.ts +38 -0
  45. package/dist/rbac/index.js +56 -0
  46. package/dist/rbac/index.js.map +1 -0
  47. package/dist/rbac/index.mjs +29 -0
  48. package/dist/rbac/index.mjs.map +1 -0
  49. package/dist/security/index.d.cts +26 -0
  50. package/dist/security/index.d.ts +26 -0
  51. package/dist/security/index.js +102 -0
  52. package/dist/security/index.js.map +1 -0
  53. package/dist/security/index.mjs +69 -0
  54. package/dist/security/index.mjs.map +1 -0
  55. package/dist/webhooks/index.d.cts +36 -0
  56. package/dist/webhooks/index.d.ts +36 -0
  57. package/dist/webhooks/index.js +147 -0
  58. package/dist/webhooks/index.js.map +1 -0
  59. package/dist/webhooks/index.mjs +118 -0
  60. package/dist/webhooks/index.mjs.map +1 -0
  61. package/package.json +95 -0
@@ -0,0 +1,246 @@
1
+ import { AuthRequest, AuthConfig, Session } from '@uniforge/platform-core/auth';
2
+
3
+ /**
4
+ * Session token extraction and Shopify token exchange.
5
+ *
6
+ * Extracts session tokens from embedded app requests and exchanges
7
+ * them for access tokens via the Shopify API.
8
+ */
9
+
10
+ /**
11
+ * Extract the session token from the Authorization header.
12
+ * Returns null if the header is missing or malformed.
13
+ */
14
+ declare function extractSessionToken(request: AuthRequest): string | null;
15
+ /**
16
+ * Extract the shop domain from the request.
17
+ * Checks query parameter first, then x-shopify-shop-domain header.
18
+ * Validates the domain format before returning.
19
+ */
20
+ declare function extractShop(request: AuthRequest): string | null;
21
+ /**
22
+ * Minimal interface for the Shopify token exchange function.
23
+ * Allows mocking in tests without depending on the full Shopify API.
24
+ */
25
+ interface ShopifyTokenExchangeFn {
26
+ (params: {
27
+ sessionToken: string;
28
+ shop: string;
29
+ requestedTokenType: string;
30
+ }): Promise<{
31
+ session: {
32
+ id: string;
33
+ shop: string;
34
+ state: string;
35
+ isOnline: boolean;
36
+ scope?: string;
37
+ expires?: Date;
38
+ accessToken?: string;
39
+ refreshToken?: string;
40
+ onlineAccessInfo?: any;
41
+ };
42
+ }>;
43
+ }
44
+ /** Input parameters for performing a Shopify token exchange. */
45
+ interface PerformTokenExchangeInput {
46
+ config: AuthConfig;
47
+ sessionToken: string;
48
+ shop: string;
49
+ tokenType: 'online' | 'offline';
50
+ /** Optional: provide a custom token exchange function (for testing). */
51
+ tokenExchangeFn?: ShopifyTokenExchangeFn;
52
+ }
53
+ /**
54
+ * Create a Shopify API client and return its tokenExchange function.
55
+ */
56
+ declare function createShopifyTokenExchangeFn(config: AuthConfig): Promise<ShopifyTokenExchangeFn>;
57
+ /**
58
+ * Exchange a session token for an access token via Shopify API.
59
+ *
60
+ * Maps the Shopify Session result to UniForge's Session type.
61
+ * Emits auth events on success/failure.
62
+ */
63
+ declare function performTokenExchange(input: PerformTokenExchangeInput): Promise<Session>;
64
+
65
+ /**
66
+ * Shopify session manager for embedded app authentication.
67
+ *
68
+ * Manages the lifecycle of Shopify sessions: checks storage for
69
+ * existing valid sessions, performs token exchange when needed,
70
+ * refreshes expiring tokens, and stores results via encrypted
71
+ * session storage.
72
+ */
73
+
74
+ /**
75
+ * Injectable function for refreshing a Shopify access token.
76
+ * In production, wraps `shopify.auth.refreshToken()`.
77
+ */
78
+ interface ShopifyTokenRefreshFn {
79
+ (params: {
80
+ session: {
81
+ accessToken?: string;
82
+ refreshToken?: string;
83
+ shop: string;
84
+ };
85
+ }): Promise<{
86
+ session: {
87
+ id: string;
88
+ shop: string;
89
+ state: string;
90
+ isOnline: boolean;
91
+ scope?: string;
92
+ expires?: Date;
93
+ accessToken?: string;
94
+ refreshToken?: string;
95
+ };
96
+ }>;
97
+ }
98
+ /** Options for configuring the ShopifySessionManager with test doubles. */
99
+ interface ShopifySessionManagerOptions {
100
+ /** Optional: provide a custom token exchange function (for testing). */
101
+ tokenExchangeFn?: ShopifyTokenExchangeFn;
102
+ /** Optional: provide a custom token refresh function (for testing). */
103
+ tokenRefreshFn?: ShopifyTokenRefreshFn;
104
+ }
105
+ declare class ShopifySessionManager {
106
+ private readonly config;
107
+ private readonly encryptedStorage;
108
+ private readonly tokenExchangeFn?;
109
+ private readonly tokenRefreshFn?;
110
+ constructor(config: AuthConfig, options?: ShopifySessionManagerOptions);
111
+ /**
112
+ * Get an existing valid session or create one via token exchange.
113
+ *
114
+ * For embedded apps, this:
115
+ * 1. Extracts the session token and shop from the request
116
+ * 2. Checks storage for an existing non-expired session
117
+ * 3. If no valid session, performs token exchange with Shopify
118
+ * 4. Stores the new session (encrypted) and returns it
119
+ */
120
+ getOrCreateSession(request: AuthRequest): Promise<Session>;
121
+ /**
122
+ * Get an existing valid online session or create one via token exchange.
123
+ */
124
+ getOrCreateOnlineSession(request: AuthRequest): Promise<Session>;
125
+ /**
126
+ * Refresh an expiring session token.
127
+ *
128
+ * Retries up to `config.tokenRefresh.maxRetries` on failure.
129
+ * On success, stores the refreshed session (encrypted) and emits
130
+ * a `token_refreshed` event. On final failure, emits
131
+ * `token_refresh_failed` and throws.
132
+ */
133
+ refreshSessionToken(session: Session): Promise<Session>;
134
+ /**
135
+ * Delete all sessions for a shop (e.g., on app uninstall).
136
+ *
137
+ * Finds all sessions via `findSessionsByShop`, deletes them,
138
+ * and emits `session_deleted` events.
139
+ */
140
+ cleanupShopSessions(shop: string): Promise<void>;
141
+ private isExpired;
142
+ private mapRefreshedSession;
143
+ private createDefaultRefreshFn;
144
+ }
145
+
146
+ /**
147
+ * HMAC validation for Shopify OAuth callbacks.
148
+ *
149
+ * Validates the HMAC signature on OAuth callback query parameters
150
+ * using timing-safe comparison to prevent timing attacks.
151
+ */
152
+ /**
153
+ * Validate the HMAC signature on OAuth callback query parameters.
154
+ *
155
+ * Per Shopify's spec: remove the `hmac` parameter, sort remaining
156
+ * params alphabetically, join as `key=value` with `&`, and compute
157
+ * HMAC-SHA256 with the app's API secret key. Compare using
158
+ * timing-safe equality.
159
+ */
160
+ declare function validateOAuthHmac(query: Record<string, string>, secret: string): boolean;
161
+
162
+ /**
163
+ * OAuth authorization code flow for non-embedded Shopify apps.
164
+ *
165
+ * Implements the begin (redirect to Shopify) and callback
166
+ * (exchange code for access token) phases of the OAuth flow.
167
+ */
168
+
169
+ /**
170
+ * Injectable function for initiating OAuth.
171
+ * In production, wraps `shopify.auth.begin()`.
172
+ */
173
+ interface ShopifyOAuthBeginFn {
174
+ (params: {
175
+ shop: string;
176
+ callbackPath: string;
177
+ isOnline: boolean;
178
+ }): Promise<{
179
+ redirectUrl: string;
180
+ }>;
181
+ }
182
+ /**
183
+ * Injectable function for handling the OAuth callback.
184
+ * In production, wraps `shopify.auth.callback()`.
185
+ */
186
+ interface ShopifyOAuthCallbackFn {
187
+ (params: {
188
+ query: Record<string, string>;
189
+ }): Promise<{
190
+ session: {
191
+ id: string;
192
+ shop: string;
193
+ state: string;
194
+ isOnline: boolean;
195
+ scope?: string;
196
+ expires?: Date;
197
+ accessToken?: string;
198
+ refreshToken?: string;
199
+ };
200
+ }>;
201
+ }
202
+ /** Input parameters for beginning the OAuth authorization flow. */
203
+ interface BeginOAuthInput {
204
+ config: AuthConfig;
205
+ request: AuthRequest;
206
+ callbackPath: string;
207
+ isOnline?: boolean;
208
+ beginFn?: ShopifyOAuthBeginFn;
209
+ }
210
+ /** Result from beginning OAuth containing the authorization redirect URL. */
211
+ interface BeginOAuthResult {
212
+ redirectUrl: string;
213
+ }
214
+ /** Input parameters for handling the OAuth callback from Shopify. */
215
+ interface HandleOAuthCallbackInput {
216
+ config: AuthConfig;
217
+ request: AuthRequest;
218
+ callbackFn?: ShopifyOAuthCallbackFn;
219
+ }
220
+ /** Result from OAuth callback — either success with session or failure with error details. */
221
+ type OAuthCallbackResult = {
222
+ success: true;
223
+ session: Session;
224
+ redirectUrl: string;
225
+ } | {
226
+ success: false;
227
+ error: {
228
+ code: string;
229
+ message: string;
230
+ statusCode: number;
231
+ };
232
+ };
233
+ /**
234
+ * Initiate OAuth by redirecting the merchant to Shopify's authorization page.
235
+ */
236
+ declare function beginOAuth(input: BeginOAuthInput): Promise<BeginOAuthResult>;
237
+ /**
238
+ * Handle the OAuth callback from Shopify.
239
+ *
240
+ * Validates HMAC, checks for authorization denial, exchanges the code
241
+ * for an access token, validates scopes, stores the session encrypted,
242
+ * and emits events.
243
+ */
244
+ declare function handleOAuthCallback(input: HandleOAuthCallbackInput): Promise<OAuthCallbackResult>;
245
+
246
+ export { type BeginOAuthInput, type BeginOAuthResult, type HandleOAuthCallbackInput, type OAuthCallbackResult, type PerformTokenExchangeInput, type ShopifyOAuthBeginFn, type ShopifyOAuthCallbackFn, ShopifySessionManager, type ShopifySessionManagerOptions, type ShopifyTokenExchangeFn, type ShopifyTokenRefreshFn, beginOAuth, createShopifyTokenExchangeFn, extractSessionToken, extractShop, handleOAuthCallback, performTokenExchange, validateOAuthHmac };
@@ -0,0 +1,246 @@
1
+ import { AuthRequest, AuthConfig, Session } from '@uniforge/platform-core/auth';
2
+
3
+ /**
4
+ * Session token extraction and Shopify token exchange.
5
+ *
6
+ * Extracts session tokens from embedded app requests and exchanges
7
+ * them for access tokens via the Shopify API.
8
+ */
9
+
10
+ /**
11
+ * Extract the session token from the Authorization header.
12
+ * Returns null if the header is missing or malformed.
13
+ */
14
+ declare function extractSessionToken(request: AuthRequest): string | null;
15
+ /**
16
+ * Extract the shop domain from the request.
17
+ * Checks query parameter first, then x-shopify-shop-domain header.
18
+ * Validates the domain format before returning.
19
+ */
20
+ declare function extractShop(request: AuthRequest): string | null;
21
+ /**
22
+ * Minimal interface for the Shopify token exchange function.
23
+ * Allows mocking in tests without depending on the full Shopify API.
24
+ */
25
+ interface ShopifyTokenExchangeFn {
26
+ (params: {
27
+ sessionToken: string;
28
+ shop: string;
29
+ requestedTokenType: string;
30
+ }): Promise<{
31
+ session: {
32
+ id: string;
33
+ shop: string;
34
+ state: string;
35
+ isOnline: boolean;
36
+ scope?: string;
37
+ expires?: Date;
38
+ accessToken?: string;
39
+ refreshToken?: string;
40
+ onlineAccessInfo?: any;
41
+ };
42
+ }>;
43
+ }
44
+ /** Input parameters for performing a Shopify token exchange. */
45
+ interface PerformTokenExchangeInput {
46
+ config: AuthConfig;
47
+ sessionToken: string;
48
+ shop: string;
49
+ tokenType: 'online' | 'offline';
50
+ /** Optional: provide a custom token exchange function (for testing). */
51
+ tokenExchangeFn?: ShopifyTokenExchangeFn;
52
+ }
53
+ /**
54
+ * Create a Shopify API client and return its tokenExchange function.
55
+ */
56
+ declare function createShopifyTokenExchangeFn(config: AuthConfig): Promise<ShopifyTokenExchangeFn>;
57
+ /**
58
+ * Exchange a session token for an access token via Shopify API.
59
+ *
60
+ * Maps the Shopify Session result to UniForge's Session type.
61
+ * Emits auth events on success/failure.
62
+ */
63
+ declare function performTokenExchange(input: PerformTokenExchangeInput): Promise<Session>;
64
+
65
+ /**
66
+ * Shopify session manager for embedded app authentication.
67
+ *
68
+ * Manages the lifecycle of Shopify sessions: checks storage for
69
+ * existing valid sessions, performs token exchange when needed,
70
+ * refreshes expiring tokens, and stores results via encrypted
71
+ * session storage.
72
+ */
73
+
74
+ /**
75
+ * Injectable function for refreshing a Shopify access token.
76
+ * In production, wraps `shopify.auth.refreshToken()`.
77
+ */
78
+ interface ShopifyTokenRefreshFn {
79
+ (params: {
80
+ session: {
81
+ accessToken?: string;
82
+ refreshToken?: string;
83
+ shop: string;
84
+ };
85
+ }): Promise<{
86
+ session: {
87
+ id: string;
88
+ shop: string;
89
+ state: string;
90
+ isOnline: boolean;
91
+ scope?: string;
92
+ expires?: Date;
93
+ accessToken?: string;
94
+ refreshToken?: string;
95
+ };
96
+ }>;
97
+ }
98
+ /** Options for configuring the ShopifySessionManager with test doubles. */
99
+ interface ShopifySessionManagerOptions {
100
+ /** Optional: provide a custom token exchange function (for testing). */
101
+ tokenExchangeFn?: ShopifyTokenExchangeFn;
102
+ /** Optional: provide a custom token refresh function (for testing). */
103
+ tokenRefreshFn?: ShopifyTokenRefreshFn;
104
+ }
105
+ declare class ShopifySessionManager {
106
+ private readonly config;
107
+ private readonly encryptedStorage;
108
+ private readonly tokenExchangeFn?;
109
+ private readonly tokenRefreshFn?;
110
+ constructor(config: AuthConfig, options?: ShopifySessionManagerOptions);
111
+ /**
112
+ * Get an existing valid session or create one via token exchange.
113
+ *
114
+ * For embedded apps, this:
115
+ * 1. Extracts the session token and shop from the request
116
+ * 2. Checks storage for an existing non-expired session
117
+ * 3. If no valid session, performs token exchange with Shopify
118
+ * 4. Stores the new session (encrypted) and returns it
119
+ */
120
+ getOrCreateSession(request: AuthRequest): Promise<Session>;
121
+ /**
122
+ * Get an existing valid online session or create one via token exchange.
123
+ */
124
+ getOrCreateOnlineSession(request: AuthRequest): Promise<Session>;
125
+ /**
126
+ * Refresh an expiring session token.
127
+ *
128
+ * Retries up to `config.tokenRefresh.maxRetries` on failure.
129
+ * On success, stores the refreshed session (encrypted) and emits
130
+ * a `token_refreshed` event. On final failure, emits
131
+ * `token_refresh_failed` and throws.
132
+ */
133
+ refreshSessionToken(session: Session): Promise<Session>;
134
+ /**
135
+ * Delete all sessions for a shop (e.g., on app uninstall).
136
+ *
137
+ * Finds all sessions via `findSessionsByShop`, deletes them,
138
+ * and emits `session_deleted` events.
139
+ */
140
+ cleanupShopSessions(shop: string): Promise<void>;
141
+ private isExpired;
142
+ private mapRefreshedSession;
143
+ private createDefaultRefreshFn;
144
+ }
145
+
146
+ /**
147
+ * HMAC validation for Shopify OAuth callbacks.
148
+ *
149
+ * Validates the HMAC signature on OAuth callback query parameters
150
+ * using timing-safe comparison to prevent timing attacks.
151
+ */
152
+ /**
153
+ * Validate the HMAC signature on OAuth callback query parameters.
154
+ *
155
+ * Per Shopify's spec: remove the `hmac` parameter, sort remaining
156
+ * params alphabetically, join as `key=value` with `&`, and compute
157
+ * HMAC-SHA256 with the app's API secret key. Compare using
158
+ * timing-safe equality.
159
+ */
160
+ declare function validateOAuthHmac(query: Record<string, string>, secret: string): boolean;
161
+
162
+ /**
163
+ * OAuth authorization code flow for non-embedded Shopify apps.
164
+ *
165
+ * Implements the begin (redirect to Shopify) and callback
166
+ * (exchange code for access token) phases of the OAuth flow.
167
+ */
168
+
169
+ /**
170
+ * Injectable function for initiating OAuth.
171
+ * In production, wraps `shopify.auth.begin()`.
172
+ */
173
+ interface ShopifyOAuthBeginFn {
174
+ (params: {
175
+ shop: string;
176
+ callbackPath: string;
177
+ isOnline: boolean;
178
+ }): Promise<{
179
+ redirectUrl: string;
180
+ }>;
181
+ }
182
+ /**
183
+ * Injectable function for handling the OAuth callback.
184
+ * In production, wraps `shopify.auth.callback()`.
185
+ */
186
+ interface ShopifyOAuthCallbackFn {
187
+ (params: {
188
+ query: Record<string, string>;
189
+ }): Promise<{
190
+ session: {
191
+ id: string;
192
+ shop: string;
193
+ state: string;
194
+ isOnline: boolean;
195
+ scope?: string;
196
+ expires?: Date;
197
+ accessToken?: string;
198
+ refreshToken?: string;
199
+ };
200
+ }>;
201
+ }
202
+ /** Input parameters for beginning the OAuth authorization flow. */
203
+ interface BeginOAuthInput {
204
+ config: AuthConfig;
205
+ request: AuthRequest;
206
+ callbackPath: string;
207
+ isOnline?: boolean;
208
+ beginFn?: ShopifyOAuthBeginFn;
209
+ }
210
+ /** Result from beginning OAuth containing the authorization redirect URL. */
211
+ interface BeginOAuthResult {
212
+ redirectUrl: string;
213
+ }
214
+ /** Input parameters for handling the OAuth callback from Shopify. */
215
+ interface HandleOAuthCallbackInput {
216
+ config: AuthConfig;
217
+ request: AuthRequest;
218
+ callbackFn?: ShopifyOAuthCallbackFn;
219
+ }
220
+ /** Result from OAuth callback — either success with session or failure with error details. */
221
+ type OAuthCallbackResult = {
222
+ success: true;
223
+ session: Session;
224
+ redirectUrl: string;
225
+ } | {
226
+ success: false;
227
+ error: {
228
+ code: string;
229
+ message: string;
230
+ statusCode: number;
231
+ };
232
+ };
233
+ /**
234
+ * Initiate OAuth by redirecting the merchant to Shopify's authorization page.
235
+ */
236
+ declare function beginOAuth(input: BeginOAuthInput): Promise<BeginOAuthResult>;
237
+ /**
238
+ * Handle the OAuth callback from Shopify.
239
+ *
240
+ * Validates HMAC, checks for authorization denial, exchanges the code
241
+ * for an access token, validates scopes, stores the session encrypted,
242
+ * and emits events.
243
+ */
244
+ declare function handleOAuthCallback(input: HandleOAuthCallbackInput): Promise<OAuthCallbackResult>;
245
+
246
+ export { type BeginOAuthInput, type BeginOAuthResult, type HandleOAuthCallbackInput, type OAuthCallbackResult, type PerformTokenExchangeInput, type ShopifyOAuthBeginFn, type ShopifyOAuthCallbackFn, ShopifySessionManager, type ShopifySessionManagerOptions, type ShopifyTokenExchangeFn, type ShopifyTokenRefreshFn, beginOAuth, createShopifyTokenExchangeFn, extractSessionToken, extractShop, handleOAuthCallback, performTokenExchange, validateOAuthHmac };