@forge/react 11.9.0-next.1 → 11.9.1-experimental-60ea29e
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/CHANGELOG.md +42 -0
- package/LICENSE.txt +1 -1
- package/out/hooks/__test__/usePermissions.test.js +381 -4
- package/out/hooks/usePermissions.d.ts +4 -31
- package/out/hooks/usePermissions.d.ts.map +1 -1
- package/out/hooks/usePermissions.js +29 -121
- package/package.json +3 -3
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @forge/react
|
|
2
2
|
|
|
3
|
+
## 11.9.1-experimental-60ea29e
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f058dd8: Expose Permissions API in @forge/bridge for Custom UI apps
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [f058dd8]
|
|
12
|
+
- @forge/bridge@5.10.2-experimental-60ea29e
|
|
13
|
+
|
|
14
|
+
## 11.9.1
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- f57dd69: Bug fix for usePermissions hook
|
|
19
|
+
- Updated dependencies [4205052]
|
|
20
|
+
- @forge/bridge@5.10.2
|
|
21
|
+
|
|
22
|
+
## 11.9.1-next.1
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Updated dependencies [4205052]
|
|
27
|
+
- @forge/bridge@5.10.2-next.0
|
|
28
|
+
|
|
29
|
+
## 11.9.1-next.0
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- f57dd69: Bug fix for usePermissions hook
|
|
34
|
+
|
|
35
|
+
## 11.9.0
|
|
36
|
+
|
|
37
|
+
### Minor Changes
|
|
38
|
+
|
|
39
|
+
- 92c8fe8: Added useTheme hook to access the current theme and subscribe to theme updates
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- @forge/bridge@5.10.1
|
|
44
|
+
|
|
3
45
|
## 11.9.0-next.1
|
|
4
46
|
|
|
5
47
|
### Minor Changes
|
package/LICENSE.txt
CHANGED
|
@@ -4,11 +4,25 @@ const react_hooks_1 = require("@testing-library/react-hooks");
|
|
|
4
4
|
const usePermissions_1 = require("../usePermissions");
|
|
5
5
|
const testUtils_1 = require("../../__test__/testUtils");
|
|
6
6
|
// Mock @forge/bridge
|
|
7
|
-
jest.mock('@forge/bridge', () =>
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
jest.mock('@forge/bridge', () => {
|
|
8
|
+
// Set up window before requiring actual bridge to avoid initialization issues
|
|
9
|
+
if (typeof window === 'undefined') {
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
global.window = global;
|
|
12
|
+
// @ts-ignore
|
|
13
|
+
global.window.__bridge = {
|
|
14
|
+
callBridge: jest.fn()
|
|
15
|
+
};
|
|
10
16
|
}
|
|
11
|
-
|
|
17
|
+
const actualBridge = jest.requireActual('@forge/bridge');
|
|
18
|
+
return {
|
|
19
|
+
...actualBridge,
|
|
20
|
+
view: {
|
|
21
|
+
...actualBridge.view,
|
|
22
|
+
getContext: jest.fn()
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
});
|
|
12
26
|
const mockGetContext = jest.fn();
|
|
13
27
|
describe('usePermissions', () => {
|
|
14
28
|
beforeEach(() => {
|
|
@@ -363,6 +377,369 @@ describe('usePermissions', () => {
|
|
|
363
377
|
});
|
|
364
378
|
});
|
|
365
379
|
});
|
|
380
|
+
describe('CSP path matching for client fetch', () => {
|
|
381
|
+
it('should allow any path when allowlist has no path', async () => {
|
|
382
|
+
const mockContext = {
|
|
383
|
+
permissions: {
|
|
384
|
+
external: {
|
|
385
|
+
fetch: {
|
|
386
|
+
client: ['https://cdn.example.com']
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
392
|
+
const requiredPermissions = {
|
|
393
|
+
external: {
|
|
394
|
+
fetch: {
|
|
395
|
+
client: [
|
|
396
|
+
'https://cdn.example.com',
|
|
397
|
+
'https://cdn.example.com/',
|
|
398
|
+
'https://cdn.example.com/any/path',
|
|
399
|
+
'https://cdn.example.com/file.js'
|
|
400
|
+
]
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
405
|
+
await (0, react_hooks_1.act)(async () => {
|
|
406
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
407
|
+
});
|
|
408
|
+
expect(result.current.isLoading).toBe(false);
|
|
409
|
+
expect(result.current.hasPermission).toBe(true);
|
|
410
|
+
});
|
|
411
|
+
it('should use prefix matching when allowlist has trailing slash', async () => {
|
|
412
|
+
const mockContext = {
|
|
413
|
+
permissions: {
|
|
414
|
+
external: {
|
|
415
|
+
fetch: {
|
|
416
|
+
client: ['https://cdn.example.com/api/']
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
422
|
+
const requiredPermissions = {
|
|
423
|
+
external: {
|
|
424
|
+
fetch: {
|
|
425
|
+
client: [
|
|
426
|
+
'https://cdn.example.com/api/',
|
|
427
|
+
'https://cdn.example.com/api/users',
|
|
428
|
+
'https://cdn.example.com/api/v1/data'
|
|
429
|
+
]
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
434
|
+
await (0, react_hooks_1.act)(async () => {
|
|
435
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
436
|
+
});
|
|
437
|
+
expect(result.current.isLoading).toBe(false);
|
|
438
|
+
expect(result.current.hasPermission).toBe(true);
|
|
439
|
+
});
|
|
440
|
+
it('should block paths outside prefix when using trailing slash', async () => {
|
|
441
|
+
const mockContext = {
|
|
442
|
+
permissions: {
|
|
443
|
+
external: {
|
|
444
|
+
fetch: {
|
|
445
|
+
client: ['https://cdn.example.com/api/']
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
451
|
+
const requiredPermissions = {
|
|
452
|
+
external: {
|
|
453
|
+
fetch: {
|
|
454
|
+
client: ['https://cdn.example.com/other', 'https://cdn.example.com/']
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
459
|
+
await (0, react_hooks_1.act)(async () => {
|
|
460
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
461
|
+
});
|
|
462
|
+
expect(result.current.isLoading).toBe(false);
|
|
463
|
+
expect(result.current.hasPermission).toBe(false);
|
|
464
|
+
expect(result.current.missingPermissions?.external?.fetch?.client).toEqual([
|
|
465
|
+
'https://cdn.example.com/other',
|
|
466
|
+
'https://cdn.example.com/'
|
|
467
|
+
]);
|
|
468
|
+
});
|
|
469
|
+
it('should use exact matching when allowlist has no trailing slash', async () => {
|
|
470
|
+
const mockContext = {
|
|
471
|
+
permissions: {
|
|
472
|
+
external: {
|
|
473
|
+
fetch: {
|
|
474
|
+
client: ['https://cdn.example.com/bundle.js']
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
480
|
+
const requiredPermissions = {
|
|
481
|
+
external: {
|
|
482
|
+
fetch: {
|
|
483
|
+
client: ['https://cdn.example.com/bundle.js']
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
488
|
+
await (0, react_hooks_1.act)(async () => {
|
|
489
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
490
|
+
});
|
|
491
|
+
expect(result.current.isLoading).toBe(false);
|
|
492
|
+
expect(result.current.hasPermission).toBe(true);
|
|
493
|
+
});
|
|
494
|
+
it('should block non-exact paths when using exact matching', async () => {
|
|
495
|
+
const mockContext = {
|
|
496
|
+
permissions: {
|
|
497
|
+
external: {
|
|
498
|
+
fetch: {
|
|
499
|
+
client: ['https://cdn.example.com/bundle.js']
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
505
|
+
const requiredPermissions = {
|
|
506
|
+
external: {
|
|
507
|
+
fetch: {
|
|
508
|
+
client: [
|
|
509
|
+
'https://cdn.example.com/bundle.js/extra',
|
|
510
|
+
'https://cdn.example.com/other.js',
|
|
511
|
+
'https://cdn.example.com/'
|
|
512
|
+
]
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
517
|
+
await (0, react_hooks_1.act)(async () => {
|
|
518
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
519
|
+
});
|
|
520
|
+
expect(result.current.isLoading).toBe(false);
|
|
521
|
+
expect(result.current.hasPermission).toBe(false);
|
|
522
|
+
expect(result.current.missingPermissions?.external?.fetch?.client).toEqual([
|
|
523
|
+
'https://cdn.example.com/bundle.js/extra',
|
|
524
|
+
'https://cdn.example.com/other.js',
|
|
525
|
+
'https://cdn.example.com/'
|
|
526
|
+
]);
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
describe('Backend vs Client CSP differences', () => {
|
|
530
|
+
it('should allow any path for backend (hostname-only matching)', async () => {
|
|
531
|
+
const mockContext = {
|
|
532
|
+
permissions: {
|
|
533
|
+
external: {
|
|
534
|
+
fetch: {
|
|
535
|
+
backend: ['https://api.example.com/specific/path']
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
541
|
+
const requiredPermissions = {
|
|
542
|
+
external: {
|
|
543
|
+
fetch: {
|
|
544
|
+
backend: [
|
|
545
|
+
'https://api.example.com',
|
|
546
|
+
'https://api.example.com/different/path',
|
|
547
|
+
'https://api.example.com/specific/path'
|
|
548
|
+
]
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
553
|
+
await (0, react_hooks_1.act)(async () => {
|
|
554
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
555
|
+
});
|
|
556
|
+
expect(result.current.isLoading).toBe(false);
|
|
557
|
+
expect(result.current.hasPermission).toBe(true);
|
|
558
|
+
});
|
|
559
|
+
it('should demonstrate backend allows but client blocks different paths', async () => {
|
|
560
|
+
const mockContext = {
|
|
561
|
+
permissions: {
|
|
562
|
+
external: {
|
|
563
|
+
fetch: {
|
|
564
|
+
backend: ['https://api.example.com/api/'],
|
|
565
|
+
client: ['https://api.example.com/api/']
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
571
|
+
const requiredPermissions = {
|
|
572
|
+
external: {
|
|
573
|
+
fetch: {
|
|
574
|
+
backend: ['https://api.example.com/private/secret.json'],
|
|
575
|
+
client: ['https://api.example.com/private/secret.json']
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
580
|
+
await (0, react_hooks_1.act)(async () => {
|
|
581
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
582
|
+
});
|
|
583
|
+
expect(result.current.isLoading).toBe(false);
|
|
584
|
+
expect(result.current.hasPermission).toBe(false);
|
|
585
|
+
// Backend should pass (hostname-only), client should fail (CSP path check)
|
|
586
|
+
expect(result.current.missingPermissions?.external?.fetch?.backend).toBeUndefined();
|
|
587
|
+
expect(result.current.missingPermissions?.external?.fetch?.client).toEqual([
|
|
588
|
+
'https://api.example.com/private/secret.json'
|
|
589
|
+
]);
|
|
590
|
+
});
|
|
591
|
+
});
|
|
592
|
+
describe('CSP for resource types', () => {
|
|
593
|
+
it('should use CSP validation for images with paths', async () => {
|
|
594
|
+
const mockContext = {
|
|
595
|
+
permissions: {
|
|
596
|
+
external: {
|
|
597
|
+
images: ['https://cdn.example.com/public/']
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
602
|
+
const requiredPermissions = {
|
|
603
|
+
external: {
|
|
604
|
+
images: ['https://cdn.example.com/public/image.png', 'https://cdn.example.com/public/nested/image.jpg']
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
608
|
+
await (0, react_hooks_1.act)(async () => {
|
|
609
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
610
|
+
});
|
|
611
|
+
expect(result.current.isLoading).toBe(false);
|
|
612
|
+
expect(result.current.hasPermission).toBe(true);
|
|
613
|
+
});
|
|
614
|
+
it('should block images outside allowed directory', async () => {
|
|
615
|
+
const mockContext = {
|
|
616
|
+
permissions: {
|
|
617
|
+
external: {
|
|
618
|
+
images: ['https://cdn.example.com/public/']
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
623
|
+
const requiredPermissions = {
|
|
624
|
+
external: {
|
|
625
|
+
images: ['https://cdn.example.com/private/image.png', 'https://cdn.example.com/image.png']
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
629
|
+
await (0, react_hooks_1.act)(async () => {
|
|
630
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
631
|
+
});
|
|
632
|
+
expect(result.current.isLoading).toBe(false);
|
|
633
|
+
expect(result.current.hasPermission).toBe(false);
|
|
634
|
+
expect(result.current.missingPermissions?.external?.images).toEqual([
|
|
635
|
+
'https://cdn.example.com/private/image.png',
|
|
636
|
+
'https://cdn.example.com/image.png'
|
|
637
|
+
]);
|
|
638
|
+
});
|
|
639
|
+
it('should apply CSP to all resource types', async () => {
|
|
640
|
+
const mockContext = {
|
|
641
|
+
permissions: {
|
|
642
|
+
external: {
|
|
643
|
+
scripts: ['https://cdn.example.com'],
|
|
644
|
+
styles: ['https://cdn.example.com/css/'],
|
|
645
|
+
fonts: ['https://fonts.example.com/font.woff2']
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
650
|
+
const requiredPermissions = {
|
|
651
|
+
external: {
|
|
652
|
+
scripts: ['https://cdn.example.com/bundle.js'],
|
|
653
|
+
styles: ['https://cdn.example.com/css/main.css'],
|
|
654
|
+
fonts: ['https://fonts.example.com/font.woff2'] // Exact match
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
658
|
+
await (0, react_hooks_1.act)(async () => {
|
|
659
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
660
|
+
});
|
|
661
|
+
expect(result.current.isLoading).toBe(false);
|
|
662
|
+
expect(result.current.hasPermission).toBe(true);
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
describe('URL normalization and protocol handling', () => {
|
|
666
|
+
it('should handle URLs without protocol', async () => {
|
|
667
|
+
const mockContext = {
|
|
668
|
+
permissions: {
|
|
669
|
+
external: {
|
|
670
|
+
fetch: {
|
|
671
|
+
backend: ['api.example.com']
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
677
|
+
const requiredPermissions = {
|
|
678
|
+
external: {
|
|
679
|
+
fetch: {
|
|
680
|
+
backend: ['https://api.example.com', 'api.example.com']
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
685
|
+
await (0, react_hooks_1.act)(async () => {
|
|
686
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
687
|
+
});
|
|
688
|
+
expect(result.current.isLoading).toBe(false);
|
|
689
|
+
expect(result.current.hasPermission).toBe(true);
|
|
690
|
+
});
|
|
691
|
+
it('should handle CSP secure protocol upgrades (http -> https)', async () => {
|
|
692
|
+
const mockContext = {
|
|
693
|
+
permissions: {
|
|
694
|
+
external: {
|
|
695
|
+
fetch: {
|
|
696
|
+
client: ['http://example.com']
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
702
|
+
const requiredPermissions = {
|
|
703
|
+
external: {
|
|
704
|
+
fetch: {
|
|
705
|
+
client: ['http://example.com', 'https://example.com']
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
710
|
+
await (0, react_hooks_1.act)(async () => {
|
|
711
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
712
|
+
});
|
|
713
|
+
expect(result.current.isLoading).toBe(false);
|
|
714
|
+
expect(result.current.hasPermission).toBe(true);
|
|
715
|
+
});
|
|
716
|
+
it('should not allow protocol downgrades (https -> http)', async () => {
|
|
717
|
+
const mockContext = {
|
|
718
|
+
permissions: {
|
|
719
|
+
external: {
|
|
720
|
+
fetch: {
|
|
721
|
+
client: ['https://example.com']
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
mockGetContext.mockResolvedValue(mockContext);
|
|
727
|
+
const requiredPermissions = {
|
|
728
|
+
external: {
|
|
729
|
+
fetch: {
|
|
730
|
+
client: ['http://example.com']
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
const { result } = (0, react_hooks_1.renderHook)(() => (0, usePermissions_1.usePermissions)(requiredPermissions));
|
|
735
|
+
await (0, react_hooks_1.act)(async () => {
|
|
736
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
737
|
+
});
|
|
738
|
+
expect(result.current.isLoading).toBe(false);
|
|
739
|
+
expect(result.current.hasPermission).toBe(false);
|
|
740
|
+
expect(result.current.missingPermissions?.external?.fetch?.client).toEqual(['http://example.com']);
|
|
741
|
+
});
|
|
742
|
+
});
|
|
366
743
|
describe('Edge cases', () => {
|
|
367
744
|
it('should handle empty required permissions', async () => {
|
|
368
745
|
const mockContext = {
|
|
@@ -1,17 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* reuse logic from @forge/api
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Resource types that can be loaded externally
|
|
7
|
-
*/
|
|
8
|
-
declare const RESOURCE_TYPES: readonly ["fonts", "styles", "frames", "images", "media", "scripts"];
|
|
9
|
-
export declare type ResourceType = (typeof RESOURCE_TYPES)[number];
|
|
10
|
-
/**
|
|
11
|
-
* Fetch types for external requests
|
|
12
|
-
*/
|
|
13
|
-
declare const FETCH_TYPES: readonly ["backend", "client"];
|
|
14
|
-
export declare type FetchType = (typeof FETCH_TYPES)[number];
|
|
1
|
+
import { type PermissionRequirements, type MissingPermissions, type PermissionCheckResult, type ResourceType, type FetchType } from '@forge/bridge';
|
|
2
|
+
export type { ResourceType, FetchType };
|
|
15
3
|
export interface Permissions {
|
|
16
4
|
scopes?: string[];
|
|
17
5
|
external?: {
|
|
@@ -28,21 +16,7 @@ export interface Permissions {
|
|
|
28
16
|
};
|
|
29
17
|
content?: Record<string, unknown>;
|
|
30
18
|
}
|
|
31
|
-
|
|
32
|
-
* Required permissions for a component
|
|
33
|
-
*/
|
|
34
|
-
export declare type PermissionRequirements = Permissions;
|
|
35
|
-
/**
|
|
36
|
-
* Missing permissions information
|
|
37
|
-
*/
|
|
38
|
-
export declare type MissingPermissions = Permissions;
|
|
39
|
-
/**
|
|
40
|
-
* Permission check result
|
|
41
|
-
*/
|
|
42
|
-
export interface PermissionCheckResult {
|
|
43
|
-
granted: boolean;
|
|
44
|
-
missing: MissingPermissions | null;
|
|
45
|
-
}
|
|
19
|
+
export type { PermissionRequirements, MissingPermissions, PermissionCheckResult };
|
|
46
20
|
/**
|
|
47
21
|
* Hook for checking permissions in Forge apps
|
|
48
22
|
*
|
|
@@ -82,8 +56,7 @@ export interface PermissionCheckResult {
|
|
|
82
56
|
export declare const usePermissions: (requiredPermissions: PermissionRequirements) => {
|
|
83
57
|
hasPermission: boolean;
|
|
84
58
|
isLoading: boolean;
|
|
85
|
-
missingPermissions:
|
|
59
|
+
missingPermissions: PermissionRequirements | null;
|
|
86
60
|
error: Error | null;
|
|
87
61
|
};
|
|
88
|
-
export {};
|
|
89
62
|
//# sourceMappingURL=usePermissions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePermissions.d.ts","sourceRoot":"","sources":["../../src/hooks/usePermissions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"usePermissions.d.ts","sourceRoot":"","sources":["../../src/hooks/usePermissions.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,SAAS,EACf,MAAM,eAAe,CAAC;AAGvB,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;AAExC,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE;YACN,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;YACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;SACnB,CAAC;QACF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAGD,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,cAAc,wBAAyB,sBAAsB;;;;;CAoEzE,CAAC"}
|