@mosano-product-framework/sdk 0.2.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.
Files changed (74) hide show
  1. package/README.md +827 -0
  2. package/README.react.md +348 -0
  3. package/dist/auth/claims-types.d.ts +89 -0
  4. package/dist/auth/claims.d.ts +125 -0
  5. package/dist/auth/cross-tab.d.ts +114 -0
  6. package/dist/auth/errors.d.ts +40 -0
  7. package/dist/auth/index.d.ts +18 -0
  8. package/dist/auth/index.js +5 -0
  9. package/dist/auth/index.js.map +1 -0
  10. package/dist/auth/oauth-state.d.ts +93 -0
  11. package/dist/auth/session-manager.d.ts +253 -0
  12. package/dist/auth/storage.d.ts +36 -0
  13. package/dist/auth/tenant-directory.d.ts +59 -0
  14. package/dist/auth/tenant-selection.d.ts +92 -0
  15. package/dist/chunk-7WAV52EO.js +621 -0
  16. package/dist/chunk-7WAV52EO.js.map +1 -0
  17. package/dist/chunk-AJWM5MDZ.js +410 -0
  18. package/dist/chunk-AJWM5MDZ.js.map +1 -0
  19. package/dist/chunk-EXPYHNPV.js +212 -0
  20. package/dist/chunk-EXPYHNPV.js.map +1 -0
  21. package/dist/chunk-GPWGOYCA.js +85 -0
  22. package/dist/chunk-GPWGOYCA.js.map +1 -0
  23. package/dist/chunk-GQJ3QQPH.js +339 -0
  24. package/dist/chunk-GQJ3QQPH.js.map +1 -0
  25. package/dist/chunk-K2ELAI2X.js +64 -0
  26. package/dist/chunk-K2ELAI2X.js.map +1 -0
  27. package/dist/chunk-LRM6JJ63.js +616 -0
  28. package/dist/chunk-LRM6JJ63.js.map +1 -0
  29. package/dist/chunk-XAXFIIRT.js +959 -0
  30. package/dist/chunk-XAXFIIRT.js.map +1 -0
  31. package/dist/client/core/client-factory.d.ts +61 -0
  32. package/dist/client/core/client.d.ts +144 -0
  33. package/dist/client/core/errors.d.ts +105 -0
  34. package/dist/client/core/index.d.ts +9 -0
  35. package/dist/client/core/middleware.d.ts +67 -0
  36. package/dist/client/core/types.d.ts +99 -0
  37. package/dist/client/graphql/client.d.ts +66 -0
  38. package/dist/client/graphql/factory.d.ts +84 -0
  39. package/dist/client/graphql/operation.d.ts +24 -0
  40. package/dist/client/graphql/types.d.ts +60 -0
  41. package/dist/client/graphql/ws-client.d.ts +116 -0
  42. package/dist/client/index.d.ts +17 -0
  43. package/dist/client/index.js +227 -0
  44. package/dist/client/index.js.map +1 -0
  45. package/dist/client/middlewares/admin-auth.d.ts +90 -0
  46. package/dist/client/middlewares/auth.d.ts +81 -0
  47. package/dist/client/middlewares/index.d.ts +12 -0
  48. package/dist/client/middlewares/logging.d.ts +102 -0
  49. package/dist/client/middlewares/retry.d.ts +138 -0
  50. package/dist/client/middlewares/tenant.d.ts +60 -0
  51. package/dist/client/middlewares/turnstile.d.ts +41 -0
  52. package/dist/client/peer-free.d.ts +25 -0
  53. package/dist/client/utils/url.d.ts +19 -0
  54. package/dist/identity/index.d.ts +85 -0
  55. package/dist/identity/index.js +6 -0
  56. package/dist/identity/index.js.map +1 -0
  57. package/dist/identity/types.d.ts +690 -0
  58. package/dist/identity/v0.d.ts +594 -0
  59. package/dist/index.d.ts +50 -0
  60. package/dist/index.js +24 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/react/context.d.ts +47 -0
  63. package/dist/react/hooks.d.ts +120 -0
  64. package/dist/react/index.d.ts +19 -0
  65. package/dist/react/index.js +308 -0
  66. package/dist/react/index.js.map +1 -0
  67. package/dist/react/provider.d.ts +68 -0
  68. package/dist/react/store.d.ts +85 -0
  69. package/dist/storage/index.d.ts +31 -0
  70. package/dist/storage/index.js +5 -0
  71. package/dist/storage/index.js.map +1 -0
  72. package/dist/storage/types.d.ts +107 -0
  73. package/dist/storage/v0.d.ts +120 -0
  74. package/package.json +99 -0
