windmill-components 1.504.5 → 1.510.1

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 (202) hide show
  1. package/package/ata/index.js +1 -1
  2. package/package/components/AppConnectInner.svelte +161 -29
  3. package/package/components/ArgInput.svelte +33 -103
  4. package/package/components/AuthSettings.svelte +45 -1
  5. package/package/components/Dev.svelte +31 -24
  6. package/package/components/DisplayResult.svelte +53 -26
  7. package/package/components/DisplayResult.svelte.d.ts +1 -1
  8. package/package/components/DynSelect.svelte +3 -3
  9. package/package/components/Editor.svelte +7 -4
  10. package/package/components/EditorBar.svelte +2 -2
  11. package/package/components/ErrorOrRecoveryHandler.svelte +73 -67
  12. package/package/components/ErrorOrRecoveryHandler.svelte.d.ts +8 -24
  13. package/package/components/FlowBuilder.svelte +11 -2
  14. package/package/components/FlowJobResult.svelte +12 -17
  15. package/package/components/FlowJobResult.svelte.d.ts +5 -18
  16. package/package/components/FlowPreviewContent.svelte +13 -10
  17. package/package/components/FlowPreviewContent.svelte.d.ts +1 -1
  18. package/package/components/FlowPreviewResult.svelte +14 -6
  19. package/package/components/FlowStatusViewer.svelte +11 -24
  20. package/package/components/FlowStatusViewer.svelte.d.ts +19 -18
  21. package/package/components/FlowStatusViewerInner.svelte +110 -131
  22. package/package/components/FlowStatusViewerInner.svelte.d.ts +20 -18
  23. package/package/components/GitDiffPreview.svelte +55 -0
  24. package/package/components/GitDiffPreview.svelte.d.ts +13 -0
  25. package/package/components/HistoricInputs.svelte +2 -2
  26. package/package/components/InitGitRepoPopover.svelte +410 -0
  27. package/package/components/InitGitRepoPopover.svelte.d.ts +13 -0
  28. package/package/components/InstanceSetting.svelte +21 -9
  29. package/package/components/InstanceSettings.svelte +16 -3
  30. package/package/components/JobLoader.svelte +567 -0
  31. package/package/components/JobLoader.svelte.d.ts +53 -0
  32. package/package/components/JobLogs.svelte +6 -4
  33. package/package/components/JobLogs.svelte.d.ts +5 -18
  34. package/package/components/LightweightResourcePicker.svelte +18 -39
  35. package/package/components/LightweightResourcePicker.svelte.d.ts +6 -22
  36. package/package/components/LogViewer.svelte +35 -41
  37. package/package/components/LogViewer.svelte.d.ts +6 -20
  38. package/package/components/ModulePreviewResultViewer.svelte +3 -1
  39. package/package/components/ModulePreviewResultViewer.svelte.d.ts +1 -0
  40. package/package/components/ModuleTest.svelte +16 -11
  41. package/package/components/PullGitRepoPopover.svelte +355 -0
  42. package/package/components/PullGitRepoPopover.svelte.d.ts +18 -0
  43. package/package/components/S3FilePicker.svelte +5 -3
  44. package/package/components/SavedInputs.svelte +2 -2
  45. package/package/components/ScriptBuilder.svelte +4 -3
  46. package/package/components/ScriptEditor.svelte +34 -31
  47. package/package/components/ScriptEditor.svelte.d.ts +3 -3
  48. package/package/components/ServiceLogsInner.svelte +2 -1
  49. package/package/components/ServiceLogsInner.svelte.d.ts +1 -0
  50. package/package/components/UserSettings.svelte +1 -1
  51. package/package/components/WorkerTagSelect.svelte +32 -3
  52. package/package/components/apps/components/buttons/AppButton.svelte +7 -1
  53. package/package/components/apps/components/buttons/AppButton.svelte.d.ts +1 -0
  54. package/package/components/apps/components/display/AppCustomComponent.svelte +1 -1
  55. package/package/components/apps/components/display/AppDisplayComponentByJobId.svelte +16 -11
  56. package/package/components/apps/components/display/AppJobIdLogComponent.svelte +13 -10
  57. package/package/components/apps/components/display/AppMenu.svelte +5 -0
  58. package/package/components/apps/components/display/dbtable/AppDbExplorer.svelte +3 -3
  59. package/package/components/apps/components/display/dbtable/DeleteRow.svelte +3 -3
  60. package/package/components/apps/components/display/dbtable/InsertRowRunnable.svelte +3 -3
  61. package/package/components/apps/components/display/dbtable/UpdateCell.svelte +3 -3
  62. package/package/components/apps/components/display/table/AppAggridInfiniteTable.svelte +3 -3
  63. package/package/components/apps/components/helpers/RunnableComponent.svelte +65 -54
  64. package/package/components/apps/components/helpers/RunnableComponent.svelte.d.ts +5 -5
  65. package/package/components/apps/components/inputs/AppUserResource.svelte +26 -8
  66. package/package/components/apps/editor/AppEditorHeader.svelte +11 -5
  67. package/package/components/apps/editor/AppJobsDrawer.svelte +5 -5
  68. package/package/components/apps/editor/RunnableJobPanel.svelte +4 -4
  69. package/package/components/apps/editor/component/components.d.ts +12 -0
  70. package/package/components/apps/editor/component/components.js +19 -7
  71. package/package/components/assets/AssetButtons.svelte +38 -0
  72. package/package/components/assets/AssetButtons.svelte.d.ts +15 -0
  73. package/package/components/assets/AssetsDropdownButton.svelte +60 -72
  74. package/package/components/assets/AssetsDropdownButton.svelte.d.ts +3 -4
  75. package/package/components/assets/AssetsUsageDrawer.svelte +10 -10
  76. package/package/components/assets/JobAssetsViewer.svelte +79 -0
  77. package/package/components/assets/JobAssetsViewer.svelte.d.ts +7 -0
  78. package/package/components/assets/README_DEV.md +0 -0
  79. package/package/components/assets/lib.d.ts +9 -1
  80. package/package/components/assets/lib.js +48 -7
  81. package/package/components/common/fileUpload/FileUpload.svelte +126 -84
  82. package/package/components/common/fileUpload/FileUpload.svelte.d.ts +13 -3
  83. package/package/components/common/fileUpload/S3ArgInput.svelte +111 -0
  84. package/package/components/common/fileUpload/S3ArgInput.svelte.d.ts +21 -0
  85. package/package/components/common/table/ScriptRow.svelte +3 -1
  86. package/package/components/copilot/autocomplete/Autocompletor.js +23 -5
  87. package/package/components/copilot/chat/AIChatDisplay.svelte +8 -0
  88. package/package/components/copilot/chat/AIChatManager.svelte.js +13 -8
  89. package/package/components/copilot/chat/flow/ModuleAcceptReject.svelte +5 -5
  90. package/package/components/copilot/chat/flow/core.d.ts +1 -1
  91. package/package/components/copilot/chat/flow/core.js +2 -38
  92. package/package/components/copilot/chat/navigator/apiTools.d.ts +8 -0
  93. package/package/components/copilot/chat/navigator/apiTools.js +95 -15
  94. package/package/components/copilot/chat/navigator/core.d.ts +1 -1
  95. package/package/components/copilot/chat/navigator/core.js +2 -1
  96. package/package/components/copilot/chat/script/core.d.ts +11 -2
  97. package/package/components/copilot/chat/script/core.js +135 -1
  98. package/package/components/copilot/chat/shared.d.ts +10 -0
  99. package/package/components/copilot/chat/shared.js +56 -0
  100. package/package/components/copilot/lib.d.ts +1 -0
  101. package/package/components/copilot/lib.js +27 -9
  102. package/package/components/custom_ui.d.ts +1 -0
  103. package/package/components/flows/FlowAssetsHandler.svelte +133 -0
  104. package/package/components/flows/FlowAssetsHandler.svelte.d.ts +14 -0
  105. package/package/components/flows/content/FlowModuleComponent.svelte +16 -18
  106. package/package/components/flows/flowStore.d.ts +1 -1
  107. package/package/components/flows/map/FlowModuleSchemaItem.svelte +1 -0
  108. package/package/components/flows/propPicker/OutputPicker.svelte +9 -4
  109. package/package/components/flows/scheduleUtils.js +1 -1
  110. package/package/components/flows/types.d.ts +2 -1
  111. package/package/components/graph/FlowGraphV2.svelte +8 -104
  112. package/package/components/graph/FlowGraphV2.svelte.d.ts +0 -2
  113. package/package/components/graph/graphBuilder.svelte.d.ts +6 -3
  114. package/package/components/graph/graphBuilder.svelte.js +35 -9
  115. package/package/components/graph/renderers/edges/BaseEdge.svelte +2 -5
  116. package/package/components/graph/renderers/edges/BaseEdge.svelte.d.ts +1 -0
  117. package/package/components/graph/renderers/nodes/AssetNode.svelte +23 -20
  118. package/package/components/graph/renderers/nodes/AssetNode.svelte.d.ts +5 -10
  119. package/package/components/graph/renderers/nodes/AssetsOverflowedNode.svelte +1 -1
  120. package/package/components/graph/util.js +1 -1
  121. package/package/components/home/ItemsList.svelte +2 -0
  122. package/package/components/icons/AssetGenericIcon.svelte +0 -3
  123. package/package/components/jobs/JobPreview.svelte +10 -6
  124. package/package/components/raw_apps/RawAppInlineScriptRunnable.svelte +13 -12
  125. package/package/components/runs/BatchReRunOptionsPane.svelte +5 -1
  126. package/package/components/runs/JobPreview.svelte +26 -16
  127. package/package/components/runs/{JobLoader.svelte.d.ts → JobsLoader.svelte.d.ts} +3 -3
  128. package/package/components/runs/NoWorkerWithTagWarning.svelte +2 -2
  129. package/package/components/runs/NoWorkerWithTagWarning.svelte.d.ts +1 -0
  130. package/package/components/runs/RunsFilter.svelte.d.ts +1 -1
  131. package/package/components/schema/AddPropertyFormV2.svelte +42 -33
  132. package/package/components/schema/AddPropertyFormV2.svelte.d.ts +1 -0
  133. package/package/components/schema/AddPropertyV2.svelte +2 -1
  134. package/package/components/schema/AddPropertyV2.svelte.d.ts +1 -0
  135. package/package/components/schema/EditableSchemaWrapper.svelte +3 -1
  136. package/package/components/schema/editable_schema_wrapper.d.ts +3 -0
  137. package/package/components/scriptEditor/LogPanel.svelte +3 -2
  138. package/package/components/script_builder.d.ts +2 -2
  139. package/package/components/settings/CreateToken.svelte +76 -41
  140. package/package/components/settings/CreateToken.svelte.d.ts +1 -1
  141. package/package/components/settings/ScopeSelector.svelte +613 -0
  142. package/package/components/settings/ScopeSelector.svelte.d.ts +8 -0
  143. package/package/components/settings/TokenDisplay.svelte +103 -0
  144. package/package/components/settings/TokenDisplay.svelte.d.ts +10 -0
  145. package/package/components/settings/TokensTable.svelte +70 -349
  146. package/package/components/sidebar/CriticalAlertModal.svelte +3 -0
  147. package/package/components/triggers/DeleteTriggerButton.svelte +1 -1
  148. package/package/components/triggers/TriggerEditorToolbar.svelte +3 -3
  149. package/package/components/triggers/TriggerRetriesAndErrorHandler.svelte +55 -0
  150. package/package/components/triggers/TriggerRetriesAndErrorHandler.svelte.d.ts +13 -0
  151. package/package/components/triggers/TriggersEditor.svelte +45 -3
  152. package/package/components/triggers/TriggersWrapper.svelte +2 -2
  153. package/package/components/triggers/gcp/GcpTriggerEditorInner.svelte +43 -2
  154. package/package/components/triggers/gcp/utils.js +9 -1
  155. package/package/components/triggers/http/OpenAPISpecGenerator.svelte +1 -0
  156. package/package/components/triggers/http/RouteEditorInner.svelte +208 -164
  157. package/package/components/triggers/http/RouteEditorInner.svelte.d.ts +6 -2
  158. package/package/components/triggers/http/utils.js +9 -3
  159. package/package/components/triggers/kafka/KafkaTriggerEditorInner.svelte +43 -2
  160. package/package/components/triggers/kafka/utils.js +9 -1
  161. package/package/components/triggers/mqtt/MqttEditorConfigSection.svelte +4 -132
  162. package/package/components/triggers/mqtt/MqttEditorConfigSection.svelte.d.ts +2 -5
  163. package/package/components/triggers/mqtt/MqttTriggerEditorInner.svelte +178 -9
  164. package/package/components/triggers/mqtt/utils.js +9 -1
  165. package/package/components/triggers/nats/NatsTriggerEditorInner.svelte +43 -2
  166. package/package/components/triggers/nats/utils.js +9 -1
  167. package/package/components/triggers/postgres/PostgresTriggerEditorInner.svelte +41 -2
  168. package/package/components/triggers/postgres/utils.js +9 -1
  169. package/package/components/triggers/schedules/ScheduleEditorInner.svelte +34 -88
  170. package/package/components/triggers/sqs/SqsTriggerEditorInner.svelte +43 -2
  171. package/package/components/triggers/sqs/utils.js +9 -1
  172. package/package/components/triggers/utils.js +12 -0
  173. package/package/components/triggers/websocket/WebsocketTriggerEditorInner.svelte +43 -2
  174. package/package/components/triggers/websocket/utils.js +11 -1
  175. package/package/components/workspaceSettings/AISettings.svelte +0 -2
  176. package/package/components/workspaceSettings/FilterList.svelte +56 -0
  177. package/package/components/workspaceSettings/FilterList.svelte.d.ts +8 -0
  178. package/package/components/workspaceSettings/GitSyncFilterSettings.svelte +785 -0
  179. package/package/components/workspaceSettings/GitSyncFilterSettings.svelte.d.ts +18 -0
  180. package/package/gen/core/OpenAPI.js +1 -1
  181. package/package/gen/schemas.gen.d.ts +305 -23
  182. package/package/gen/schemas.gen.js +305 -23
  183. package/package/gen/services.gen.d.ts +33 -1
  184. package/package/gen/services.gen.js +66 -2
  185. package/package/gen/types.gen.d.ts +216 -11
  186. package/package/history.svelte.js +0 -2
  187. package/package/hub.d.ts +1 -0
  188. package/package/hubPaths.json +5 -2
  189. package/package/infer.js +16 -10
  190. package/package/svelte5Utils.svelte.d.ts +1 -0
  191. package/package/svelte5Utils.svelte.js +25 -18
  192. package/package/toast.js +10 -0
  193. package/package/utils.d.ts +3 -2
  194. package/package/utils.js +20 -5
  195. package/package.json +11 -11
  196. package/package/components/ResultJobLoader.svelte +0 -219
  197. package/package/components/ResultJobLoader.svelte.d.ts +0 -52
  198. package/package/components/TestJobLoader.svelte +0 -274
  199. package/package/components/TestJobLoader.svelte.d.ts +0 -43
  200. package/package/components/icons/AssetVarIcon.svelte +0 -31
  201. package/package/components/icons/AssetVarIcon.svelte.d.ts +0 -9
  202. /package/package/components/runs/{JobLoader.svelte → JobsLoader.svelte} +0 -0
