@weirdfingers/boards 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,1150 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ADD_BOARD_MEMBER: () => ADD_BOARD_MEMBER,
24
+ ArtifactType: () => ArtifactType,
25
+ AuthProvider: () => AuthProvider,
26
+ BOARD_FRAGMENT: () => BOARD_FRAGMENT,
27
+ BaseAuthProvider: () => BaseAuthProvider,
28
+ BoardRole: () => BoardRole,
29
+ BoardsProvider: () => BoardsProvider,
30
+ CANCEL_GENERATION: () => CANCEL_GENERATION,
31
+ CREATE_BOARD: () => CREATE_BOARD,
32
+ CREATE_GENERATION: () => CREATE_GENERATION,
33
+ DELETE_BOARD: () => DELETE_BOARD,
34
+ GENERATION_FRAGMENT: () => GENERATION_FRAGMENT,
35
+ GET_BOARD: () => GET_BOARD,
36
+ GET_BOARDS: () => GET_BOARDS,
37
+ GET_CURRENT_USER: () => GET_CURRENT_USER,
38
+ GET_GENERATION: () => GET_GENERATION,
39
+ GET_GENERATIONS: () => GET_GENERATIONS,
40
+ GET_GENERATORS: () => GET_GENERATORS,
41
+ GenerationStatus: () => GenerationStatus,
42
+ NoAuthProvider: () => NoAuthProvider,
43
+ REMOVE_BOARD_MEMBER: () => REMOVE_BOARD_MEMBER,
44
+ RETRY_GENERATION: () => RETRY_GENERATION,
45
+ UPDATE_BOARD: () => UPDATE_BOARD,
46
+ UPDATE_BOARD_MEMBER_ROLE: () => UPDATE_BOARD_MEMBER_ROLE,
47
+ USER_FRAGMENT: () => USER_FRAGMENT,
48
+ VERSION: () => VERSION,
49
+ createGraphQLClient: () => createGraphQLClient,
50
+ useApiConfig: () => useApiConfig,
51
+ useAuth: () => useAuth,
52
+ useAuthOptional: () => useAuthOptional,
53
+ useBoard: () => useBoard,
54
+ useBoards: () => useBoards,
55
+ useGeneration: () => useGeneration,
56
+ useGenerators: () => useGenerators
57
+ });
58
+ module.exports = __toCommonJS(index_exports);
59
+
60
+ // src/auth/context.tsx
61
+ var import_react = require("react");
62
+ var import_jsx_runtime = require("react/jsx-runtime");
63
+ var AuthContext = (0, import_react.createContext)(null);
64
+ function AuthProvider({ provider, children }) {
65
+ const [state, setState] = (0, import_react.useState)({
66
+ user: null,
67
+ status: "loading",
68
+ signIn: async () => {
69
+ },
70
+ signOut: async () => {
71
+ },
72
+ getToken: async () => null,
73
+ refreshToken: async () => null
74
+ });
75
+ const [isInitializing, setIsInitializing] = (0, import_react.useState)(true);
76
+ const [error, setError] = (0, import_react.useState)(null);
77
+ const clearError = (0, import_react.useCallback)(() => {
78
+ setError(null);
79
+ }, []);
80
+ (0, import_react.useEffect)(() => {
81
+ let mounted = true;
82
+ let unsubscribe = null;
83
+ const initializeAuth = async () => {
84
+ try {
85
+ await provider.initialize();
86
+ if (!mounted) return;
87
+ unsubscribe = provider.onAuthStateChange((newState) => {
88
+ if (mounted) {
89
+ setState(newState);
90
+ }
91
+ });
92
+ const initialState = await provider.getAuthState();
93
+ if (mounted) {
94
+ setState(initialState);
95
+ setIsInitializing(false);
96
+ }
97
+ } catch (err) {
98
+ if (mounted) {
99
+ setError(err instanceof Error ? err : new Error("Auth initialization failed"));
100
+ setIsInitializing(false);
101
+ }
102
+ }
103
+ };
104
+ initializeAuth();
105
+ return () => {
106
+ mounted = false;
107
+ if (unsubscribe) {
108
+ unsubscribe();
109
+ }
110
+ };
111
+ }, [provider]);
112
+ (0, import_react.useEffect)(() => {
113
+ return () => {
114
+ provider.destroy();
115
+ };
116
+ }, [provider]);
117
+ const contextValue = {
118
+ ...state,
119
+ isInitializing,
120
+ error,
121
+ clearError
122
+ };
123
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AuthContext.Provider, { value: contextValue, children });
124
+ }
125
+ function useAuth() {
126
+ const context = (0, import_react.useContext)(AuthContext);
127
+ if (!context) {
128
+ throw new Error("useAuth must be used within an AuthProvider");
129
+ }
130
+ return context;
131
+ }
132
+ function useAuthOptional() {
133
+ return (0, import_react.useContext)(AuthContext);
134
+ }
135
+
136
+ // src/auth/providers/base.ts
137
+ var BaseAuthProvider = class {
138
+ constructor(config = {}) {
139
+ this.config = config;
140
+ }
141
+ /**
142
+ * Get the tenant ID from config.
143
+ */
144
+ getTenantId() {
145
+ return this.config.tenantId || "default";
146
+ }
147
+ };
148
+
149
+ // src/auth/providers/none.ts
150
+ var NoAuthProvider = class extends BaseAuthProvider {
151
+ constructor(config = {}) {
152
+ super(config);
153
+ this.listeners = [];
154
+ const nodeEnv = typeof process !== "undefined" ? process.env?.NODE_ENV : "";
155
+ const isDevelopment = nodeEnv === "development" || nodeEnv === "" || nodeEnv === "test";
156
+ if (!isDevelopment) {
157
+ const error = new Error(
158
+ "NoAuthProvider cannot be used in production environments. Please configure a proper authentication provider (JWT, Supabase, Clerk, etc.)"
159
+ );
160
+ console.error("\u{1F6A8} SECURITY ERROR:", error.message);
161
+ throw error;
162
+ }
163
+ this.config = {
164
+ defaultUserId: "dev-user",
165
+ defaultEmail: "dev@example.com",
166
+ defaultDisplayName: "Development User",
167
+ ...config
168
+ };
169
+ this.defaultUser = {
170
+ id: this.config.defaultUserId,
171
+ email: this.config.defaultEmail,
172
+ name: this.config.defaultDisplayName,
173
+ avatar: void 0,
174
+ metadata: { provider: "none" },
175
+ credits: {
176
+ balance: 1e3,
177
+ reserved: 0
178
+ }
179
+ };
180
+ this.currentState = {
181
+ user: this.defaultUser,
182
+ status: "authenticated",
183
+ // Always authenticated in no-auth mode
184
+ signIn: this.signIn.bind(this),
185
+ signOut: this.signOut.bind(this),
186
+ getToken: this.getToken.bind(this),
187
+ refreshToken: this.refreshToken.bind(this)
188
+ };
189
+ if (console.warn) {
190
+ console.warn(
191
+ "\u{1F6A8} [AUTH] NoAuthProvider is active - authentication is disabled!",
192
+ {
193
+ message: "This should ONLY be used in development environments",
194
+ environment: nodeEnv || "unknown",
195
+ provider: "none"
196
+ }
197
+ );
198
+ }
199
+ }
200
+ async initialize() {
201
+ this.updateState({ user: this.defaultUser, status: "authenticated" });
202
+ }
203
+ async getAuthState() {
204
+ return this.currentState;
205
+ }
206
+ async signIn() {
207
+ if (console.info) {
208
+ console.info("[AUTH] SignIn called in no-auth mode - no action taken", {
209
+ provider: "none",
210
+ action: "signIn",
211
+ status: "ignored"
212
+ });
213
+ }
214
+ }
215
+ async signOut() {
216
+ if (console.info) {
217
+ console.info("[AUTH] SignOut called in no-auth mode - no action taken", {
218
+ provider: "none",
219
+ action: "signOut",
220
+ status: "ignored"
221
+ });
222
+ }
223
+ }
224
+ async getToken() {
225
+ return "dev-token|no-auth-mode|always-valid";
226
+ }
227
+ async refreshToken() {
228
+ return "dev-token|no-auth-mode|always-valid";
229
+ }
230
+ async getUser() {
231
+ return this.defaultUser;
232
+ }
233
+ onAuthStateChange(callback) {
234
+ callback(this.currentState);
235
+ this.listeners.push(callback);
236
+ return () => {
237
+ const index = this.listeners.indexOf(callback);
238
+ if (index > -1) {
239
+ this.listeners.splice(index, 1);
240
+ }
241
+ };
242
+ }
243
+ async destroy() {
244
+ this.listeners = [];
245
+ }
246
+ updateState(updates) {
247
+ this.currentState = { ...this.currentState, ...updates };
248
+ this.listeners.forEach((listener) => listener(this.currentState));
249
+ }
250
+ };
251
+
252
+ // src/config/ApiConfigContext.tsx
253
+ var import_react2 = require("react");
254
+ var import_jsx_runtime2 = require("react/jsx-runtime");
255
+ var ApiConfigContext = (0, import_react2.createContext)(null);
256
+ function ApiConfigProvider({
257
+ children,
258
+ config
259
+ }) {
260
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ApiConfigContext.Provider, { value: config, children });
261
+ }
262
+ function useApiConfig() {
263
+ const context = (0, import_react2.useContext)(ApiConfigContext);
264
+ if (!context) {
265
+ throw new Error("useApiConfig must be used within ApiConfigProvider");
266
+ }
267
+ return context;
268
+ }
269
+
270
+ // src/graphql/client.ts
271
+ var import_urql = require("urql");
272
+ var import_exchange_auth = require("@urql/exchange-auth");
273
+ var import_graphql_ws = require("graphql-ws");
274
+ function createGraphQLClient({
275
+ url,
276
+ subscriptionUrl,
277
+ auth,
278
+ tenantId
279
+ }) {
280
+ const wsClient = subscriptionUrl ? (0, import_graphql_ws.createClient)({
281
+ url: subscriptionUrl,
282
+ connectionParams: async () => {
283
+ const token = await auth.getToken();
284
+ const headers = {};
285
+ if (token) {
286
+ headers.Authorization = `Bearer ${token}`;
287
+ }
288
+ if (tenantId) {
289
+ headers["X-Tenant"] = tenantId;
290
+ }
291
+ return headers;
292
+ }
293
+ }) : null;
294
+ return (0, import_urql.createClient)({
295
+ url,
296
+ exchanges: [
297
+ import_urql.cacheExchange,
298
+ (0, import_exchange_auth.authExchange)(async () => {
299
+ let token = await auth.getToken();
300
+ return {
301
+ addAuthToOperation: (operation) => {
302
+ const headers = {};
303
+ if (token) {
304
+ headers.Authorization = `Bearer ${token}`;
305
+ }
306
+ if (tenantId) {
307
+ headers["X-Tenant"] = tenantId;
308
+ }
309
+ const fetchOptions = typeof operation.context.fetchOptions === "function" ? operation.context.fetchOptions() : operation.context.fetchOptions || {};
310
+ return (0, import_urql.makeOperation)(operation.kind, operation, {
311
+ ...operation.context,
312
+ fetchOptions: {
313
+ ...operation.context.fetchOptions,
314
+ headers: {
315
+ ...fetchOptions.headers,
316
+ ...headers
317
+ }
318
+ }
319
+ });
320
+ },
321
+ didAuthError: (error) => {
322
+ return error.graphQLErrors.some(
323
+ (e) => e.extensions?.code === "UNAUTHENTICATED" || e.extensions?.code === "UNAUTHORIZED"
324
+ );
325
+ },
326
+ willAuthError: () => {
327
+ return false;
328
+ },
329
+ refreshAuth: async () => {
330
+ token = await auth.getToken();
331
+ }
332
+ };
333
+ }),
334
+ import_urql.fetchExchange,
335
+ ...wsClient ? [
336
+ (0, import_urql.subscriptionExchange)({
337
+ forwardSubscription: (operation) => ({
338
+ subscribe: (sink) => ({
339
+ unsubscribe: wsClient.subscribe(
340
+ {
341
+ query: operation.query || "",
342
+ variables: operation.variables
343
+ },
344
+ sink
345
+ )
346
+ })
347
+ })
348
+ })
349
+ ] : []
350
+ ]
351
+ });
352
+ }
353
+
354
+ // src/graphql/operations.ts
355
+ var import_urql2 = require("urql");
356
+ var USER_FRAGMENT = import_urql2.gql`
357
+ fragment UserFragment on User {
358
+ id
359
+ email
360
+ displayName
361
+ avatarUrl
362
+ createdAt
363
+ }
364
+ `;
365
+ var BOARD_FRAGMENT = import_urql2.gql`
366
+ fragment BoardFragment on Board {
367
+ id
368
+ tenantId
369
+ ownerId
370
+ title
371
+ description
372
+ isPublic
373
+ settings
374
+ metadata
375
+ createdAt
376
+ updatedAt
377
+ generationCount
378
+ }
379
+ `;
380
+ var GENERATION_FRAGMENT = import_urql2.gql`
381
+ fragment GenerationFragment on Generation {
382
+ id
383
+ boardId
384
+ userId
385
+ generatorName
386
+ artifactType
387
+ status
388
+ progress
389
+ storageUrl
390
+ thumbnailUrl
391
+ inputParams
392
+ outputMetadata
393
+ errorMessage
394
+ createdAt
395
+ updatedAt
396
+ completedAt
397
+ }
398
+ `;
399
+ var GET_CURRENT_USER = import_urql2.gql`
400
+ ${USER_FRAGMENT}
401
+ query GetCurrentUser {
402
+ me {
403
+ ...UserFragment
404
+ }
405
+ }
406
+ `;
407
+ var GET_BOARDS = import_urql2.gql`
408
+ ${BOARD_FRAGMENT}
409
+ ${USER_FRAGMENT}
410
+ query GetBoards($limit: Int, $offset: Int) {
411
+ myBoards(limit: $limit, offset: $offset) {
412
+ ...BoardFragment
413
+ owner {
414
+ ...UserFragment
415
+ }
416
+ }
417
+ }
418
+ `;
419
+ var GET_BOARD = import_urql2.gql`
420
+ ${BOARD_FRAGMENT}
421
+ ${USER_FRAGMENT}
422
+ ${GENERATION_FRAGMENT}
423
+ query GetBoard($id: UUID!) {
424
+ board(id: $id) {
425
+ ...BoardFragment
426
+ owner {
427
+ ...UserFragment
428
+ }
429
+ members {
430
+ id
431
+ boardId
432
+ userId
433
+ role
434
+ invitedBy
435
+ joinedAt
436
+ user {
437
+ ...UserFragment
438
+ }
439
+ inviter {
440
+ ...UserFragment
441
+ }
442
+ }
443
+ generations(limit: 10) {
444
+ ...GenerationFragment
445
+ }
446
+ }
447
+ }
448
+ `;
449
+ var GET_GENERATORS = import_urql2.gql`
450
+ query GetGenerators($artifactType: String) {
451
+ generators(artifactType: $artifactType) {
452
+ name
453
+ description
454
+ artifactType
455
+ inputSchema
456
+ }
457
+ }
458
+ `;
459
+ var GET_GENERATIONS = import_urql2.gql`
460
+ ${GENERATION_FRAGMENT}
461
+ query GetGenerations($boardId: UUID, $limit: Int, $offset: Int) {
462
+ generations(boardId: $boardId, limit: $limit, offset: $offset) {
463
+ ...GenerationFragment
464
+ board {
465
+ id
466
+ title
467
+ }
468
+ user {
469
+ ...UserFragment
470
+ }
471
+ }
472
+ }
473
+ `;
474
+ var GET_GENERATION = import_urql2.gql`
475
+ ${GENERATION_FRAGMENT}
476
+ query GetGeneration($id: UUID!) {
477
+ generation(id: $id) {
478
+ ...GenerationFragment
479
+ board {
480
+ ...BoardFragment
481
+ }
482
+ user {
483
+ ...UserFragment
484
+ }
485
+ }
486
+ }
487
+ `;
488
+ var CREATE_BOARD = import_urql2.gql`
489
+ ${BOARD_FRAGMENT}
490
+ ${USER_FRAGMENT}
491
+ mutation CreateBoard($input: CreateBoardInput!) {
492
+ createBoard(input: $input) {
493
+ ...BoardFragment
494
+ owner {
495
+ ...UserFragment
496
+ }
497
+ }
498
+ }
499
+ `;
500
+ var UPDATE_BOARD = import_urql2.gql`
501
+ ${BOARD_FRAGMENT}
502
+ mutation UpdateBoard($id: UUID!, $input: UpdateBoardInput!) {
503
+ updateBoard(id: $id, input: $input) {
504
+ ...BoardFragment
505
+ }
506
+ }
507
+ `;
508
+ var DELETE_BOARD = import_urql2.gql`
509
+ mutation DeleteBoard($id: UUID!) {
510
+ deleteBoard(id: $id) {
511
+ success
512
+ }
513
+ }
514
+ `;
515
+ var ADD_BOARD_MEMBER = import_urql2.gql`
516
+ mutation AddBoardMember($boardId: UUID!, $email: String!, $role: BoardRole!) {
517
+ addBoardMember(boardId: $boardId, email: $email, role: $role) {
518
+ id
519
+ boardId
520
+ userId
521
+ role
522
+ invitedBy
523
+ joinedAt
524
+ user {
525
+ ...UserFragment
526
+ }
527
+ }
528
+ }
529
+ `;
530
+ var UPDATE_BOARD_MEMBER_ROLE = import_urql2.gql`
531
+ mutation UpdateBoardMemberRole($id: UUID!, $role: BoardRole!) {
532
+ updateBoardMemberRole(id: $id, role: $role) {
533
+ id
534
+ role
535
+ }
536
+ }
537
+ `;
538
+ var REMOVE_BOARD_MEMBER = import_urql2.gql`
539
+ mutation RemoveBoardMember($id: UUID!) {
540
+ removeBoardMember(id: $id) {
541
+ success
542
+ }
543
+ }
544
+ `;
545
+ var CREATE_GENERATION = import_urql2.gql`
546
+ ${GENERATION_FRAGMENT}
547
+ mutation CreateGeneration($input: CreateGenerationInput!) {
548
+ createGeneration(input: $input) {
549
+ ...GenerationFragment
550
+ }
551
+ }
552
+ `;
553
+ var CANCEL_GENERATION = import_urql2.gql`
554
+ mutation CancelGeneration($id: UUID!) {
555
+ cancelGeneration(id: $id) {
556
+ id
557
+ status
558
+ }
559
+ }
560
+ `;
561
+ var RETRY_GENERATION = import_urql2.gql`
562
+ ${GENERATION_FRAGMENT}
563
+ mutation RetryGeneration($id: UUID!) {
564
+ retryGeneration(id: $id) {
565
+ ...GenerationFragment
566
+ }
567
+ }
568
+ `;
569
+ var BoardRole = /* @__PURE__ */ ((BoardRole2) => {
570
+ BoardRole2["VIEWER"] = "VIEWER";
571
+ BoardRole2["EDITOR"] = "EDITOR";
572
+ BoardRole2["ADMIN"] = "ADMIN";
573
+ return BoardRole2;
574
+ })(BoardRole || {});
575
+ var GenerationStatus = /* @__PURE__ */ ((GenerationStatus2) => {
576
+ GenerationStatus2["PENDING"] = "PENDING";
577
+ GenerationStatus2["RUNNING"] = "RUNNING";
578
+ GenerationStatus2["COMPLETED"] = "COMPLETED";
579
+ GenerationStatus2["FAILED"] = "FAILED";
580
+ GenerationStatus2["CANCELLED"] = "CANCELLED";
581
+ return GenerationStatus2;
582
+ })(GenerationStatus || {});
583
+ var ArtifactType = /* @__PURE__ */ ((ArtifactType4) => {
584
+ ArtifactType4["IMAGE"] = "image";
585
+ ArtifactType4["VIDEO"] = "video";
586
+ ArtifactType4["AUDIO"] = "audio";
587
+ ArtifactType4["TEXT"] = "text";
588
+ ArtifactType4["LORA"] = "lora";
589
+ ArtifactType4["MODEL"] = "model";
590
+ return ArtifactType4;
591
+ })(ArtifactType || {});
592
+
593
+ // src/hooks/useBoards.ts
594
+ var import_react3 = require("react");
595
+ var import_urql3 = require("urql");
596
+ function useBoards(options = {}) {
597
+ const { limit = 50, offset = 0 } = options;
598
+ const [searchQuery, setSearchQuery] = (0, import_react3.useState)("");
599
+ const [{ data, fetching, error }, reexecuteQuery] = (0, import_urql3.useQuery)({
600
+ query: GET_BOARDS,
601
+ variables: { limit, offset }
602
+ });
603
+ const [, createBoardMutation] = (0, import_urql3.useMutation)(CREATE_BOARD);
604
+ const [, deleteBoardMutation] = (0, import_urql3.useMutation)(DELETE_BOARD);
605
+ const boards = (0, import_react3.useMemo)(() => data?.myBoards || [], [data?.myBoards]);
606
+ const createBoard = (0, import_react3.useCallback)(
607
+ async (input) => {
608
+ const result = await createBoardMutation({ input });
609
+ if (result.error) {
610
+ throw new Error(result.error.message);
611
+ }
612
+ if (!result.data?.createBoard) {
613
+ throw new Error("Failed to create board");
614
+ }
615
+ return result.data.createBoard;
616
+ },
617
+ [createBoardMutation]
618
+ );
619
+ const deleteBoard = (0, import_react3.useCallback)(
620
+ async (boardId) => {
621
+ const result = await deleteBoardMutation({ id: boardId });
622
+ if (result.error) {
623
+ throw new Error(result.error.message);
624
+ }
625
+ if (!result.data?.deleteBoard?.success) {
626
+ throw new Error("Failed to delete board");
627
+ }
628
+ reexecuteQuery({ requestPolicy: "network-only" });
629
+ },
630
+ [deleteBoardMutation, reexecuteQuery]
631
+ );
632
+ const searchBoards = (0, import_react3.useCallback)(
633
+ async (query) => {
634
+ setSearchQuery(query);
635
+ return new Promise((resolve) => {
636
+ setTimeout(() => {
637
+ resolve(
638
+ boards.filter(
639
+ (board) => board.title.toLowerCase().includes(query.toLowerCase()) || board.description?.toLowerCase().includes(query.toLowerCase())
640
+ )
641
+ );
642
+ }, 350);
643
+ });
644
+ },
645
+ [boards]
646
+ );
647
+ const refresh = (0, import_react3.useCallback)(async () => {
648
+ await reexecuteQuery({ requestPolicy: "network-only" });
649
+ }, [reexecuteQuery]);
650
+ return {
651
+ boards,
652
+ loading: fetching,
653
+ error: error ? new Error(error.message) : null,
654
+ createBoard,
655
+ deleteBoard,
656
+ searchBoards,
657
+ refresh,
658
+ setSearchQuery,
659
+ searchQuery
660
+ };
661
+ }
662
+
663
+ // src/hooks/useBoard.ts
664
+ var import_react4 = require("react");
665
+ var import_urql4 = require("urql");
666
+ function useBoard(boardId) {
667
+ const { user } = useAuth();
668
+ const [{ data, fetching, error }, reexecuteQuery] = (0, import_urql4.useQuery)({
669
+ query: GET_BOARD,
670
+ variables: { id: boardId },
671
+ pause: !boardId,
672
+ requestPolicy: "cache-and-network"
673
+ // Always fetch fresh data while showing cached data
674
+ });
675
+ const [, updateBoardMutation] = (0, import_urql4.useMutation)(UPDATE_BOARD);
676
+ const [, deleteBoardMutation] = (0, import_urql4.useMutation)(DELETE_BOARD);
677
+ const [, addMemberMutation] = (0, import_urql4.useMutation)(ADD_BOARD_MEMBER);
678
+ const [, updateMemberRoleMutation] = (0, import_urql4.useMutation)(UPDATE_BOARD_MEMBER_ROLE);
679
+ const [, removeMemberMutation] = (0, import_urql4.useMutation)(REMOVE_BOARD_MEMBER);
680
+ const board = (0, import_react4.useMemo)(() => data?.board || null, [data?.board]);
681
+ const members = (0, import_react4.useMemo)(() => board?.members || [], [board?.members]);
682
+ const permissions = (0, import_react4.useMemo)(() => {
683
+ if (!board || !user) {
684
+ return {
685
+ canEdit: false,
686
+ canDelete: false,
687
+ canAddMembers: false,
688
+ canRemoveMembers: false,
689
+ canGenerate: false,
690
+ canExport: false
691
+ };
692
+ }
693
+ const isOwner = board.ownerId === user.id;
694
+ const userMember = members.find(
695
+ (member) => member.userId === user.id
696
+ );
697
+ const userRole = userMember?.role;
698
+ const isAdmin = userRole === "ADMIN" /* ADMIN */;
699
+ const isEditor = userRole === "EDITOR" /* EDITOR */ || isAdmin;
700
+ const isViewer = userRole === "VIEWER" /* VIEWER */ || isEditor;
701
+ return {
702
+ canEdit: isOwner || isAdmin || isEditor,
703
+ canDelete: isOwner,
704
+ canAddMembers: isOwner || isAdmin,
705
+ canRemoveMembers: isOwner || isAdmin,
706
+ canGenerate: isOwner || isAdmin || isEditor,
707
+ canExport: isViewer
708
+ // Even viewers can export
709
+ };
710
+ }, [board, user, members]);
711
+ const updateBoard = (0, import_react4.useCallback)(
712
+ async (updates) => {
713
+ if (!boardId) {
714
+ throw new Error("Board ID is required");
715
+ }
716
+ const result = await updateBoardMutation({
717
+ id: boardId,
718
+ input: updates
719
+ });
720
+ if (result.error) {
721
+ throw new Error(result.error.message);
722
+ }
723
+ if (!result.data?.updateBoard) {
724
+ throw new Error("Failed to update board");
725
+ }
726
+ return result.data.updateBoard;
727
+ },
728
+ [boardId, updateBoardMutation]
729
+ );
730
+ const deleteBoard = (0, import_react4.useCallback)(async () => {
731
+ if (!boardId) {
732
+ throw new Error("Board ID is required");
733
+ }
734
+ const result = await deleteBoardMutation({ id: boardId });
735
+ if (result.error) {
736
+ throw new Error(result.error.message);
737
+ }
738
+ if (!result.data?.deleteBoard?.success) {
739
+ throw new Error("Failed to delete board");
740
+ }
741
+ }, [boardId, deleteBoardMutation]);
742
+ const addMember = (0, import_react4.useCallback)(
743
+ async (email, role) => {
744
+ if (!boardId) {
745
+ throw new Error("Board ID is required");
746
+ }
747
+ const result = await addMemberMutation({
748
+ boardId,
749
+ email,
750
+ role
751
+ });
752
+ if (result.error) {
753
+ throw new Error(result.error.message);
754
+ }
755
+ if (!result.data?.addBoardMember) {
756
+ throw new Error("Failed to add member");
757
+ }
758
+ reexecuteQuery({ requestPolicy: "network-only" });
759
+ return result.data.addBoardMember;
760
+ },
761
+ [boardId, addMemberMutation, reexecuteQuery]
762
+ );
763
+ const removeMember = (0, import_react4.useCallback)(
764
+ async (memberId) => {
765
+ const result = await removeMemberMutation({ id: memberId });
766
+ if (result.error) {
767
+ throw new Error(result.error.message);
768
+ }
769
+ if (!result.data?.removeBoardMember?.success) {
770
+ throw new Error("Failed to remove member");
771
+ }
772
+ reexecuteQuery({ requestPolicy: "network-only" });
773
+ },
774
+ [removeMemberMutation, reexecuteQuery]
775
+ );
776
+ const updateMemberRole = (0, import_react4.useCallback)(
777
+ async (memberId, role) => {
778
+ const result = await updateMemberRoleMutation({
779
+ id: memberId,
780
+ role
781
+ });
782
+ if (result.error) {
783
+ throw new Error(result.error.message);
784
+ }
785
+ if (!result.data?.updateBoardMemberRole) {
786
+ throw new Error("Failed to update member role");
787
+ }
788
+ reexecuteQuery({ requestPolicy: "network-only" });
789
+ return result.data.updateBoardMemberRole;
790
+ },
791
+ [updateMemberRoleMutation, reexecuteQuery]
792
+ );
793
+ const generateShareLink = (0, import_react4.useCallback)(
794
+ async (_options) => {
795
+ throw new Error("Share links not implemented yet");
796
+ },
797
+ []
798
+ );
799
+ const revokeShareLink = (0, import_react4.useCallback)(
800
+ async (_linkId) => {
801
+ throw new Error("Share link revocation not implemented yet");
802
+ },
803
+ []
804
+ );
805
+ const refresh = (0, import_react4.useCallback)(async () => {
806
+ await reexecuteQuery({ requestPolicy: "network-only" });
807
+ }, [reexecuteQuery]);
808
+ return {
809
+ board,
810
+ members,
811
+ permissions,
812
+ loading: fetching,
813
+ error: error ? new Error(error.message) : null,
814
+ updateBoard,
815
+ deleteBoard,
816
+ refresh,
817
+ addMember,
818
+ removeMember,
819
+ updateMemberRole,
820
+ generateShareLink,
821
+ revokeShareLink
822
+ };
823
+ }
824
+
825
+ // src/hooks/useGeneration.ts
826
+ var import_react5 = require("react");
827
+ var import_fetch_event_source = require("@microsoft/fetch-event-source");
828
+ var import_urql5 = require("urql");
829
+ function useGeneration() {
830
+ const [progress, setProgress] = (0, import_react5.useState)(null);
831
+ const [result, setResult] = (0, import_react5.useState)(null);
832
+ const [error, setError] = (0, import_react5.useState)(null);
833
+ const [isGenerating, setIsGenerating] = (0, import_react5.useState)(false);
834
+ const [history, setHistory] = (0, import_react5.useState)([]);
835
+ const { apiUrl } = useApiConfig();
836
+ const auth = useAuth();
837
+ const abortControllers = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
838
+ const [, createGenerationMutation] = (0, import_urql5.useMutation)(CREATE_GENERATION);
839
+ const [, cancelGenerationMutation] = (0, import_urql5.useMutation)(CANCEL_GENERATION);
840
+ const [, retryGenerationMutation] = (0, import_urql5.useMutation)(RETRY_GENERATION);
841
+ (0, import_react5.useEffect)(() => {
842
+ return () => {
843
+ abortControllers.current.forEach((controller) => {
844
+ controller.abort();
845
+ });
846
+ abortControllers.current.clear();
847
+ };
848
+ }, []);
849
+ const connectToSSE = (0, import_react5.useCallback)(
850
+ async (jobId) => {
851
+ const existingController = abortControllers.current.get(jobId);
852
+ if (existingController) {
853
+ existingController.abort();
854
+ }
855
+ const abortController = new AbortController();
856
+ abortControllers.current.set(jobId, abortController);
857
+ const token = await auth.getToken();
858
+ const headers = {
859
+ Accept: "text/event-stream"
860
+ };
861
+ if (token) {
862
+ headers.Authorization = `Bearer ${token}`;
863
+ }
864
+ const sseUrl = `${apiUrl}/api/sse/generations/${jobId}/progress`;
865
+ console.log("SSE: Connecting to", sseUrl, "with headers:", headers);
866
+ try {
867
+ await (0, import_fetch_event_source.fetchEventSource)(sseUrl, {
868
+ headers,
869
+ signal: abortController.signal,
870
+ async onopen(response) {
871
+ console.log(
872
+ "SSE: Connection opened",
873
+ response.status,
874
+ response.statusText
875
+ );
876
+ if (response.ok) {
877
+ console.log("SSE: Connection successful");
878
+ } else {
879
+ console.error("SSE: Connection failed", response.status);
880
+ throw new Error(`SSE connection failed: ${response.statusText}`);
881
+ }
882
+ },
883
+ onmessage(event) {
884
+ console.log("SSE: Raw event received:", event);
885
+ if (!event.data || event.data.trim() === "") {
886
+ console.log("SSE: Skipping empty message");
887
+ return;
888
+ }
889
+ try {
890
+ const progressData = JSON.parse(event.data);
891
+ console.log("SSE: progress data received:", progressData);
892
+ setProgress(progressData);
893
+ if (progressData.status === "completed" || progressData.status === "failed" || progressData.status === "cancelled") {
894
+ setIsGenerating(false);
895
+ if (progressData.status === "completed") {
896
+ const mockResult = {
897
+ id: progressData.jobId,
898
+ jobId: progressData.jobId,
899
+ boardId: "",
900
+ // Would be filled from the original request
901
+ request: {},
902
+ artifacts: [],
903
+ credits: { cost: 0, balanceBefore: 0, balance: 0 },
904
+ performance: {
905
+ queueTime: 0,
906
+ processingTime: 0,
907
+ totalTime: 0
908
+ },
909
+ createdAt: /* @__PURE__ */ new Date()
910
+ };
911
+ setResult(mockResult);
912
+ setHistory((prev) => [...prev, mockResult]);
913
+ } else if (progressData.status === "failed") {
914
+ setError(new Error("Generation failed"));
915
+ }
916
+ abortController.abort();
917
+ abortControllers.current.delete(jobId);
918
+ }
919
+ } catch (err) {
920
+ console.error("Failed to parse SSE message:", err);
921
+ setError(new Error("Failed to parse progress update"));
922
+ setIsGenerating(false);
923
+ abortController.abort();
924
+ abortControllers.current.delete(jobId);
925
+ }
926
+ },
927
+ onerror(err) {
928
+ console.error("SSE connection error:", err);
929
+ console.error("SSE error details:", {
930
+ message: err instanceof Error ? err.message : String(err),
931
+ jobId,
932
+ url: sseUrl
933
+ });
934
+ setError(new Error("Lost connection to generation progress"));
935
+ setIsGenerating(false);
936
+ abortController.abort();
937
+ abortControllers.current.delete(jobId);
938
+ throw err;
939
+ },
940
+ openWhenHidden: true
941
+ // Keep connection open when tab is hidden
942
+ });
943
+ } catch (err) {
944
+ if (abortController.signal.aborted) {
945
+ console.log("SSE connection aborted for job:", jobId);
946
+ } else {
947
+ console.error("SSE connection failed:", err);
948
+ }
949
+ }
950
+ },
951
+ [apiUrl, auth]
952
+ );
953
+ const submit = (0, import_react5.useCallback)(
954
+ async (request) => {
955
+ setError(null);
956
+ setProgress(null);
957
+ setResult(null);
958
+ setIsGenerating(true);
959
+ const input = {
960
+ boardId: request.boardId,
961
+ generatorName: request.model,
962
+ artifactType: request.artifactType,
963
+ inputParams: {
964
+ ...request.inputs,
965
+ ...request.options
966
+ }
967
+ };
968
+ let lastError = null;
969
+ const maxRetries = 2;
970
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
971
+ try {
972
+ const result2 = await createGenerationMutation({ input });
973
+ if (result2.error) {
974
+ throw new Error(result2.error.message);
975
+ }
976
+ if (!result2.data?.createGeneration) {
977
+ throw new Error("Failed to create generation");
978
+ }
979
+ const jobId = result2.data.createGeneration.id;
980
+ connectToSSE(jobId);
981
+ setIsGenerating(false);
982
+ return jobId;
983
+ } catch (err) {
984
+ lastError = err instanceof Error ? err : new Error("Failed to submit generation");
985
+ if (lastError.message.includes("insufficient credits") || lastError.message.includes("validation") || lastError.message.includes("unauthorized") || lastError.message.includes("forbidden")) {
986
+ setError(lastError);
987
+ setIsGenerating(false);
988
+ throw lastError;
989
+ }
990
+ if (attempt === maxRetries) {
991
+ setError(lastError);
992
+ setIsGenerating(false);
993
+ throw lastError;
994
+ }
995
+ await new Promise((resolve) => setTimeout(resolve, 1e3 * attempt));
996
+ }
997
+ }
998
+ const finalError = lastError || new Error("Failed to submit generation after retries");
999
+ setError(finalError);
1000
+ setIsGenerating(false);
1001
+ throw finalError;
1002
+ },
1003
+ [createGenerationMutation, connectToSSE]
1004
+ );
1005
+ const cancel = (0, import_react5.useCallback)(
1006
+ async (jobId) => {
1007
+ try {
1008
+ const result2 = await cancelGenerationMutation({ id: jobId });
1009
+ if (result2.error) {
1010
+ throw new Error(result2.error.message);
1011
+ }
1012
+ const controller = abortControllers.current.get(jobId);
1013
+ if (controller) {
1014
+ controller.abort();
1015
+ abortControllers.current.delete(jobId);
1016
+ }
1017
+ setIsGenerating(false);
1018
+ setProgress((prev) => prev ? { ...prev, status: "cancelled" } : null);
1019
+ } catch (err) {
1020
+ setError(
1021
+ err instanceof Error ? err : new Error("Failed to cancel generation")
1022
+ );
1023
+ }
1024
+ },
1025
+ [cancelGenerationMutation]
1026
+ );
1027
+ const retry = (0, import_react5.useCallback)(
1028
+ async (jobId) => {
1029
+ try {
1030
+ setError(null);
1031
+ setIsGenerating(true);
1032
+ const result2 = await retryGenerationMutation({ id: jobId });
1033
+ if (result2.error) {
1034
+ throw new Error(result2.error.message);
1035
+ }
1036
+ if (!result2.data?.retryGeneration) {
1037
+ throw new Error("Failed to retry generation");
1038
+ }
1039
+ const newJobId = result2.data.retryGeneration.id;
1040
+ connectToSSE(newJobId);
1041
+ } catch (err) {
1042
+ setError(
1043
+ err instanceof Error ? err : new Error("Failed to retry generation")
1044
+ );
1045
+ setIsGenerating(false);
1046
+ }
1047
+ },
1048
+ [retryGenerationMutation, connectToSSE]
1049
+ );
1050
+ const clearHistory = (0, import_react5.useCallback)(() => {
1051
+ setHistory([]);
1052
+ }, []);
1053
+ return {
1054
+ progress,
1055
+ result,
1056
+ error,
1057
+ isGenerating,
1058
+ submit,
1059
+ cancel,
1060
+ retry,
1061
+ history,
1062
+ clearHistory
1063
+ };
1064
+ }
1065
+
1066
+ // src/hooks/useGenerators.ts
1067
+ var import_react6 = require("react");
1068
+ var import_urql6 = require("urql");
1069
+ function useGenerators(options = {}) {
1070
+ const { artifactType } = options;
1071
+ const [{ data, fetching, error }] = (0, import_urql6.useQuery)({
1072
+ query: GET_GENERATORS,
1073
+ variables: artifactType ? { artifactType } : {}
1074
+ });
1075
+ const generators = (0, import_react6.useMemo)(() => data?.generators || [], [data?.generators]);
1076
+ return {
1077
+ generators,
1078
+ loading: fetching,
1079
+ error: error ? new Error(error.message) : null
1080
+ };
1081
+ }
1082
+
1083
+ // src/providers/BoardsProvider.tsx
1084
+ var import_urql7 = require("urql");
1085
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1086
+ function BoardsProvider({
1087
+ children,
1088
+ apiUrl,
1089
+ graphqlUrl,
1090
+ subscriptionUrl,
1091
+ authProvider,
1092
+ tenantId
1093
+ }) {
1094
+ const resolvedGraphqlUrl = graphqlUrl || `${apiUrl}/graphql`;
1095
+ const apiConfig = {
1096
+ apiUrl,
1097
+ graphqlUrl: resolvedGraphqlUrl,
1098
+ subscriptionUrl
1099
+ };
1100
+ const client = createGraphQLClient({
1101
+ url: resolvedGraphqlUrl,
1102
+ subscriptionUrl,
1103
+ auth: {
1104
+ getToken: () => authProvider.getAuthState().then((state) => state.getToken())
1105
+ },
1106
+ tenantId
1107
+ });
1108
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AuthProvider, { provider: authProvider, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ApiConfigProvider, { config: apiConfig, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_urql7.Provider, { value: client, children }) }) });
1109
+ }
1110
+
1111
+ // src/index.ts
1112
+ var VERSION = "0.1.0";
1113
+ // Annotate the CommonJS export names for ESM import in node:
1114
+ 0 && (module.exports = {
1115
+ ADD_BOARD_MEMBER,
1116
+ ArtifactType,
1117
+ AuthProvider,
1118
+ BOARD_FRAGMENT,
1119
+ BaseAuthProvider,
1120
+ BoardRole,
1121
+ BoardsProvider,
1122
+ CANCEL_GENERATION,
1123
+ CREATE_BOARD,
1124
+ CREATE_GENERATION,
1125
+ DELETE_BOARD,
1126
+ GENERATION_FRAGMENT,
1127
+ GET_BOARD,
1128
+ GET_BOARDS,
1129
+ GET_CURRENT_USER,
1130
+ GET_GENERATION,
1131
+ GET_GENERATIONS,
1132
+ GET_GENERATORS,
1133
+ GenerationStatus,
1134
+ NoAuthProvider,
1135
+ REMOVE_BOARD_MEMBER,
1136
+ RETRY_GENERATION,
1137
+ UPDATE_BOARD,
1138
+ UPDATE_BOARD_MEMBER_ROLE,
1139
+ USER_FRAGMENT,
1140
+ VERSION,
1141
+ createGraphQLClient,
1142
+ useApiConfig,
1143
+ useAuth,
1144
+ useAuthOptional,
1145
+ useBoard,
1146
+ useBoards,
1147
+ useGeneration,
1148
+ useGenerators
1149
+ });
1150
+ //# sourceMappingURL=index.js.map