@@ -0,0 +1,212 @@
1
+ import { MPFClient, attachStandardMiddlewares } from './chunk-LRM6JJ63.js';
2
+ import { __export } from './chunk-AJWM5MDZ.js';
3
+
4
+ // src/storage/index.ts
5
+ var storage_exports = {};
6
+ __export(storage_exports, {
7
+ StorageClientV0: () => StorageClientV0,
8
+ createStorageClient: () => createStorageClient
9
+ });
10
+
11
+ // src/storage/v0.ts
12
+ var StorageClientV0 = class extends MPFClient {
13
+ constructor(options) {
14
+ super({
15
+ baseUrl: `${options.baseUrl}/v0`,
16
+ headers: options.headers,
17
+ middlewares: options.middlewares
18
+ });
19
+ }
20
+ // ============================================================================
21
+ // Signed URL Upload
22
+ // ============================================================================
23
+ /**
24
+ * Get a signed URL for uploading a single file
25
+ *
26
+ * @param data - File name and MIME type
27
+ * @param options - Optional request options
28
+ * @returns Signed URL and file ID
29
+ */
30
+ async getUploadSignedUrl(data, options) {
31
+ return this.post("/files/upload-url", data, options);
32
+ }
33
+ /**
34
+ * Get signed URLs for uploading multiple files
35
+ *
36
+ * @param data - Array of file descriptors
37
+ * @param options - Optional request options
38
+ * @returns Array of signed URLs and file IDs
39
+ */
40
+ async getBatchUploadSignedUrls(data, options) {
41
+ return this.post("/files/batch-upload-urls", data, options);
42
+ }
43
+ // ============================================================================
44
+ // Direct Upload (multipart)
45
+ // ============================================================================
46
+ /**
47
+ * Upload a single file via multipart form data
48
+ *
49
+ * @param file - The file or blob to upload
50
+ * @param options - Optional request options
51
+ * @returns File metadata
52
+ */
53
+ async uploadFile(file, options) {
54
+ const form = new FormData();
55
+ form.append("file", file);
56
+ return this.multipartRequest("POST", "/files/upload", form, options);
57
+ }
58
+ /**
59
+ * Upload multiple files via multipart form data
60
+ *
61
+ * @param files - Array of files or blobs to upload
62
+ * @param options - Optional request options
63
+ * @returns Array of file metadata
64
+ */
65
+ async batchUploadFiles(files, options) {
66
+ const form = new FormData();
67
+ for (const file of files) {
68
+ form.append("files", file);
69
+ }
70
+ return this.multipartRequest("POST", "/files/batch-upload", form, options);
71
+ }
72
+ // ============================================================================
73
+ // Download
74
+ // ============================================================================
75
+ /**
76
+ * Get a signed URL for downloading a file
77
+ *
78
+ * @param fileId - The file ID
79
+ * @param options - Optional request options
80
+ * @returns Signed download URL and optional metadata
81
+ */
82
+ async getDownloadSignedUrl(fileId, options) {
83
+ return this.get(`/files/${fileId}/download-url`, options);
84
+ }
85
+ /**
86
+ * Get signed URLs for downloading multiple files
87
+ *
88
+ * @param data - Array of file IDs
89
+ * @param options - Optional request options
90
+ * @returns Array of signed download URLs
91
+ */
92
+ async getBatchDownloadSignedUrls(data, options) {
93
+ return this.post("/files/batch-download-urls", data, options);
94
+ }
95
+ // ============================================================================
96
+ // Confirm
97
+ // ============================================================================
98
+ /**
99
+ * Confirm a single file upload
100
+ *
101
+ * @param fileId - The file ID to confirm
102
+ * @param options - Optional request options
103
+ * @returns Updated file metadata
104
+ */
105
+ async confirmUpload(fileId, options) {
106
+ return this.post(`/files/${fileId}/confirm`, void 0, options);
107
+ }
108
+ /**
109
+ * Confirm multiple file uploads
110
+ *
111
+ * @param data - Array of file IDs to confirm
112
+ * @param options - Optional request options
113
+ * @returns Array of updated file metadata
114
+ */
115
+ async batchConfirmUploads(data, options) {
116
+ return this.post("/files/batch-confirm", data, options);
117
+ }
118
+ // ============================================================================
119
+ // Delete
120
+ // ============================================================================
121
+ /**
122
+ * Delete a single file
123
+ *
124
+ * @param fileId - The file ID to delete
125
+ * @param options - Optional request options
126
+ * @returns Deleted file metadata
127
+ */
128
+ async deleteFile(fileId, options) {
129
+ return this.delete(`/files/${fileId}`, void 0, options);
130
+ }
131
+ /**
132
+ * Delete multiple files
133
+ *
134
+ * @param data - Array of file IDs to delete
135
+ * @param options - Optional request options
136
+ * @returns Array of deleted file metadata
137
+ */
138
+ async batchDeleteFiles(data, options) {
139
+ return this.post("/files/batch-delete", data, options);
140
+ }
141
+ // ============================================================================
142
+ // Replace (multipart)
143
+ // ============================================================================
144
+ /**
145
+ * Replace a single file via multipart form data
146
+ *
147
+ * @param fileId - The file ID to replace
148
+ * @param file - The new file or blob
149
+ * @param options - Optional request options
150
+ * @returns Updated file metadata
151
+ */
152
+ async replaceFile(fileId, file, options) {
153
+ const form = new FormData();
154
+ form.append("file", file);
155
+ return this.multipartRequest("PUT", `/files/${fileId}`, form, options);
156
+ }
157
+ /**
158
+ * Replace multiple files via multipart form data
159
+ *
160
+ * @param replacements - Array of file ID and new file pairs
161
+ * @param options - Optional request options
162
+ * @returns Array of updated file metadata
163
+ */
164
+ async batchReplaceFiles(replacements, options) {
165
+ const form = new FormData();
166
+ for (const entry of replacements) {
167
+ form.append("file_ids", entry.fileId);
168
+ form.append("files", entry.file);
169
+ }
170
+ return this.multipartRequest("POST", "/files/batch-replace", form, options);
171
+ }
172
+ // ============================================================================
173
+ // Private helpers
174
+ // ============================================================================
175
+ /**
176
+ * Execute a multipart/form-data request.
177
+ *
178
+ * Delegates to `MPFClient.request()` so multipart uploads get the same
179
+ * middleware chain and retry loop as every other request. `request()` drops
180
+ * the default JSON `Content-Type` for a `FormData` body so that fetch sets
181
+ * `multipart/form-data` with the correct boundary string.
182
+ *
183
+ * `FormData` is re-readable, so a failed upload is safely retryable.
184
+ */
185
+ async multipartRequest(method, path, form, options) {
186
+ return this.request(
187
+ path,
188
+ {
189
+ method,
190
+ body: form,
191
+ headers: options?.headers,
192
+ signal: options?.signal
193
+ },
194
+ options?.params,
195
+ options
196
+ );
197
+ }
198
+ };
199
+
200
+ // src/storage/index.ts
201
+ function createStorageClient(options) {
202
+ const client = new StorageClientV0(options);
203
+ attachStandardMiddlewares(client, {
204
+ tokenProvider: options.tokenProvider,
205
+ tenantProvider: options.tenantProvider
206
+ });
207
+ return client;
208
+ }
209
+
210
+ export { StorageClientV0, createStorageClient, storage_exports };
211
+ //# sourceMappingURL=chunk-EXPYHNPV.js.map
212
+ //# sourceMappingURL=chunk-EXPYHNPV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/storage/index.ts","../src/storage/v0.ts"],"names":[],"mappings":"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","file":"chunk-EXPYHNPV.js","sourcesContent":["import { attachStandardMiddlewares } from '../client/core/client-factory.js';\nimport type { StorageClientOptions } from './types.js';\nimport { StorageClientV0 } from './v0.js';\n\n/**\n * Creates a Storage client for API v0\n *\n * @param options - Client configuration options\n * @returns StorageClientV0 instance\n *\n * @example\n * ```typescript\n * import { createStorageClient } from '@mosano-product-framework/sdk/storage';\n *\n * // Create authenticated storage client\n * const storage = createStorageClient({\n * baseUrl: 'https://api.example.com/storage',\n * tokenProvider: {\n * getAccessToken: () => localStorage.getItem('accessToken'),\n * onTokenExpired: () => { window.location.href = '/login'; },\n * },\n * });\n *\n * // Upload a file\n * const metadata = await storage.uploadFile(myFile);\n *\n * // Get a download URL\n * const { url } = await storage.getDownloadSignedUrl(metadata.id);\n * ```\n */\nexport function createStorageClient(options: StorageClientOptions): StorageClientV0 {\n const client = new StorageClientV0(options);\n\n attachStandardMiddlewares(client, {\n tokenProvider: options.tokenProvider,\n tenantProvider: options.tenantProvider,\n });\n\n return client;\n}\n\nexport { StorageClientV0 } from './v0.js';\nexport * from './types.js';\n","import { MPFClient } from '../client/core/client.js';\nimport type { RequestOptions } from '../client/core/types.js';\nimport type {\n StorageClientOptions,\n FileMetadataResponse,\n UploadSignedUrlResponse,\n DownloadSignedUrlResponse,\n GetUploadSignedUrlRequest,\n GetBatchUploadSignedUrlsRequest,\n GetBatchDownloadSignedUrlsRequest,\n BatchDeleteFilesRequest,\n BatchConfirmUploadsRequest,\n BatchReplaceFilesEntry,\n} from './types.js';\n\n/**\n * Storage API v0 client\n *\n * Provides methods for file upload, download, confirmation, deletion,\n * and replacement using both signed URLs and direct multipart uploads.\n */\nexport class StorageClientV0 extends MPFClient {\n constructor(options: StorageClientOptions) {\n super({\n baseUrl: `${options.baseUrl}/v0`,\n headers: options.headers,\n middlewares: options.middlewares,\n });\n }\n\n // ============================================================================\n // Signed URL Upload\n // ============================================================================\n\n /**\n * Get a signed URL for uploading a single file\n *\n * @param data - File name and MIME type\n * @param options - Optional request options\n * @returns Signed URL and file ID\n */\n async getUploadSignedUrl(\n data: GetUploadSignedUrlRequest,\n options?: RequestOptions,\n ): Promise<UploadSignedUrlResponse> {\n return this.post<UploadSignedUrlResponse>('/files/upload-url', data, options);\n }\n\n /**\n * Get signed URLs for uploading multiple files\n *\n * @param data - Array of file descriptors\n * @param options - Optional request options\n * @returns Array of signed URLs and file IDs\n */\n async getBatchUploadSignedUrls(\n data: GetBatchUploadSignedUrlsRequest,\n options?: RequestOptions,\n ): Promise<UploadSignedUrlResponse[]> {\n return this.post<UploadSignedUrlResponse[]>('/files/batch-upload-urls', data, options);\n }\n\n // ============================================================================\n // Direct Upload (multipart)\n // ============================================================================\n\n /**\n * Upload a single file via multipart form data\n *\n * @param file - The file or blob to upload\n * @param options - Optional request options\n * @returns File metadata\n */\n async uploadFile(\n file: Blob | File,\n options?: RequestOptions,\n ): Promise<FileMetadataResponse> {\n const form = new FormData();\n form.append('file', file);\n return this.multipartRequest<FileMetadataResponse>('POST', '/files/upload', form, options);\n }\n\n /**\n * Upload multiple files via multipart form data\n *\n * @param files - Array of files or blobs to upload\n * @param options - Optional request options\n * @returns Array of file metadata\n */\n async batchUploadFiles(\n files: (Blob | File)[],\n options?: RequestOptions,\n ): Promise<FileMetadataResponse[]> {\n const form = new FormData();\n for (const file of files) {\n form.append('files', file);\n }\n return this.multipartRequest<FileMetadataResponse[]>('POST', '/files/batch-upload', form, options);\n }\n\n // ============================================================================\n // Download\n // ============================================================================\n\n /**\n * Get a signed URL for downloading a file\n *\n * @param fileId - The file ID\n * @param options - Optional request options\n * @returns Signed download URL and optional metadata\n */\n async getDownloadSignedUrl(\n fileId: string,\n options?: RequestOptions,\n ): Promise<DownloadSignedUrlResponse> {\n return this.get<DownloadSignedUrlResponse>(`/files/${fileId}/download-url`, options);\n }\n\n /**\n * Get signed URLs for downloading multiple files\n *\n * @param data - Array of file IDs\n * @param options - Optional request options\n * @returns Array of signed download URLs\n */\n async getBatchDownloadSignedUrls(\n data: GetBatchDownloadSignedUrlsRequest,\n options?: RequestOptions,\n ): Promise<DownloadSignedUrlResponse[]> {\n return this.post<DownloadSignedUrlResponse[]>('/files/batch-download-urls', data, options);\n }\n\n // ============================================================================\n // Confirm\n // ============================================================================\n\n /**\n * Confirm a single file upload\n *\n * @param fileId - The file ID to confirm\n * @param options - Optional request options\n * @returns Updated file metadata\n */\n async confirmUpload(\n fileId: string,\n options?: RequestOptions,\n ): Promise<FileMetadataResponse> {\n return this.post<FileMetadataResponse>(`/files/${fileId}/confirm`, undefined, options);\n }\n\n /**\n * Confirm multiple file uploads\n *\n * @param data - Array of file IDs to confirm\n * @param options - Optional request options\n * @returns Array of updated file metadata\n */\n async batchConfirmUploads(\n data: BatchConfirmUploadsRequest,\n options?: RequestOptions,\n ): Promise<FileMetadataResponse[]> {\n return this.post<FileMetadataResponse[]>('/files/batch-confirm', data, options);\n }\n\n // ============================================================================\n // Delete\n // ============================================================================\n\n /**\n * Delete a single file\n *\n * @param fileId - The file ID to delete\n * @param options - Optional request options\n * @returns Deleted file metadata\n */\n async deleteFile(\n fileId: string,\n options?: RequestOptions,\n ): Promise<FileMetadataResponse> {\n return this.delete<FileMetadataResponse>(`/files/${fileId}`, undefined, options);\n }\n\n /**\n * Delete multiple files\n *\n * @param data - Array of file IDs to delete\n * @param options - Optional request options\n * @returns Array of deleted file metadata\n */\n async batchDeleteFiles(\n data: BatchDeleteFilesRequest,\n options?: RequestOptions,\n ): Promise<FileMetadataResponse[]> {\n return this.post<FileMetadataResponse[]>('/files/batch-delete', data, options);\n }\n\n // ============================================================================\n // Replace (multipart)\n // ============================================================================\n\n /**\n * Replace a single file via multipart form data\n *\n * @param fileId - The file ID to replace\n * @param file - The new file or blob\n * @param options - Optional request options\n * @returns Updated file metadata\n */\n async replaceFile(\n fileId: string,\n file: Blob | File,\n options?: RequestOptions,\n ): Promise<FileMetadataResponse> {\n const form = new FormData();\n form.append('file', file);\n return this.multipartRequest<FileMetadataResponse>('PUT', `/files/${fileId}`, form, options);\n }\n\n /**\n * Replace multiple files via multipart form data\n *\n * @param replacements - Array of file ID and new file pairs\n * @param options - Optional request options\n * @returns Array of updated file metadata\n */\n async batchReplaceFiles(\n replacements: BatchReplaceFilesEntry[],\n options?: RequestOptions,\n ): Promise<FileMetadataResponse[]> {\n const form = new FormData();\n for (const entry of replacements) {\n form.append('file_ids', entry.fileId);\n form.append('files', entry.file);\n }\n return this.multipartRequest<FileMetadataResponse[]>('POST', '/files/batch-replace', form, options);\n }\n\n // ============================================================================\n // Private helpers\n // ============================================================================\n\n /**\n * Execute a multipart/form-data request.\n *\n * Delegates to `MPFClient.request()` so multipart uploads get the same\n * middleware chain and retry loop as every other request. `request()` drops\n * the default JSON `Content-Type` for a `FormData` body so that fetch sets\n * `multipart/form-data` with the correct boundary string.\n *\n * `FormData` is re-readable, so a failed upload is safely retryable.\n */\n private async multipartRequest<T>(\n method: string,\n path: string,\n form: FormData,\n options?: RequestOptions,\n ): Promise<T> {\n return this.request<T>(\n path,\n {\n method,\n body: form,\n headers: options?.headers,\n signal: options?.signal,\n },\n options?.params,\n options,\n );\n }\n}\n"]}
@@ -0,0 +1,85 @@
1
+ import { isSupersededError, createTenantSelection, createSessionStorage, createSessionManager, createMemoryStorage, createDefaultStorage, createCrossTabCoordinator, applyJitter, SessionManager, REFRESH_TOKEN_SUPERSEDED } from './chunk-7WAV52EO.js';
2
+ import { __export, tryDecodeAccessToken, tokenLifetimeSeconds, supportedClaimsVersions, rolesForTenant, registerClaimsParser, listTenants, isExpired, isDeadSession, expiresAt, defaultRoleForTenant, decodeAccessToken, clearClaimsCache, canSelect, MPFUnsupportedClaimsVersionError, MPFClaimsError, DeadSessionError } from './chunk-AJWM5MDZ.js';
3
+
4
+ // src/auth/index.ts
5
+ var auth_exports = {};
6
+ __export(auth_exports, {
7
+ DeadSessionError: () => DeadSessionError,
8
+ MPFClaimsError: () => MPFClaimsError,
9
+ MPFUnsupportedClaimsVersionError: () => MPFUnsupportedClaimsVersionError,
10
+ REFRESH_TOKEN_SUPERSEDED: () => REFRESH_TOKEN_SUPERSEDED,
11
+ SessionManager: () => SessionManager,
12
+ TENANT_DIRECTORY_QUERY: () => TENANT_DIRECTORY_QUERY,
13
+ applyJitter: () => applyJitter,
14
+ canSelect: () => canSelect,
15
+ clearClaimsCache: () => clearClaimsCache,
16
+ createCrossTabCoordinator: () => createCrossTabCoordinator,
17
+ createDefaultStorage: () => createDefaultStorage,
18
+ createMemoryStorage: () => createMemoryStorage,
19
+ createOAuthStateStore: () => createOAuthStateStore,
20
+ createSessionManager: () => createSessionManager,
21
+ createSessionStorage: () => createSessionStorage,
22
+ createTenantSelection: () => createTenantSelection,
23
+ decodeAccessToken: () => decodeAccessToken,
24
+ defaultRoleForTenant: () => defaultRoleForTenant,
25
+ expiresAt: () => expiresAt,
26
+ isDeadSession: () => isDeadSession,
27
+ isExpired: () => isExpired,
28
+ isSupersededError: () => isSupersededError,
29
+ listTenants: () => listTenants,
30
+ registerClaimsParser: () => registerClaimsParser,
31
+ rolesForTenant: () => rolesForTenant,
32
+ supportedClaimsVersions: () => supportedClaimsVersions,
33
+ tokenLifetimeSeconds: () => tokenLifetimeSeconds,
34
+ tryDecodeAccessToken: () => tryDecodeAccessToken
35
+ });
36
+
37
+ // src/auth/oauth-state.ts
38
+ var DEFAULT_OAUTH_STATE_KEY = "mpf.oauth_state";
39
+ function createOAuthStateStore(options = {}) {
40
+ const storage = options.storage ?? createSessionStorage();
41
+ const key = options.storageKey ?? DEFAULT_OAUTH_STATE_KEY;
42
+ return {
43
+ save(state) {
44
+ if (typeof state !== "string" || state === "") {
45
+ throw new Error("[MPF SDK] Refusing to store an empty OAuth state");
46
+ }
47
+ try {
48
+ storage.setItem(key, state);
49
+ } catch {
50
+ }
51
+ },
52
+ take() {
53
+ const state = this.peek();
54
+ this.clear();
55
+ return state;
56
+ },
57
+ peek() {
58
+ try {
59
+ const value = storage.getItem(key);
60
+ return value === "" ? null : value;
61
+ } catch {
62
+ return null;
63
+ }
64
+ },
65
+ clear() {
66
+ try {
67
+ storage.removeItem(key);
68
+ } catch {
69
+ }
70
+ }
71
+ };
72
+ }
73
+
74
+ // src/auth/tenant-directory.ts
75
+ var TENANT_DIRECTORY_QUERY = `query MPFTenantDirectory {
76
+ identity_tenants {
77
+ id
78
+ name
79
+ slug
80
+ }
81
+ }`;
82
+
83
+ export { TENANT_DIRECTORY_QUERY, auth_exports, createOAuthStateStore };
84
+ //# sourceMappingURL=chunk-GPWGOYCA.js.map
85
+ //# sourceMappingURL=chunk-GPWGOYCA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth/index.ts","../src/auth/oauth-state.ts","../src/auth/tenant-directory.ts"],"names":[],"mappings":";;;;AAAA,IAAA,YAAA,GAAA;AAAA,QAAA,CAAA,YAAA,EAAA;AAAA,EAAA,gBAAA,EAAA,MAAA,gBAAA;AAAA,EAAA,cAAA,EAAA,MAAA,cAAA;AAAA,EAAA,gCAAA,EAAA,MAAA,gCAAA;AAAA,EAAA,wBAAA,EAAA,MAAA,wBAAA;AAAA,EAAA,cAAA,EAAA,MAAA,cAAA;AAAA,EAAA,sBAAA,EAAA,MAAA,sBAAA;AAAA,EAAA,WAAA,EAAA,MAAA,WAAA;AAAA,EAAA,SAAA,EAAA,MAAA,SAAA;AAAA,EAAA,gBAAA,EAAA,MAAA,gBAAA;AAAA,EAAA,yBAAA,EAAA,MAAA,yBAAA;AAAA,EAAA,oBAAA,EAAA,MAAA,oBAAA;AAAA,EAAA,mBAAA,EAAA,MAAA,mBAAA;AAAA,EAAA,qBAAA,EAAA,MAAA,qBAAA;AAAA,EAAA,oBAAA,EAAA,MAAA,oBAAA;AAAA,EAAA,oBAAA,EAAA,MAAA,oBAAA;AAAA,EAAA,qBAAA,EAAA,MAAA,qBAAA;AAAA,EAAA,iBAAA,EAAA,MAAA,iBAAA;AAAA,EAAA,oBAAA,EAAA,MAAA,oBAAA;AAAA,EAAA,SAAA,EAAA,MAAA,SAAA;AAAA,EAAA,aAAA,EAAA,MAAA,aAAA;AAAA,EAAA,SAAA,EAAA,MAAA,SAAA;AAAA,EAAA,iBAAA,EAAA,MAAA,iBAAA;AAAA,EAAA,WAAA,EAAA,MAAA,WAAA;AAAA,EAAA,oBAAA,EAAA,MAAA,oBAAA;AAAA,EAAA,cAAA,EAAA,MAAA,cAAA;AAAA,EAAA,uBAAA,EAAA,MAAA,uBAAA;AAAA,EAAA,oBAAA,EAAA,MAAA,oBAAA;AAAA,EAAA,oBAAA,EAAA,MAAA;AAAA,CAAA,CAAA;;;ACsCA,IAAM,uBAAA,GAA0B,iBAAA;AA6DzB,SAAS,qBAAA,CACd,OAAA,GAAkC,EAAC,EAClB;AACjB,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,oBAAA,EAAqB;AACxD,EAAA,MAAM,GAAA,GAAM,QAAQ,UAAA,IAAc,uBAAA;AAElC,EAAA,OAAO;AAAA,IACL,KAAK,KAAA,EAAO;AACV,MAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,EAAA,EAAI;AAG7C,QAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,MACpE;AACA,MAAA,IAAI;AACF,QAAA,OAAA,CAAQ,OAAA,CAAQ,KAAK,KAAK,CAAA;AAAA,MAC5B,CAAA,CAAA,MAAQ;AAAA,MAGR;AAAA,IACF,CAAA;AAAA,IAEA,IAAA,GAAO;AACL,MAAA,MAAM,KAAA,GAAQ,KAAK,IAAA,EAAK;AACxB,MAAA,IAAA,CAAK,KAAA,EAAM;AACX,MAAA,OAAO,KAAA;AAAA,IACT,CAAA;AAAA,IAEA,IAAA,GAAO;AACL,MAAA,IAAI;AACF,QAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AACjC,QAAA,OAAO,KAAA,KAAU,KAAK,IAAA,GAAO,KAAA;AAAA,MAC/B,CAAA,CAAA,MAAQ;AACN,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,IACF,CAAA;AAAA,IAEA,KAAA,GAAQ;AACN,MAAA,IAAI;AACF,QAAA,OAAA,CAAQ,WAAW,GAAG,CAAA;AAAA,MACxB,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,GACF;AACF;;;AChFO,IAAM,sBAAA,GAAyB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA","file":"chunk-GPWGOYCA.js","sourcesContent":["/**\n * Auth layer: token claims, tenant selection, session lifecycle.\n *\n * ⚠️ Claim decoding in this module DECODES BUT DOES NOT VERIFY. Its output is\n * UX-only and is forgeable by a hostile client. See the security block at the\n * top of `claims.ts`.\n *\n * @packageDocumentation\n */\n\nexport type {\n MPFRole,\n MPFClaims,\n MPFClaimsV1,\n MPFTenantClaim,\n ClaimsParser,\n} from './claims-types.js';\n\nexport {\n decodeAccessToken,\n tryDecodeAccessToken,\n listTenants,\n rolesForTenant,\n defaultRoleForTenant,\n canSelect,\n expiresAt,\n isExpired,\n tokenLifetimeSeconds,\n clearClaimsCache,\n registerClaimsParser,\n supportedClaimsVersions,\n} from './claims.js';\n\nexport {\n MPFClaimsError,\n MPFUnsupportedClaimsVersionError,\n DeadSessionError,\n isDeadSession,\n} from './errors.js';\n\nexport {\n createTenantSelection,\n type TenantSelection,\n type TenantSelectionStore,\n type TenantSelectionPersistence,\n type CreateTenantSelectionOptions,\n} from './tenant-selection.js';\n\nexport {\n createDefaultStorage,\n createSessionStorage,\n createMemoryStorage,\n type MPFAuthStorage,\n} from './storage.js';\n\nexport {\n createOAuthStateStore,\n type OAuthStateStore,\n type OAuthStateStoreOptions,\n} from './oauth-state.js';\n\nexport {\n createCrossTabCoordinator,\n applyJitter,\n type CrossTabCoordinator,\n type CrossTabMode,\n type CrossTabOptions,\n type TokenBroadcast,\n type LockManagerLike,\n type BroadcastChannelLike,\n} from './cross-tab.js';\n\nexport {\n TENANT_DIRECTORY_QUERY,\n type TenantDirectoryEntry,\n type TenantDirectoryLoader,\n} from './tenant-directory.js';\n\nexport {\n SessionManager,\n createSessionManager,\n isSupersededError,\n REFRESH_TOKEN_SUPERSEDED,\n type SessionManagerOptions,\n type SignOutResult,\n type TokenPair,\n} from './session-manager.js';\n","/**\n * OAuth `state` persistence — CSRF protection for the Google OAuth flow.\n *\n * ## What the state is for\n *\n * Without it, an OAuth callback route accepts *any* `code` presented to it. An\n * attacker who obtains an authorization code for their own account can send the\n * victim to `.../callback?code=<attacker's code>`; the victim's browser posts it,\n * the server exchanges it, and the victim is now signed in **as the attacker** —\n * whose account the attacker still controls. Anything the victim then does\n * (entering payment details, uploading documents, accepting an invitation)\n * happens inside the attacker's account.\n *\n * That is not theoretical: it was confirmed exploitable end to end in the\n * reference boilerplate, whose callback route discarded `state` and auto-posted\n * `code` on page load with no user interaction.\n *\n * The state closes it: the server mints an unguessable value, the client stores\n * it before redirecting, and the server accepts the callback only if the value\n * comes back. An attacker cannot produce a state that matches the victim's\n * browser storage.\n *\n * ## Why this store exists\n *\n * The state must survive a **full-page redirect to Google and back**, so it\n * cannot live in memory or in React state. That leaves web storage, and\n * \"remember to persist a value across a redirect\" is precisely the step the\n * boilerplate omitted. This makes it one call each way.\n *\n * Sending the state stays explicit: `GoogleOAuthCallbackRequest.state` is\n * required, so TypeScript rejects a callback that forgets it. This store solves\n * *where to keep it*, not *whether to send it* — a security parameter should not\n * be invisibly injected.\n */\n\nimport { createSessionStorage, type MPFAuthStorage } from './storage.js';\n\n/** Default storage key for the pending OAuth state. */\nconst DEFAULT_OAUTH_STATE_KEY = 'mpf.oauth_state';\n\nexport interface OAuthStateStore {\n /** Persist the state returned by `getGoogleOAuthURL`, before redirecting. */\n save(state: string): void;\n /**\n * Read and **clear** the stored state, for sending on the callback.\n *\n * Single-use by design: a state that could be replayed is not much better than\n * no state at all, and the server also rejects reuse.\n */\n take(): string | null;\n /** Read without clearing. For diagnostics; prefer `take()` in the flow. */\n peek(): string | null;\n /** Discard any pending state (e.g. the user abandoned the flow). */\n clear(): void;\n}\n\nexport interface OAuthStateStoreOptions {\n /**\n * Where to keep the pending state.\n *\n * Defaults to `sessionStorage`, which is the right default for a redirect\n * flow: it is scoped to the tab that started it, so a state minted in one tab\n * cannot be consumed by another, and it is cleared when the tab closes.\n *\n * **Limitation worth knowing:** `sessionStorage` is per-tab, so a callback\n * that lands in a *different* tab (a provider opening a new tab, or the user\n * moving the URL) will not find the state and the callback will be rejected —\n * failing closed, which is the correct direction, but it will look like a\n * spurious error. Pass `createDefaultStorage()` (localStorage) if your flow\n * can land in another tab, accepting that the state is then visible to every\n * tab on the origin.\n *\n * A cookie is not offered: one set from JavaScript cannot be `httpOnly`, so it\n * is no less readable than web storage while adding `SameSite` pitfalls on the\n * cross-site return leg.\n */\n storage?: MPFAuthStorage;\n /** Storage key. @default 'mpf.oauth_state' */\n storageKey?: string;\n}\n\n/**\n * Create a store for the pending OAuth state.\n *\n * @example\n * ```ts\n * const oauthState = createOAuthStateStore();\n * const auth = createAuthClient({ baseUrl, oauthStateStore: oauthState });\n *\n * // Starting the flow — the client saves the state for you.\n * const { url } = await auth.getGoogleOAuthURL({ redirect_uri });\n * window.location.href = url;\n *\n * // On the callback route, send it back explicitly.\n * const state = oauthState.take();\n * if (!state) throw new Error('No pending OAuth flow in this tab');\n * const tokens = await auth.handleGoogleOAuthCallback({ code, redirect_uri, state });\n * ```\n */\nexport function createOAuthStateStore(\n options: OAuthStateStoreOptions = {},\n): OAuthStateStore {\n const storage = options.storage ?? createSessionStorage();\n const key = options.storageKey ?? DEFAULT_OAUTH_STATE_KEY;\n\n return {\n save(state) {\n if (typeof state !== 'string' || state === '') {\n // Storing an empty state would guarantee a failed callback later, with\n // the cause long out of view. Fail where the mistake is.\n throw new Error('[MPF SDK] Refusing to store an empty OAuth state');\n }\n try {\n storage.setItem(key, state);\n } catch {\n // A blocked or full quota must not break starting the flow; the callback\n // will fail closed instead.\n }\n },\n\n take() {\n const state = this.peek();\n this.clear();\n return state;\n },\n\n peek() {\n try {\n const value = storage.getItem(key);\n return value === '' ? null : value;\n } catch {\n return null;\n }\n },\n\n clear() {\n try {\n storage.removeItem(key);\n } catch {\n // ignore\n }\n },\n };\n}\n","/**\n * Tenant display metadata — names and slugs.\n *\n * ## Why this is separate from the claim set\n *\n * The access token's `tnts` array carries tenant **ids and roles only**. That is\n * the authoritative, freshest answer to \"which tenants may this user select\",\n * and it needs no network call — but it renders a switcher full of UUIDs.\n *\n * Names live in the database (`identity.tenants`), so fetching them is a real\n * network operation that can really fail. Keeping it in its own module, behind\n * its own state, means the two questions stay distinguishable:\n *\n * - \"which tenants can I select?\" — claims, synchronous, cannot fail\n * - \"what are they called?\" — network, asynchronous, can fail\n *\n * Merging them would make a failed name lookup indistinguishable from having no\n * tenants, which is the exact bug class the tri-state exists to prevent.\n *\n * ## Reachability\n *\n * The `tenantless` Hasura role has select permissions on `identity_tenants`\n * scoped to `X-Hasura-User-Id`, so a signed-in user with NO tenant selected can\n * read the names of the tenants they belong to. That is precisely the switcher's\n * situation.\n */\n\n/** Display metadata for one tenant. */\nexport interface TenantDirectoryEntry {\n id: string;\n name?: string;\n slug?: string;\n}\n\n/**\n * Loads display metadata for the given tenant ids.\n *\n * Injected rather than implemented here so that neither this module nor the\n * React layer has to depend on the GraphQL client — which keeps `src/auth/` a\n * leaf and the React layer free of `src/client/graphql/`. Use\n * {@link TENANT_DIRECTORY_QUERY} with whichever GraphQL client the app already\n * has.\n */\nexport type TenantDirectoryLoader = (\n tenantIds: string[],\n) => Promise<TenantDirectoryEntry[]>;\n\n/**\n * A ready-made query for the loader.\n *\n * Runs under the `tenantless` role — i.e. with no `X-MPF-Tenant` header — which\n * is why a switcher can populate itself before any tenant is selected.\n *\n * @example\n * ```ts\n * import { TENANT_DIRECTORY_QUERY } from '@mosano-product-framework/sdk/auth';\n *\n * const loadTenantDirectory = async () => {\n * const data = await gql.query(TENANT_DIRECTORY_QUERY, undefined, { tenant: null });\n * return data.identity_tenants;\n * };\n * ```\n */\nexport const TENANT_DIRECTORY_QUERY = `query MPFTenantDirectory {\n identity_tenants {\n id\n name\n slug\n }\n}`;\n"]}