@@ -0,0 +1,613 @@
1
+ <script lang="ts">import { TokenService } from '../../gen';
2
+ import { sendUserToast } from '../../toast';
3
+ import { ChevronRight, Loader2, X } from 'lucide-svelte';
4
+ import Button from '../common/button/Button.svelte';
5
+ import Popover from '../meltComponents/Popover.svelte';
6
+ import Tooltip from '../Tooltip.svelte';
7
+ import { twMerge } from 'tailwind-merge';
8
+ let { selectedScopes = $bindable([]), disabled = false, class: className = '' } = $props();
9
+ let scopeDomains = $state(null);
10
+ let loading = $state(false);
11
+ let error = $state(null);
12
+ let componentState = $state({ domains: {} });
13
+ function createScopeState(scope) {
14
+ return {
15
+ isSelected: false,
16
+ resourcePaths: [],
17
+ currentInputValue: '',
18
+ pathError: undefined
19
+ };
20
+ }
21
+ function createDomainState(domain) {
22
+ const scopes = {};
23
+ for (const scope of domain.scopes) {
24
+ scopes[scope.value] = createScopeState(scope);
25
+ }
26
+ return {
27
+ isExpanded: false,
28
+ hasFullAccess: false,
29
+ scopes
30
+ };
31
+ }
32
+ function getScopeState(scopeValue) {
33
+ for (const domainState of Object.values(componentState.domains)) {
34
+ if (domainState.scopes[scopeValue]) {
35
+ return domainState.scopes[scopeValue];
36
+ }
37
+ }
38
+ return undefined;
39
+ }
40
+ function isScopeDisabled(scope, domain) {
41
+ const domainState = getDomainState(domain.name);
42
+ if (!domainState)
43
+ return false;
44
+ if (domainState.hasFullAccess && scope.value.endsWith(':read')) {
45
+ return true;
46
+ }
47
+ if (scope.value.endsWith(':read')) {
48
+ const writeScope = scope.value.replace(':read', ':write');
49
+ const writeScopeState = domainState.scopes[writeScope];
50
+ if (writeScopeState?.isSelected) {
51
+ return true;
52
+ }
53
+ }
54
+ return false;
55
+ }
56
+ function getDomainState(domainName) {
57
+ return componentState.domains[domainName];
58
+ }
59
+ async function fetchScopeDomains() {
60
+ loading = true;
61
+ error = null;
62
+ try {
63
+ scopeDomains = await TokenService.listAvailableScopes();
64
+ initializeDomainStates();
65
+ }
66
+ catch (err) {
67
+ console.error('Error fetching scope domains:', err);
68
+ sendUserToast('Failed to load scope options', true);
69
+ error = 'Failed to load scope options';
70
+ }
71
+ finally {
72
+ loading = false;
73
+ }
74
+ }
75
+ function initializeDomainStates() {
76
+ if (!scopeDomains)
77
+ return;
78
+ const newDomains = {};
79
+ for (const domain of scopeDomains) {
80
+ const domainState = createDomainState(domain);
81
+ const writeScopeValue = getWriteScopeForDomain(domain);
82
+ const hasWriteSelected = writeScopeValue &&
83
+ selectedScopes.some((scope) => scope === writeScopeValue || scope.startsWith(writeScopeValue + ':'));
84
+ const runScopes = domain.scopes.filter((scope) => scope.value.includes(':run:'));
85
+ const hasRunScopesSelected = runScopes.length === 0 ||
86
+ runScopes.every((runScope) => selectedScopes.some((scope) => scope === runScope.value || scope.startsWith(runScope.value + ':')));
87
+ domainState.hasFullAccess = Boolean(hasWriteSelected && hasRunScopesSelected);
88
+ // Initialize individual scope states
89
+ for (const scope of domain.scopes) {
90
+ const scopeState = domainState.scopes[scope.value];
91
+ const isSelected = selectedScopes.some((selected) => {
92
+ if (scope.requires_resource_path && selected.startsWith(scope.value + ':')) {
93
+ const resourcePath = selected.substring(scope.value.length + 1);
94
+ const paths = resourcePath.split(',').map((p) => p.trim());
95
+ scopeState.resourcePaths = [...scopeState.resourcePaths, ...paths];
96
+ return true;
97
+ }
98
+ return selected === scope.value;
99
+ });
100
+ scopeState.isSelected = isSelected;
101
+ }
102
+ newDomains[domain.name] = domainState;
103
+ }
104
+ componentState = { domains: newDomains };
105
+ }
106
+ function getWriteScopeForDomain(domain) {
107
+ const writeScope = domain.scopes.find((scope) => scope.value.endsWith(':write'));
108
+ return writeScope?.value || null;
109
+ }
110
+ function toggleDomainExpansion(domainName) {
111
+ const domainState = getDomainState(domainName);
112
+ if (domainState) {
113
+ domainState.isExpanded = !domainState.isExpanded;
114
+ }
115
+ }
116
+ function handleDomainCheckboxChange(domain, checked) {
117
+ const writeScopeValue = getWriteScopeForDomain(domain);
118
+ if (!writeScopeValue)
119
+ return;
120
+ const domainState = getDomainState(domain.name);
121
+ if (!domainState)
122
+ return;
123
+ domainState.hasFullAccess = checked;
124
+ if (checked) {
125
+ selectedScopes = selectedScopes.filter((scope) => !domain.scopes.some((domainScope) => scope === domainScope.value || scope.startsWith(domainScope.value + ':')));
126
+ const writeScope = domain.scopes.find((s) => s.value === writeScopeValue);
127
+ if (writeScope?.requires_resource_path) {
128
+ selectedScopes = [...selectedScopes, `${writeScopeValue}`];
129
+ }
130
+ else {
131
+ selectedScopes = [...selectedScopes, writeScopeValue];
132
+ }
133
+ const runScopes = domain.scopes.filter((scope) => scope.value.includes(':run:'));
134
+ for (const runScope of runScopes) {
135
+ if (runScope.requires_resource_path) {
136
+ selectedScopes = [...selectedScopes, `${runScope.value}`];
137
+ }
138
+ else {
139
+ selectedScopes = [...selectedScopes, runScope.value];
140
+ }
141
+ }
142
+ for (const scope of domain.scopes) {
143
+ const scopeState = domainState.scopes[scope.value];
144
+ if (scopeState) {
145
+ if (scope.value === writeScopeValue || runScopes.some((rs) => rs.value === scope.value)) {
146
+ scopeState.isSelected = true;
147
+ }
148
+ }
149
+ }
150
+ }
151
+ else {
152
+ // Remove all scopes for this domain
153
+ selectedScopes = selectedScopes.filter((scope) => !domain.scopes.some((domainScope) => scope === domainScope.value || scope.startsWith(domainScope.value + ':')));
154
+ }
155
+ }
156
+ function handleIndividualScopeChange(scope, checked) {
157
+ const scopeState = getScopeState(scope.value);
158
+ if (!scopeState)
159
+ return;
160
+ scopeState.isSelected = checked;
161
+ if (scope.requires_resource_path) {
162
+ if (!checked) {
163
+ scopeState.resourcePaths = [];
164
+ updateSelectedScopesForResourcePaths(scope.value, []);
165
+ }
166
+ else {
167
+ const currentPaths = scopeState.resourcePaths || [];
168
+ updateSelectedScopesForResourcePaths(scope.value, currentPaths, false);
169
+ }
170
+ }
171
+ else {
172
+ selectedScopes = selectedScopes.filter((s) => !s.startsWith(scope.value + ':') && s !== scope.value);
173
+ if (checked) {
174
+ selectedScopes = [...selectedScopes, scope.value];
175
+ }
176
+ }
177
+ updateDomainCheckboxState(scope);
178
+ }
179
+ function updateDomainCheckboxState(changedScope) {
180
+ if (!scopeDomains)
181
+ return;
182
+ const domain = scopeDomains.find((d) => d.scopes.some((s) => s.value === changedScope.value));
183
+ if (!domain)
184
+ return;
185
+ const domainState = getDomainState(domain.name);
186
+ if (!domainState)
187
+ return;
188
+ const writeScope = getWriteScopeForDomain(domain);
189
+ const hasWriteSelected = writeScope && domainState.scopes[writeScope]?.isSelected;
190
+ const runScopes = domain.scopes.filter((scope) => scope.value.includes(':run:'));
191
+ const hasRunScopesSelected = runScopes.length === 0 ||
192
+ runScopes.every((runScope) => domainState.scopes[runScope.value]?.isSelected);
193
+ const isDomainFullySelected = hasWriteSelected && hasRunScopesSelected;
194
+ domainState.hasFullAccess = Boolean(isDomainFullySelected);
195
+ }
196
+ function getSelectedScopesForDomain(domain) {
197
+ const domainState = getDomainState(domain.name);
198
+ if (!domainState)
199
+ return [];
200
+ return domain.scopes
201
+ .filter((scope) => domainState.scopes[scope.value]?.isSelected)
202
+ .map((scope) => {
203
+ const scopeState = domainState.scopes[scope.value];
204
+ const resourcePaths = scopeState?.resourcePaths || [];
205
+ return resourcePaths.length > 0 ? `${scope.value}:${resourcePaths.join(',')}` : scope.value;
206
+ });
207
+ }
208
+ function removeSelectedScope(scopeToRemove) {
209
+ selectedScopes = selectedScopes.filter((scope) => scope !== scopeToRemove);
210
+ const baseScopeValue = scopeToRemove.includes(':') && scopeToRemove.split(':').length > 2
211
+ ? scopeToRemove.split(':').slice(0, 2).join(':')
212
+ : scopeToRemove;
213
+ const scopeState = getScopeState(baseScopeValue);
214
+ if (scopeState) {
215
+ if (scopeToRemove.includes(':') && scopeToRemove.split(':').length > 2) {
216
+ const pathPart = scopeToRemove.substring(baseScopeValue.length + 1);
217
+ const pathsToRemove = pathPart.split(',').map((p) => p.trim());
218
+ scopeState.resourcePaths = scopeState.resourcePaths.filter((path) => !pathsToRemove.includes(path));
219
+ if (scopeState.resourcePaths.length === 0) {
220
+ scopeState.isSelected = false;
221
+ }
222
+ }
223
+ else {
224
+ scopeState.isSelected = false;
225
+ scopeState.resourcePaths = [];
226
+ }
227
+ updateDomainCheckboxState({ value: baseScopeValue });
228
+ }
229
+ }
230
+ function clearAllScopes() {
231
+ selectedScopes = [];
232
+ for (const domainState of Object.values(componentState.domains)) {
233
+ domainState.hasFullAccess = false;
234
+ domainState.isExpanded = false;
235
+ for (const scopeState of Object.values(domainState.scopes)) {
236
+ scopeState.isSelected = false;
237
+ scopeState.resourcePaths = [];
238
+ scopeState.currentInputValue = '';
239
+ scopeState.pathError = undefined;
240
+ }
241
+ }
242
+ }
243
+ const hasAdministratorScope = $derived(selectedScopes.includes('*'));
244
+ $effect(() => {
245
+ if (scopeDomains && componentState.domains) {
246
+ syncSelectedScopesWithState();
247
+ }
248
+ });
249
+ function validateResourcePath(path) {
250
+ if (!path.trim())
251
+ return 'Path cannot be empty';
252
+ const trimmedPath = path.trim();
253
+ if (trimmedPath === '*')
254
+ return null;
255
+ if (trimmedPath === 'u/*' || trimmedPath === 'f/*')
256
+ return null;
257
+ if (!trimmedPath.startsWith('u/') && !trimmedPath.startsWith('f/')) {
258
+ return 'Path must start with u/ or f/';
259
+ }
260
+ const parts = trimmedPath.split('/');
261
+ if (parts.length !== 3) {
262
+ return 'Path must have exactly 3 parts: u/{user}/{resource} or f/{folder}/{resource}';
263
+ }
264
+ if (parts[1] === '') {
265
+ return 'Username/folder name cannot be empty';
266
+ }
267
+ if (parts[2] === '') {
268
+ return 'Resource name cannot be empty';
269
+ }
270
+ if (parts[2] === '*')
271
+ return null;
272
+ if (parts[2].includes('*')) {
273
+ return 'Wildcards can only be used as the full resource name (*)';
274
+ }
275
+ return null;
276
+ }
277
+ function addResourcePath(scopeValue, path) {
278
+ const scopeState = getScopeState(scopeValue);
279
+ if (!scopeState)
280
+ return false;
281
+ const error = validateResourcePath(path);
282
+ if (error) {
283
+ scopeState.pathError = error;
284
+ return false;
285
+ }
286
+ scopeState.pathError = undefined;
287
+ if (scopeState.resourcePaths.includes(path.trim())) {
288
+ scopeState.pathError = 'Path already exists';
289
+ return false;
290
+ }
291
+ const newPaths = [...scopeState.resourcePaths, path.trim()];
292
+ scopeState.resourcePaths = newPaths;
293
+ scopeState.currentInputValue = '';
294
+ updateSelectedScopesForResourcePaths(scopeValue, newPaths);
295
+ return true;
296
+ }
297
+ function removeResourcePath(scopeValue, pathToRemove) {
298
+ const scopeState = getScopeState(scopeValue);
299
+ if (!scopeState)
300
+ return;
301
+ const newPaths = scopeState.resourcePaths.filter((p) => p !== pathToRemove);
302
+ scopeState.resourcePaths = newPaths;
303
+ scopeState.pathError = undefined;
304
+ updateSelectedScopesForResourcePaths(scopeValue, newPaths, false);
305
+ }
306
+ function updateSelectedScopesForResourcePaths(scopeValue, paths, removeScope = true) {
307
+ selectedScopes = selectedScopes.filter((s) => !s.startsWith(scopeValue + ':') && s !== scopeValue);
308
+ const scopeState = getScopeState(scopeValue);
309
+ if (!scopeState)
310
+ return;
311
+ if (paths.length > 0 || !removeScope) {
312
+ selectedScopes = [
313
+ ...selectedScopes,
314
+ paths.length > 0 ? `${scopeValue}:${paths.join(',')}` : scopeValue
315
+ ];
316
+ scopeState.isSelected = true;
317
+ }
318
+ else {
319
+ scopeState.isSelected = false;
320
+ }
321
+ updateDomainCheckboxState({ value: scopeValue });
322
+ }
323
+ function syncSelectedScopesWithState() {
324
+ if (!scopeDomains)
325
+ return;
326
+ for (const domain of scopeDomains) {
327
+ const domainState = getDomainState(domain.name);
328
+ if (!domainState)
329
+ continue;
330
+ const writeScopeValue = getWriteScopeForDomain(domain);
331
+ const hasWriteSelected = writeScopeValue &&
332
+ selectedScopes.some((scope) => scope === writeScopeValue || scope.startsWith(writeScopeValue + ':'));
333
+ const runScopes = domain.scopes.filter((scope) => scope.value.includes(':run:'));
334
+ const hasRunScopesSelected = runScopes.length === 0 ||
335
+ runScopes.every((runScope) => selectedScopes.some((scope) => scope === runScope.value || scope.startsWith(runScope.value + ':')));
336
+ domainState.hasFullAccess = Boolean(hasWriteSelected && hasRunScopesSelected);
337
+ for (const scope of domain.scopes) {
338
+ const scopeState = domainState.scopes[scope.value];
339
+ if (!scopeState)
340
+ continue;
341
+ scopeState.resourcePaths = [];
342
+ const isSelected = selectedScopes.some((selected) => {
343
+ if (scope.requires_resource_path && selected.startsWith(scope.value + ':')) {
344
+ const resourcePath = selected.substring(scope.value.length + 1);
345
+ const paths = resourcePath.split(',').map((p) => p.trim());
346
+ scopeState.resourcePaths = [...paths];
347
+ return true;
348
+ }
349
+ return selected === scope.value;
350
+ });
351
+ scopeState.isSelected = isSelected;
352
+ }
353
+ }
354
+ }
355
+ fetchScopeDomains();
356
+ </script>
357
+
358
+ <div class="w-full {className} p-2">
359
+ {#if loading}
360
+ <div class="flex items-center justify-center py-12">
361
+ <Loader2 size={32} class="animate-spin text-primary" />
362
+ </div>
363
+ {:else if error}
364
+ <div class="p-4 bg-red-50 border border-red-200 rounded-lg">
365
+ <p class="text-sm text-red-800 mb-3">{error}</p>
366
+ <Button onclick={fetchScopeDomains} variant="contained" color="red" size="sm">
367
+ Try again
368
+ </Button>
369
+ </div>
370
+ {:else if scopeDomains}
371
+ <div class="mb-6 p-4 bg-surface-secondary border rounded-lg">
372
+ <div class="flex items-center justify-between mb-3">
373
+ <h4 class="text-sm font-semibold text-primary">
374
+ Selected Scopes ({selectedScopes.length})
375
+ </h4>
376
+ <Button onclick={clearAllScopes} {disabled} size="xs" color="light">Clear All</Button>
377
+ </div>
378
+
379
+ {#if selectedScopes.length === 0}
380
+ <p class="text-sm text-tertiary">No scopes selected. Token will have full access.</p>
381
+ {:else if hasAdministratorScope}
382
+ <p class="text-sm text-tertiary">Administrator scope grants full access to all resources.</p
383
+ >
384
+ {:else}
385
+ <div class="flex flex-wrap gap-2">
386
+ {#each selectedScopes.slice(0, 10) as scope}
387
+ <span
388
+ class="inline-flex items-center gap-1 px-1.5 py-0.5 text-xs font-medium bg-blue-100 text-blue-800 rounded font-mono"
389
+ >
390
+ {scope}
391
+ <button
392
+ type="button"
393
+ onclick={() => removeSelectedScope(scope)}
394
+ class="text-blue-600 hover:text-blue-800 flex-shrink-0"
395
+ title="Remove scope"
396
+ {disabled}
397
+ >
398
+ <X size={10} />
399
+ </button>
400
+ </span>
401
+ {/each}
402
+ {#if selectedScopes.length > 10}
403
+ <span
404
+ class="inline-flex items-center px-2.5 py-0.5 text-xs font-medium bg-surface text-secondary rounded"
405
+ >
406
+ +{selectedScopes.length - 10} more
407
+ </span>
408
+ {/if}
409
+ </div>
410
+ {/if}
411
+ </div>
412
+
413
+ <div class="space-y-3 max-h-96 overflow-y-auto border rounded-lg p-4">
414
+ {#each scopeDomains as domain}
415
+ {@const domainState = getDomainState(domain.name)}
416
+ {@const isExpanded = domainState?.isExpanded || false}
417
+ {@const isDomainSelected = domainState?.hasFullAccess || false}
418
+ {@const selectedScopes = getSelectedScopesForDomain(domain)}
419
+
420
+ <div class="border rounded-lg bg-surface overflow-hidden">
421
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
422
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
423
+ <div
424
+ class="p-4 bg-surface-secondary cursor-pointer hover:bg-surface-tertiary transition-colors"
425
+ onclick={() => toggleDomainExpansion(domain.name)}
426
+ >
427
+ <div class="flex items-center gap-3">
428
+ <div class="flex-shrink-0">
429
+ <ChevronRight
430
+ size={16}
431
+ class="text-secondary transition-transform duration-200 {isExpanded
432
+ ? 'rotate-90'
433
+ : ''}"
434
+ />
435
+ </div>
436
+
437
+ <div class="flex-shrink-0">
438
+ <input
439
+ type="checkbox"
440
+ id={`domain-${domain.name}`}
441
+ checked={isDomainSelected}
442
+ {disabled}
443
+ onchange={(e) => handleDomainCheckboxChange(domain, e.currentTarget.checked)}
444
+ onclick={(e) => e.stopPropagation()}
445
+ class="!w-4 !h-4 cursor-pointer"
446
+ />
447
+ </div>
448
+
449
+ <div class="flex-1 min-w-0">
450
+ <div class="flex items-center gap-2 flex-wrap">
451
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
452
+ <label
453
+ for={`domain-${domain.name}`}
454
+ class="text-sm font-semibold text-primary cursor-pointer"
455
+ onclick={(e) => e.stopPropagation()}
456
+ >
457
+ {domain.name}
458
+ </label>
459
+ {#each selectedScopes as scope}
460
+ <span
461
+ class="inline-flex items-center gap-1 px-1.5 py-0.5 text-xs font-medium bg-blue-100 text-blue-800 rounded font-mono"
462
+ >
463
+ {scope}
464
+ <button
465
+ type="button"
466
+ onclick={(e) => {
467
+ e.stopPropagation()
468
+ removeSelectedScope(scope)
469
+ }}
470
+ class="text-blue-600 hover:text-blue-800 flex-shrink-0"
471
+ title="Remove scope"
472
+ {disabled}
473
+ >
474
+ <X size={10} />
475
+ </button>
476
+ </span>
477
+ {/each}
478
+ </div>
479
+ {#if domain.description}
480
+ <p class="text-xs text-tertiary mt-0.5">{domain.description}</p>
481
+ {/if}
482
+ </div>
483
+ </div>
484
+ </div>
485
+
486
+ {#if isExpanded}
487
+ <div class="p-2">
488
+ <div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
489
+ {#each domain.scopes as scope}
490
+ {@const scopeState = domainState?.scopes[scope.value]}
491
+ {@const isSelected = scopeState?.isSelected || false}
492
+ {@const resourcePathArray = scopeState?.resourcePaths || []}
493
+ {@const currentInput = scopeState?.currentInputValue || ''}
494
+ {@const pathError = scopeState?.pathError}
495
+ {@const isDisabled = disabled || isScopeDisabled(scope, domain)}
496
+
497
+ <div
498
+ class="p-3 border rounded-lg w-full {isDisabled
499
+ ? 'bg-surface opacity-60'
500
+ : 'bg-surface-secondary'}"
501
+ >
502
+ <div class="flex justify-between items-center mb-2">
503
+ <label
504
+ class={twMerge(
505
+ 'flex items-center gap-2 flex-1 min-w-0',
506
+ isDisabled ? 'cursor-not-allowed' : 'cursor-pointer'
507
+ )}
508
+ >
509
+ <input
510
+ type="checkbox"
511
+ checked={isSelected}
512
+ disabled={isDisabled}
513
+ onchange={(e) =>
514
+ handleIndividualScopeChange(scope, e.currentTarget.checked)}
515
+ class="!w-4 !h-4 flex-shrink-0"
516
+ />
517
+
518
+ <span
519
+ class={twMerge(
520
+ 'font-medium text-xs truncate cursor-pointer',
521
+ isDisabled ? 'text-tertiary' : ''
522
+ )}
523
+ >
524
+ {scope.label}
525
+ </span>
526
+ </label>
527
+ <div class="flex-shrink-0">
528
+ {#if scope.requires_resource_path}
529
+ <Popover
530
+ disabled={isDisabled}
531
+ closeOnOtherPopoverOpen
532
+ contentClasses="p-3"
533
+ >
534
+ {#snippet trigger()}
535
+ <Button size="xs" disabled={isDisabled} color="dark">
536
+ Restrict paths
537
+ <Tooltip light>
538
+ Restrict this scope to specific resource paths. If no paths are
539
+ specified, the scope gives full access.
540
+ </Tooltip>
541
+ </Button>
542
+ {/snippet}
543
+ {#snippet content()}
544
+ <div class="w-80">
545
+ <div class="flex gap-2">
546
+ <input
547
+ type="text"
548
+ value={currentInput}
549
+ {disabled}
550
+ oninput={(e) => {
551
+ if (scopeState) {
552
+ scopeState.currentInputValue = e.currentTarget.value
553
+ scopeState.pathError = undefined
554
+ }
555
+ }}
556
+ placeholder="e.g. f/folder/*, u/user/path"
557
+ onkeydown={(e) => {
558
+ if (e.key === 'Enter' && currentInput.trim()) {
559
+ e.preventDefault()
560
+ addResourcePath(scope.value, currentInput)
561
+ }
562
+ }}
563
+ class="flex-1 text-sm px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-surface"
564
+ />
565
+ <Button
566
+ onclick={() => {
567
+ addResourcePath(scope.value, currentInput)
568
+ }}
569
+ size="xs"
570
+ disabled={!currentInput.trim()}
571
+ >
572
+ Add
573
+ </Button>
574
+ </div>
575
+ {#if pathError}
576
+ <p class="text-xs text-red-600 mt-1">{pathError}</p>
577
+ {/if}
578
+ </div>
579
+ {/snippet}
580
+ </Popover>
581
+ {/if}
582
+ </div>
583
+ </div>
584
+
585
+ {#if scope.requires_resource_path && resourcePathArray.length > 0}
586
+ <div class="flex flex-wrap gap-1 mt-2">
587
+ {#each resourcePathArray as path}
588
+ <span
589
+ class="inline-flex items-center gap-1 px-1.5 py-0.5 text-xs font-medium bg-blue-100 text-blue-800 rounded font-mono"
590
+ >
591
+ {path}
592
+ <button
593
+ type="button"
594
+ onclick={() => removeResourcePath(scope.value, path)}
595
+ class="text-blue-600 hover:text-blue-800 flex-shrink-0"
596
+ title="Remove path"
597
+ >
598
+ <X size={10} />
599
+ </button>
600
+ </span>
601
+ {/each}
602
+ </div>
603
+ {/if}
604
+ </div>
605
+ {/each}
606
+ </div>
607
+ </div>
608
+ {/if}
609
+ </div>
610
+ {/each}
611
+ </div>
612
+ {/if}
613
+ </div>
@@ -0,0 +1,8 @@
1
+ interface Props {
2
+ selectedScopes?: string[];
3
+ disabled?: boolean;
4
+ class?: string;
5
+ }
6
+ declare const ScopeSelector: import("svelte").Component<Props, {}, "selectedScopes">;
7
+ type ScopeSelector = ReturnType<typeof ScopeSelector>;
8
+ export default ScopeSelector;