@synopackageland/cli 0.4.0 → 0.4.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.
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/build-spk/generator.js +3 -0
- package/dist/build-spk/notices.d.ts +5 -0
- package/dist/build-spk/notices.js +60 -0
- package/dist/cli.js +9 -2
- package/dist/error-registry.js +20 -0
- package/dist/init.d.ts +4 -0
- package/dist/init.js +20 -12
- package/dist/standalone/build.js +3 -1
- package/dist/standalone/daemon.d.ts +23 -0
- package/dist/standalone/daemon.js +114 -0
- package/dist/standalone/dsm-window.d.ts +1 -1
- package/dist/standalone/dsm-window.js +50 -10
- package/dist/standalone/helper-inventory.d.ts +16 -0
- package/dist/standalone/helper-inventory.js +2 -0
- package/dist/standalone/helper.d.ts +2 -12
- package/dist/standalone/helper.js +8 -7
- package/dist/standalone/host-adapter.d.ts +5 -0
- package/dist/standalone/host-adapter.js +148 -0
- package/dist/standalone/host-platform.d.ts +13 -0
- package/dist/standalone/host-platform.js +42 -0
- package/dist/standalone/host-types.d.ts +49 -0
- package/dist/standalone/host-types.js +1 -0
- package/dist/standalone/inspect-helper.d.ts +6 -0
- package/dist/standalone/inspect-helper.js +88 -0
- package/dist/standalone/inspect-spk.d.ts +2 -0
- package/dist/standalone/inspect-spk.js +49 -0
- package/dist/standalone/lifecycle-helper.d.ts +2 -0
- package/dist/standalone/lifecycle-helper.js +12 -0
- package/dist/standalone/lifecycle.d.ts +3 -0
- package/dist/standalone/lifecycle.js +53 -0
- package/dist/standalone/manifest.d.ts +2 -0
- package/dist/standalone/manifest.js +4 -1
- package/dist/standalone/reopen-events.d.ts +2 -0
- package/dist/standalone/reopen-events.js +28 -0
- package/dist/standalone/root-messages.js +9 -0
- package/dist/standalone/root-window.d.ts +4 -3
- package/dist/standalone/root-window.js +17 -5
- package/dist/standalone/spk.d.ts +4 -3
- package/dist/standalone/spk.js +20 -33
- package/dist/template-paths.d.ts +2 -0
- package/dist/template-paths.js +9 -0
- package/dist/templates/_daemon/fixture_linux_test.go +15 -0
- package/dist/templates/_daemon/fixture_other_test.go +7 -0
- package/dist/templates/_daemon/go.mod +3 -0
- package/dist/templates/_daemon/main.go +608 -0
- package/dist/templates/_daemon/main_test.go +242 -0
- package/dist/templates/_daemon/process_linux.go +55 -0
- package/dist/templates/_daemon/process_linux_test.go +161 -0
- package/dist/templates/_daemon/process_other.go +14 -0
- package/dist/templates/_daemon/testdata/child.go +41 -0
- package/dist/templates/_shared/GO-LICENSE +27 -0
- package/dist/templates/_shared/GO-PATENTS +22 -0
- package/dist/templates/_shared/LICENSES/TEMPLATE.txt +21 -0
- package/dist/templates/_shared/notice-overrides/index.json +14 -0
- package/dist/templates/_shared/notice-overrides/utils-0.4.2-LICENSE +21 -0
- package/dist/templates/_shared/notice-overrides/utils-0.5.0-LICENSE +21 -0
- package/dist/templates/_shared/third-party-notices.mjs +51 -0
- package/dist/templates/native-go/README.md +57 -1
- package/dist/templates/native-go/backend/THIRD_PARTY_NOTICES.txt +59 -0
- package/dist/templates/native-go/backend/cmd/dsm-readonly-probe/main_linux.go +46 -0
- package/dist/templates/native-go/backend/go.mod +4 -0
- package/dist/templates/native-go/backend/go.sum +4 -0
- package/dist/templates/native-go/backend/internal/api/binary.go +154 -0
- package/dist/templates/native-go/backend/internal/api/binary_test.go +165 -0
- package/dist/templates/native-go/backend/internal/api/registry.go +14 -4
- package/dist/templates/native-go/backend/internal/api/server.go +4 -0
- package/dist/templates/native-go/backend/internal/app/nas_cgi_artifact_test.go +121 -0
- package/dist/templates/native-go/backend/internal/app/nas_refusal_test.go +46 -0
- package/dist/templates/native-go/backend/internal/app/routes.go +11 -0
- package/dist/templates/native-go/backend/internal/app/routes_test.go +10 -5
- package/dist/templates/native-go/backend/internal/dsmprobe/collector_linux.go +335 -0
- package/dist/templates/native-go/backend/internal/dsmprobe/collector_linux_test.go +41 -0
- package/dist/templates/native-go/backend/internal/dsmprobe/protocol.go +179 -0
- package/dist/templates/native-go/backend/internal/dsmprobe/protocol_test.go +29 -0
- package/dist/templates/native-go/backend/internal/helper/cohort.go +393 -0
- package/dist/templates/native-go/backend/internal/helper/cohort_publication.go +18 -0
- package/dist/templates/native-go/backend/internal/helper/cohort_publication_test.go +105 -0
- package/dist/templates/native-go/backend/internal/helper/cohort_test.go +372 -0
- package/dist/templates/native-go/backend/internal/helper/control.go +226 -21
- package/dist/templates/native-go/backend/internal/helper/control_test.go +10 -2
- package/dist/templates/native-go/backend/internal/helper/data_contract.go +242 -0
- package/dist/templates/native-go/backend/internal/helper/data_contract_test.go +51 -0
- package/dist/templates/native-go/backend/internal/helper/data_decode.go +184 -0
- package/dist/templates/native-go/backend/internal/helper/data_decode_pointer_test.go +30 -0
- package/dist/templates/native-go/backend/internal/helper/data_protocol.go +416 -0
- package/dist/templates/native-go/backend/internal/helper/data_protocol_test.go +449 -0
- package/dist/templates/native-go/backend/internal/helper/dsm_acl_linux.go +139 -0
- package/dist/templates/native-go/backend/internal/helper/dsm_acl_linux_test.go +121 -0
- package/dist/templates/native-go/backend/internal/helper/dsm_acl_syscall_linux.go +95 -0
- package/dist/templates/native-go/backend/internal/helper/dsm_acl_syscall_linux_test.go +56 -0
- package/dist/templates/native-go/backend/internal/helper/dsm_bridge_linux.go +144 -0
- package/dist/templates/native-go/backend/internal/helper/dsm_bridge_linux_test.go +89 -0
- package/dist/templates/native-go/backend/internal/helper/dsm_home_linux.go +107 -0
- package/dist/templates/native-go/backend/internal/helper/dsm_home_linux_test.go +82 -0
- package/dist/templates/native-go/backend/internal/helper/dsm_probe_transport_linux.go +151 -0
- package/dist/templates/native-go/backend/internal/helper/dsm_probe_transport_linux_test.go +66 -0
- package/dist/templates/native-go/backend/internal/helper/grant_authority.go +516 -0
- package/dist/templates/native-go/backend/internal/helper/grant_authority_contract.go +88 -0
- package/dist/templates/native-go/backend/internal/helper/grant_authority_test.go +510 -0
- package/dist/templates/native-go/backend/internal/helper/grant_cohort.go +389 -0
- package/dist/templates/native-go/backend/internal/helper/grant_cohort_test.go +239 -0
- package/dist/templates/native-go/backend/internal/helper/grant_delivery.go +49 -0
- package/dist/templates/native-go/backend/internal/helper/grant_delivery_methods.go +273 -0
- package/dist/templates/native-go/backend/internal/helper/grant_delivery_test.go +322 -0
- package/dist/templates/native-go/backend/internal/helper/grant_operation_contract.go +47 -0
- package/dist/templates/native-go/backend/internal/helper/grant_readonly_test.go +180 -0
- package/dist/templates/native-go/backend/internal/helper/grant_repository_linux.go +415 -0
- package/dist/templates/native-go/backend/internal/helper/grant_repository_linux_test.go +468 -0
- package/dist/templates/native-go/backend/internal/helper/grant_rpc.go +147 -0
- package/dist/templates/native-go/backend/internal/helper/grant_rpc_test.go +51 -0
- package/dist/templates/native-go/backend/internal/helper/grant_snapshot.go +75 -0
- package/dist/templates/native-go/backend/internal/helper/grant_supervisor_linux.go +134 -0
- package/dist/templates/native-go/backend/internal/helper/grant_supervisor_linux_test.go +254 -0
- package/dist/templates/native-go/backend/internal/helper/lease.go +112 -0
- package/dist/templates/native-go/backend/internal/helper/lease_linux.go +44 -0
- package/dist/templates/native-go/backend/internal/helper/lease_other.go +8 -0
- package/dist/templates/native-go/backend/internal/helper/lifecycle.go +234 -0
- package/dist/templates/native-go/backend/internal/helper/lifecycle_test.go +473 -0
- package/dist/templates/native-go/backend/internal/helper/managed_pilot.go +284 -0
- package/dist/templates/native-go/backend/internal/helper/managed_pilot_repository_linux.go +74 -0
- package/dist/templates/native-go/backend/internal/helper/managed_pilot_repository_linux_test.go +40 -0
- package/dist/templates/native-go/backend/internal/helper/managed_pilot_test.go +210 -0
- package/dist/templates/native-go/backend/internal/helper/nas_shipped_refusal_linux_test.go +97 -0
- package/dist/templates/native-go/backend/internal/helper/native_discovery.go +79 -0
- package/dist/templates/native-go/backend/internal/helper/native_identity.go +89 -0
- package/dist/templates/native-go/backend/internal/helper/native_provider.go +159 -0
- package/dist/templates/native-go/backend/internal/helper/native_provider_test.go +421 -0
- package/dist/templates/native-go/backend/internal/helper/observation.go +204 -0
- package/dist/templates/native-go/backend/internal/helper/observation_health_test.go +115 -0
- package/dist/templates/native-go/backend/internal/helper/observation_reload_budget_test.go +117 -0
- package/dist/templates/native-go/backend/internal/helper/observation_test.go +196 -0
- package/dist/templates/native-go/backend/internal/helper/observer_diagnostic.go +109 -0
- package/dist/templates/native-go/backend/internal/helper/observer_diagnostic_linux.go +76 -0
- package/dist/templates/native-go/backend/internal/helper/observer_diagnostic_linux_test.go +124 -0
- package/dist/templates/native-go/backend/internal/helper/observer_diagnostic_test.go +98 -0
- package/dist/templates/native-go/backend/internal/helper/operation_bootstrap_linux.go +193 -0
- package/dist/templates/native-go/backend/internal/helper/operation_credentials_linux.go +56 -0
- package/dist/templates/native-go/backend/internal/helper/operation_linux.go +480 -0
- package/dist/templates/native-go/backend/internal/helper/operation_linux_test.go +528 -0
- package/dist/templates/native-go/backend/internal/helper/process_linux.go +11 -1
- package/dist/templates/native-go/backend/internal/helper/publication_test.go +178 -0
- package/dist/templates/native-go/backend/internal/helper/read_stream_cohort_linux_test.go +156 -0
- package/dist/templates/native-go/backend/internal/helper/read_transfer.go +40 -0
- package/dist/templates/native-go/backend/internal/helper/read_transfer_linux.go +109 -0
- package/dist/templates/native-go/backend/internal/helper/read_transfer_linux_test.go +118 -0
- package/dist/templates/native-go/backend/internal/helper/read_transfer_test.go +177 -0
- package/dist/templates/native-go/backend/internal/helper/readonly.go +105 -0
- package/dist/templates/native-go/backend/internal/helper/readonly_admission.go +85 -0
- package/dist/templates/native-go/backend/internal/helper/readonly_cohort_linux_test.go +253 -0
- package/dist/templates/native-go/backend/internal/helper/readonly_linux_test.go +378 -0
- package/dist/templates/native-go/backend/internal/helper/readonly_mount_linux.go +65 -0
- package/dist/templates/native-go/backend/internal/helper/readonly_mount_proxy_linux.go +113 -0
- package/dist/templates/native-go/backend/internal/helper/readonly_operations_linux.go +168 -0
- package/dist/templates/native-go/backend/internal/helper/readonly_resolver_linux.go +241 -0
- package/dist/templates/native-go/backend/internal/helper/readonly_test.go +40 -0
- package/dist/templates/native-go/backend/internal/helper/relaxed_identity.go +252 -0
- package/dist/templates/native-go/backend/internal/helper/relaxed_identity_contract.go +105 -0
- package/dist/templates/native-go/backend/internal/helper/relaxed_identity_test.go +406 -0
- package/dist/templates/native-go/backend/internal/helper/relaxed_invalidation.go +200 -0
- package/dist/templates/native-go/backend/internal/helper/repair.go +132 -0
- package/dist/templates/native-go/backend/internal/helper/repair_test.go +271 -0
- package/dist/templates/native-go/backend/internal/helper/s1r_actor_acl_linux_test.go +303 -0
- package/dist/templates/native-go/backend/internal/helper/s1r_actor_fixture_linux_test.go +184 -0
- package/dist/templates/native-go/backend/internal/helper/s1r_actor_races_linux_test.go +231 -0
- package/dist/templates/native-go/backend/internal/helper/s1r_fixture_contract_linux_test.go +18 -0
- package/dist/templates/native-go/backend/internal/helper/server_linux.go +44 -3
- package/dist/templates/native-go/backend/internal/helper/status.go +74 -11
- package/dist/templates/native-go/backend/internal/helper/status_test.go +83 -0
- package/dist/templates/native-go/backend/internal/helper/supervision.go +28 -5
- package/dist/templates/native-go/backend/internal/helper/supervisor_frame.go +76 -0
- package/dist/templates/native-go/backend/internal/helper/supervisor_frame_linux_test.go +69 -0
- package/dist/templates/native-go/backend/internal/helper/supervisor_frame_test.go +107 -0
- package/dist/templates/native-go/backend/internal/helper/supervisor_linux.go +187 -16
- package/dist/templates/native-go/backend/internal/helper/supervisor_linux_test.go +618 -7
- package/dist/templates/native-go/backend/internal/helper/supervisor_socket_eof_test.go +74 -0
- package/dist/templates/native-go/backend/internal/helper/supervisor_socket_test.go +45 -0
- package/dist/templates/native-go/backend/internal/helper/testdata/protocol2/create.json +23 -0
- package/dist/templates/native-go/backend/internal/helper/testdata/protocol2/frames.json +67 -0
- package/dist/templates/native-go/backend/internal/helper/testdata/protocol2/grant-store.json +52 -0
- package/dist/templates/native-go/backend/internal/helper/testdata/protocol2/intent.json +20 -0
- package/dist/templates/native-go/backend/internal/helper/testdata/protocol2/list.json +20 -0
- package/dist/templates/native-go/backend/internal/helper/testdata/protocol2/publish.json +20 -0
- package/dist/templates/native-go/backend/internal/helper/testdata/protocol2/read.json +22 -0
- package/dist/templates/native-go/backend/internal/helper/testdata/protocol2/stat.json +22 -0
- package/dist/templates/native-go/backend/internal/helper/testdata/protocol2/write-stream.json +22 -0
- package/dist/templates/native-go/backend/internal/helper/unit_connection.go +75 -0
- package/dist/templates/native-go/backend/internal/helper/unit_connection_linux_test.go +135 -0
- package/dist/templates/native-go/backend/internal/helper/unit_connection_test.go +133 -0
- package/dist/templates/native-go/backend/internal/helper/unit_frame_test.go +32 -0
- package/dist/templates/native-go/backend/internal/helper/unit_linux.go +364 -0
- package/dist/templates/native-go/backend/internal/helper/unit_linux_test.go +46 -0
- package/dist/templates/native-go/backend/internal/helper/unit_mechanisms_linux_test.go +217 -0
- package/dist/templates/native-go/backend/internal/helper/unit_padding_linux_test.go +178 -0
- package/dist/templates/native-go/backend/internal/helper/unit_query_linux.go +111 -0
- package/dist/templates/native-go/backend/internal/helper/unit_reload_linux_test.go +372 -0
- package/dist/templates/native-go/backend/internal/helper/unit_reload_sequence_linux_test.go +232 -0
- package/dist/templates/native-go/backend/internal/helper/write.go +77 -0
- package/dist/templates/native-go/backend/internal/helper/write_admission.go +260 -0
- package/dist/templates/native-go/backend/internal/helper/write_admission_test.go +135 -0
- package/dist/templates/native-go/backend/internal/helper/write_cohort_linux_test.go +218 -0
- package/dist/templates/native-go/backend/internal/helper/write_files_linux.go +202 -0
- package/dist/templates/native-go/backend/internal/helper/write_files_linux_test.go +199 -0
- package/dist/templates/native-go/backend/internal/helper/write_intent_linux.go +116 -0
- package/dist/templates/native-go/backend/internal/helper/write_intent_linux_test.go +74 -0
- package/dist/templates/native-go/backend/internal/helper/write_operation_linux.go +245 -0
- package/dist/templates/native-go/backend/internal/helper/write_test.go +53 -0
- package/dist/templates/native-go/ui/app.js +8 -1
- package/dist/templates/native-go-better-auth/node/build.mjs +2 -0
- package/dist/templates/native-go-vue/README.md +115 -1
- package/dist/templates/native-go-vue/frontend/DESIGN.md +1 -1
- package/dist/templates/native-go-vue/frontend/package-lock.json +132 -229
- package/dist/templates/native-go-vue/frontend/package.json +1 -1
- package/dist/templates/native-go-vue/frontend/src/App.vue +33 -6
- package/dist/templates/native-go-vue/frontend/src/context.ts +41 -2
- package/dist/templates/native-go-vue/frontend/src/main.ts +4 -1
- package/dist/templates/native-go-vue/frontend/src/nas/NasGrantSettings.vue +74 -0
- package/dist/templates/native-go-vue/frontend/src/nas/NasPathPicker.vue +106 -0
- package/dist/templates/native-go-vue/frontend/src/nas/NasUnavailable.vue +20 -0
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/de.json +74 -0
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/en.json +74 -0
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/es.json +74 -0
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/fr.json +74 -0
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/it.json +74 -0
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/ja.json +74 -0
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/ko.json +74 -0
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/zh-CN.json +74 -0
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/zh-TW.json +74 -0
- package/dist/templates/native-go-vue/frontend/src/nas/i18n.ts +38 -0
- package/dist/templates/native-go-vue/frontend/src/nas/mock-provider.ts +119 -0
- package/dist/templates/native-go-vue/frontend/src/nas/provider.ts +59 -0
- package/dist/templates/native-go-vue/frontend/src/nas/use-request.ts +27 -0
- package/dist/templates/native-go-vue/frontend/src/style.css +17 -0
- package/dist/templates/native-go-vue/frontend/test/apps.test.ts +44 -2
- package/dist/templates/native-go-vue/frontend/test/nas-provider.test.ts +81 -0
- package/dist/templates/native-go-vue/frontend/test/nas-request.test.ts +21 -0
- package/dist/templates/native-go-vue/frontend/test/nas-ui.test.ts +261 -0
- package/dist/templates/native-go-vue/frontend/tsconfig.json +1 -1
- package/dist/templates/native-go-vue/frontend/vite.config.ts +6 -4
- package/package.json +3 -2
- package/templates/_daemon/fixture_linux_test.go +15 -0
- package/templates/_daemon/fixture_other_test.go +7 -0
- package/templates/_daemon/go.mod +3 -0
- package/templates/_daemon/main.go +608 -0
- package/templates/_daemon/main_test.go +242 -0
- package/templates/_daemon/process_linux.go +55 -0
- package/templates/_daemon/process_linux_test.go +161 -0
- package/templates/_daemon/process_other.go +14 -0
- package/templates/_daemon/testdata/child.go +41 -0
- package/templates/_shared/GO-LICENSE +27 -0
- package/templates/_shared/GO-PATENTS +22 -0
- package/templates/_shared/LICENSES/TEMPLATE.txt +21 -0
- package/templates/_shared/notice-overrides/index.json +14 -0
- package/templates/_shared/notice-overrides/utils-0.4.2-LICENSE +21 -0
- package/templates/_shared/notice-overrides/utils-0.5.0-LICENSE +21 -0
- package/templates/_shared/third-party-notices.mjs +51 -0
- package/templates/native-go/README.md +57 -1
- package/templates/native-go/backend/THIRD_PARTY_NOTICES.txt +59 -0
- package/templates/native-go/backend/cmd/dsm-readonly-probe/main_linux.go +46 -0
- package/templates/native-go/backend/go.mod +4 -0
- package/templates/native-go/backend/go.sum +4 -0
- package/templates/native-go/backend/internal/api/binary.go +154 -0
- package/templates/native-go/backend/internal/api/binary_test.go +165 -0
- package/templates/native-go/backend/internal/api/registry.go +14 -4
- package/templates/native-go/backend/internal/api/server.go +4 -0
- package/templates/native-go/backend/internal/app/nas_cgi_artifact_test.go +121 -0
- package/templates/native-go/backend/internal/app/nas_refusal_test.go +46 -0
- package/templates/native-go/backend/internal/app/routes.go +11 -0
- package/templates/native-go/backend/internal/app/routes_test.go +10 -5
- package/templates/native-go/backend/internal/dsmprobe/collector_linux.go +335 -0
- package/templates/native-go/backend/internal/dsmprobe/collector_linux_test.go +41 -0
- package/templates/native-go/backend/internal/dsmprobe/protocol.go +179 -0
- package/templates/native-go/backend/internal/dsmprobe/protocol_test.go +29 -0
- package/templates/native-go/backend/internal/helper/cohort.go +393 -0
- package/templates/native-go/backend/internal/helper/cohort_publication.go +18 -0
- package/templates/native-go/backend/internal/helper/cohort_publication_test.go +105 -0
- package/templates/native-go/backend/internal/helper/cohort_test.go +372 -0
- package/templates/native-go/backend/internal/helper/control.go +226 -21
- package/templates/native-go/backend/internal/helper/control_test.go +10 -2
- package/templates/native-go/backend/internal/helper/data_contract.go +242 -0
- package/templates/native-go/backend/internal/helper/data_contract_test.go +51 -0
- package/templates/native-go/backend/internal/helper/data_decode.go +184 -0
- package/templates/native-go/backend/internal/helper/data_decode_pointer_test.go +30 -0
- package/templates/native-go/backend/internal/helper/data_protocol.go +416 -0
- package/templates/native-go/backend/internal/helper/data_protocol_test.go +449 -0
- package/templates/native-go/backend/internal/helper/dsm_acl_linux.go +139 -0
- package/templates/native-go/backend/internal/helper/dsm_acl_linux_test.go +121 -0
- package/templates/native-go/backend/internal/helper/dsm_acl_syscall_linux.go +95 -0
- package/templates/native-go/backend/internal/helper/dsm_acl_syscall_linux_test.go +56 -0
- package/templates/native-go/backend/internal/helper/dsm_bridge_linux.go +144 -0
- package/templates/native-go/backend/internal/helper/dsm_bridge_linux_test.go +89 -0
- package/templates/native-go/backend/internal/helper/dsm_home_linux.go +107 -0
- package/templates/native-go/backend/internal/helper/dsm_home_linux_test.go +82 -0
- package/templates/native-go/backend/internal/helper/dsm_probe_transport_linux.go +151 -0
- package/templates/native-go/backend/internal/helper/dsm_probe_transport_linux_test.go +66 -0
- package/templates/native-go/backend/internal/helper/grant_authority.go +516 -0
- package/templates/native-go/backend/internal/helper/grant_authority_contract.go +88 -0
- package/templates/native-go/backend/internal/helper/grant_authority_test.go +510 -0
- package/templates/native-go/backend/internal/helper/grant_cohort.go +389 -0
- package/templates/native-go/backend/internal/helper/grant_cohort_test.go +239 -0
- package/templates/native-go/backend/internal/helper/grant_delivery.go +49 -0
- package/templates/native-go/backend/internal/helper/grant_delivery_methods.go +273 -0
- package/templates/native-go/backend/internal/helper/grant_delivery_test.go +322 -0
- package/templates/native-go/backend/internal/helper/grant_operation_contract.go +47 -0
- package/templates/native-go/backend/internal/helper/grant_readonly_test.go +180 -0
- package/templates/native-go/backend/internal/helper/grant_repository_linux.go +415 -0
- package/templates/native-go/backend/internal/helper/grant_repository_linux_test.go +468 -0
- package/templates/native-go/backend/internal/helper/grant_rpc.go +147 -0
- package/templates/native-go/backend/internal/helper/grant_rpc_test.go +51 -0
- package/templates/native-go/backend/internal/helper/grant_snapshot.go +75 -0
- package/templates/native-go/backend/internal/helper/grant_supervisor_linux.go +134 -0
- package/templates/native-go/backend/internal/helper/grant_supervisor_linux_test.go +254 -0
- package/templates/native-go/backend/internal/helper/lease.go +112 -0
- package/templates/native-go/backend/internal/helper/lease_linux.go +44 -0
- package/templates/native-go/backend/internal/helper/lease_other.go +8 -0
- package/templates/native-go/backend/internal/helper/lifecycle.go +234 -0
- package/templates/native-go/backend/internal/helper/lifecycle_test.go +473 -0
- package/templates/native-go/backend/internal/helper/managed_pilot.go +284 -0
- package/templates/native-go/backend/internal/helper/managed_pilot_repository_linux.go +74 -0
- package/templates/native-go/backend/internal/helper/managed_pilot_repository_linux_test.go +40 -0
- package/templates/native-go/backend/internal/helper/managed_pilot_test.go +210 -0
- package/templates/native-go/backend/internal/helper/nas_shipped_refusal_linux_test.go +97 -0
- package/templates/native-go/backend/internal/helper/native_discovery.go +79 -0
- package/templates/native-go/backend/internal/helper/native_identity.go +89 -0
- package/templates/native-go/backend/internal/helper/native_provider.go +159 -0
- package/templates/native-go/backend/internal/helper/native_provider_test.go +421 -0
- package/templates/native-go/backend/internal/helper/observation.go +204 -0
- package/templates/native-go/backend/internal/helper/observation_health_test.go +115 -0
- package/templates/native-go/backend/internal/helper/observation_reload_budget_test.go +117 -0
- package/templates/native-go/backend/internal/helper/observation_test.go +196 -0
- package/templates/native-go/backend/internal/helper/observer_diagnostic.go +109 -0
- package/templates/native-go/backend/internal/helper/observer_diagnostic_linux.go +76 -0
- package/templates/native-go/backend/internal/helper/observer_diagnostic_linux_test.go +124 -0
- package/templates/native-go/backend/internal/helper/observer_diagnostic_test.go +98 -0
- package/templates/native-go/backend/internal/helper/operation_bootstrap_linux.go +193 -0
- package/templates/native-go/backend/internal/helper/operation_credentials_linux.go +56 -0
- package/templates/native-go/backend/internal/helper/operation_linux.go +480 -0
- package/templates/native-go/backend/internal/helper/operation_linux_test.go +528 -0
- package/templates/native-go/backend/internal/helper/process_linux.go +11 -1
- package/templates/native-go/backend/internal/helper/publication_test.go +178 -0
- package/templates/native-go/backend/internal/helper/read_stream_cohort_linux_test.go +156 -0
- package/templates/native-go/backend/internal/helper/read_transfer.go +40 -0
- package/templates/native-go/backend/internal/helper/read_transfer_linux.go +109 -0
- package/templates/native-go/backend/internal/helper/read_transfer_linux_test.go +118 -0
- package/templates/native-go/backend/internal/helper/read_transfer_test.go +177 -0
- package/templates/native-go/backend/internal/helper/readonly.go +105 -0
- package/templates/native-go/backend/internal/helper/readonly_admission.go +85 -0
- package/templates/native-go/backend/internal/helper/readonly_cohort_linux_test.go +253 -0
- package/templates/native-go/backend/internal/helper/readonly_linux_test.go +378 -0
- package/templates/native-go/backend/internal/helper/readonly_mount_linux.go +65 -0
- package/templates/native-go/backend/internal/helper/readonly_mount_proxy_linux.go +113 -0
- package/templates/native-go/backend/internal/helper/readonly_operations_linux.go +168 -0
- package/templates/native-go/backend/internal/helper/readonly_resolver_linux.go +241 -0
- package/templates/native-go/backend/internal/helper/readonly_test.go +40 -0
- package/templates/native-go/backend/internal/helper/relaxed_identity.go +252 -0
- package/templates/native-go/backend/internal/helper/relaxed_identity_contract.go +105 -0
- package/templates/native-go/backend/internal/helper/relaxed_identity_test.go +406 -0
- package/templates/native-go/backend/internal/helper/relaxed_invalidation.go +200 -0
- package/templates/native-go/backend/internal/helper/repair.go +132 -0
- package/templates/native-go/backend/internal/helper/repair_test.go +271 -0
- package/templates/native-go/backend/internal/helper/s1r_actor_acl_linux_test.go +303 -0
- package/templates/native-go/backend/internal/helper/s1r_actor_fixture_linux_test.go +184 -0
- package/templates/native-go/backend/internal/helper/s1r_actor_races_linux_test.go +231 -0
- package/templates/native-go/backend/internal/helper/s1r_fixture_contract_linux_test.go +18 -0
- package/templates/native-go/backend/internal/helper/server_linux.go +44 -3
- package/templates/native-go/backend/internal/helper/status.go +74 -11
- package/templates/native-go/backend/internal/helper/status_test.go +83 -0
- package/templates/native-go/backend/internal/helper/supervision.go +28 -5
- package/templates/native-go/backend/internal/helper/supervisor_frame.go +76 -0
- package/templates/native-go/backend/internal/helper/supervisor_frame_linux_test.go +69 -0
- package/templates/native-go/backend/internal/helper/supervisor_frame_test.go +107 -0
- package/templates/native-go/backend/internal/helper/supervisor_linux.go +187 -16
- package/templates/native-go/backend/internal/helper/supervisor_linux_test.go +618 -7
- package/templates/native-go/backend/internal/helper/supervisor_socket_eof_test.go +74 -0
- package/templates/native-go/backend/internal/helper/supervisor_socket_test.go +45 -0
- package/templates/native-go/backend/internal/helper/testdata/protocol2/create.json +23 -0
- package/templates/native-go/backend/internal/helper/testdata/protocol2/frames.json +67 -0
- package/templates/native-go/backend/internal/helper/testdata/protocol2/grant-store.json +52 -0
- package/templates/native-go/backend/internal/helper/testdata/protocol2/intent.json +20 -0
- package/templates/native-go/backend/internal/helper/testdata/protocol2/list.json +20 -0
- package/templates/native-go/backend/internal/helper/testdata/protocol2/publish.json +20 -0
- package/templates/native-go/backend/internal/helper/testdata/protocol2/read.json +22 -0
- package/templates/native-go/backend/internal/helper/testdata/protocol2/stat.json +22 -0
- package/templates/native-go/backend/internal/helper/testdata/protocol2/write-stream.json +22 -0
- package/templates/native-go/backend/internal/helper/unit_connection.go +75 -0
- package/templates/native-go/backend/internal/helper/unit_connection_linux_test.go +135 -0
- package/templates/native-go/backend/internal/helper/unit_connection_test.go +133 -0
- package/templates/native-go/backend/internal/helper/unit_frame_test.go +32 -0
- package/templates/native-go/backend/internal/helper/unit_linux.go +364 -0
- package/templates/native-go/backend/internal/helper/unit_linux_test.go +46 -0
- package/templates/native-go/backend/internal/helper/unit_mechanisms_linux_test.go +217 -0
- package/templates/native-go/backend/internal/helper/unit_padding_linux_test.go +178 -0
- package/templates/native-go/backend/internal/helper/unit_query_linux.go +111 -0
- package/templates/native-go/backend/internal/helper/unit_reload_linux_test.go +372 -0
- package/templates/native-go/backend/internal/helper/unit_reload_sequence_linux_test.go +232 -0
- package/templates/native-go/backend/internal/helper/write.go +77 -0
- package/templates/native-go/backend/internal/helper/write_admission.go +260 -0
- package/templates/native-go/backend/internal/helper/write_admission_test.go +135 -0
- package/templates/native-go/backend/internal/helper/write_cohort_linux_test.go +218 -0
- package/templates/native-go/backend/internal/helper/write_files_linux.go +202 -0
- package/templates/native-go/backend/internal/helper/write_files_linux_test.go +199 -0
- package/templates/native-go/backend/internal/helper/write_intent_linux.go +116 -0
- package/templates/native-go/backend/internal/helper/write_intent_linux_test.go +74 -0
- package/templates/native-go/backend/internal/helper/write_operation_linux.go +245 -0
- package/templates/native-go/backend/internal/helper/write_test.go +53 -0
- package/templates/native-go/ui/app.js +8 -1
- package/templates/native-go-better-auth/node/build.mjs +2 -0
- package/templates/native-go-vue/README.md +115 -1
- package/templates/native-go-vue/frontend/DESIGN.md +1 -1
- package/templates/native-go-vue/frontend/package-lock.json +132 -229
- package/templates/native-go-vue/frontend/package.json +1 -1
- package/templates/native-go-vue/frontend/src/App.vue +33 -6
- package/templates/native-go-vue/frontend/src/context.ts +41 -2
- package/templates/native-go-vue/frontend/src/main.ts +4 -1
- package/templates/native-go-vue/frontend/src/nas/NasGrantSettings.vue +74 -0
- package/templates/native-go-vue/frontend/src/nas/NasPathPicker.vue +106 -0
- package/templates/native-go-vue/frontend/src/nas/NasUnavailable.vue +20 -0
- package/templates/native-go-vue/frontend/src/nas/i18n/de.json +74 -0
- package/templates/native-go-vue/frontend/src/nas/i18n/en.json +74 -0
- package/templates/native-go-vue/frontend/src/nas/i18n/es.json +74 -0
- package/templates/native-go-vue/frontend/src/nas/i18n/fr.json +74 -0
- package/templates/native-go-vue/frontend/src/nas/i18n/it.json +74 -0
- package/templates/native-go-vue/frontend/src/nas/i18n/ja.json +74 -0
- package/templates/native-go-vue/frontend/src/nas/i18n/ko.json +74 -0
- package/templates/native-go-vue/frontend/src/nas/i18n/zh-CN.json +74 -0
- package/templates/native-go-vue/frontend/src/nas/i18n/zh-TW.json +74 -0
- package/templates/native-go-vue/frontend/src/nas/i18n.ts +38 -0
- package/templates/native-go-vue/frontend/src/nas/mock-provider.ts +119 -0
- package/templates/native-go-vue/frontend/src/nas/provider.ts +59 -0
- package/templates/native-go-vue/frontend/src/nas/use-request.ts +27 -0
- package/templates/native-go-vue/frontend/src/style.css +17 -0
- package/templates/native-go-vue/frontend/test/apps.test.ts +44 -2
- package/templates/native-go-vue/frontend/test/nas-provider.test.ts +81 -0
- package/templates/native-go-vue/frontend/test/nas-request.test.ts +21 -0
- package/templates/native-go-vue/frontend/test/nas-ui.test.ts +261 -0
- package/templates/native-go-vue/frontend/tsconfig.json +1 -1
- package/templates/native-go-vue/frontend/vite.config.ts +6 -4
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type {InjectionKey} from 'vue'
|
|
2
|
+
|
|
3
|
+
// S0 protocol-2 display projections, not root Grant/Intent authority records.
|
|
4
|
+
// IDs index provider-owned mappings. No paths, credentials, UID or role claims
|
|
5
|
+
// travel in picker/consent arguments. S1 discovery metadata is not a grant.
|
|
6
|
+
export type ScopeKind = 'app-shared' | 'user-personal'
|
|
7
|
+
export const readActions = ['nas.files.list', 'nas.files.stat', 'nas.files.read'] as const
|
|
8
|
+
export type ReadAction = typeof readActions[number]
|
|
9
|
+
export type SafeCode = 'unknown_method' | 'session_required' | 'auth_unavailable' | 'invalid-request' | 'incompatible' | 'unsupported' | 'replay' | 'stale' | 'expired' | 'denied' | 'unavailable' | 'suspended' | 'revoked' | 'fenced' | 'busy' | 'limit-exceeded' | 'canceled' | 'timeout' | 'partial' | 'commit-uncertain' | 'cleanup-required' | 'internal'
|
|
10
|
+
export interface SafeError {code: SafeCode; operationId?: string}
|
|
11
|
+
export interface DiscoveryAnchor {
|
|
12
|
+
ref: string
|
|
13
|
+
displayName: string
|
|
14
|
+
kind: ScopeKind
|
|
15
|
+
owner: {kind: 'local'; subjectRef: string; instanceRef: string}
|
|
16
|
+
localFilesystem: boolean
|
|
17
|
+
}
|
|
18
|
+
export interface PickerContext {contextId: string; scopeKind: ScopeKind; expiresAt: number}
|
|
19
|
+
export interface FolderEntry {nodeId: string; displayName: string; targetKind: 'directory'; selectable: boolean}
|
|
20
|
+
export interface FolderPage {entries: FolderEntry[]; nextCursor?: string}
|
|
21
|
+
export interface ConsentSummary {
|
|
22
|
+
appName: string
|
|
23
|
+
scopeKind: ScopeKind
|
|
24
|
+
displayName: string
|
|
25
|
+
targetKind: 'directory'
|
|
26
|
+
actions: readonly ReadAction[]
|
|
27
|
+
persistent: true
|
|
28
|
+
}
|
|
29
|
+
export interface PreparedIntent {intentId: string; summaryDigest: string; expiresAt: number; summary: ConsentSummary}
|
|
30
|
+
export interface GrantHandle {
|
|
31
|
+
grantId: string
|
|
32
|
+
revision: number
|
|
33
|
+
scopeKind: ScopeKind
|
|
34
|
+
displayName: string
|
|
35
|
+
targetKind: 'directory'
|
|
36
|
+
actions: readonly ReadAction[]
|
|
37
|
+
}
|
|
38
|
+
export interface GrantView extends GrantHandle {
|
|
39
|
+
effectiveState: 'active' | 'suspended' | 'revoked'
|
|
40
|
+
createdAt: number
|
|
41
|
+
canRevoke: boolean
|
|
42
|
+
// S0 R/Q receipt: undefined means Q has not been observed, never assume true.
|
|
43
|
+
drained?: boolean
|
|
44
|
+
cleanupRequired?: boolean
|
|
45
|
+
}
|
|
46
|
+
export interface GrantList {items: GrantView[]; canManageShared: boolean}
|
|
47
|
+
export interface RevokeResult {grantId: string; grantRevision: number; revoked: boolean; drained: boolean}
|
|
48
|
+
export interface NasProvider {
|
|
49
|
+
readonly mode: 'mock' | 'connected'
|
|
50
|
+
begin(kind: ScopeKind, signal: AbortSignal): Promise<PickerContext>
|
|
51
|
+
list(contextId: string, nodeId: string | undefined, cursor: string | undefined, signal: AbortSignal): Promise<FolderPage>
|
|
52
|
+
prepare(candidateId: string, signal: AbortSignal): Promise<PreparedIntent>
|
|
53
|
+
confirm(intentId: string, summaryDigest: string, signal: AbortSignal): Promise<GrantHandle>
|
|
54
|
+
cancel(contextId: string): Promise<void>
|
|
55
|
+
grants(signal: AbortSignal): Promise<GrantView[]>
|
|
56
|
+
settings(signal: AbortSignal): Promise<GrantList>
|
|
57
|
+
revoke(grantId: string, revision: number, signal: AbortSignal): Promise<RevokeResult>
|
|
58
|
+
}
|
|
59
|
+
export const NasProviderKey: InjectionKey<NasProvider> = Symbol('nas-provider')
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {onBeforeUnmount, ref} from 'vue'
|
|
2
|
+
|
|
3
|
+
// One admission at a time per component, bounded metadata requests, and no late
|
|
4
|
+
// responses after close/unmount. An abort is not a server rollback/Q receipt.
|
|
5
|
+
export function useNasRequest() {
|
|
6
|
+
const busy=ref(false), error=ref<unknown>()
|
|
7
|
+
let generation=0, disposed=false, controller:AbortController|undefined
|
|
8
|
+
function cancel() {generation++;controller?.abort();controller=undefined;busy.value=false;error.value=undefined}
|
|
9
|
+
async function run<T>(operation:(signal:AbortSignal,current:()=>boolean)=>Promise<T>):Promise<T|undefined> {
|
|
10
|
+
if(busy.value || disposed) return
|
|
11
|
+
const version=++generation, active=new AbortController();controller=active;busy.value=true;error.value=undefined
|
|
12
|
+
const current=()=>!disposed && generation===version
|
|
13
|
+
let timer:ReturnType<typeof setTimeout>|undefined
|
|
14
|
+
let onAbort:()=>void=()=>{}
|
|
15
|
+
try {
|
|
16
|
+
const stop=new Promise<never>((_,reject)=> {
|
|
17
|
+
onAbort=()=>reject({code:'canceled'});active.signal.addEventListener('abort',onAbort,{once:true})
|
|
18
|
+
timer=setTimeout(()=>{reject({code:'timeout'});active.abort()},5000)
|
|
19
|
+
})
|
|
20
|
+
const value=await Promise.race([operation(active.signal,current),stop])
|
|
21
|
+
return current()?value:undefined
|
|
22
|
+
} catch(e) {if(current()) error.value=e;return undefined}
|
|
23
|
+
finally {clearTimeout(timer);active.signal.removeEventListener('abort',onAbort);if(current()){busy.value=false;controller=undefined}}
|
|
24
|
+
}
|
|
25
|
+
onBeforeUnmount(()=>{disposed=true;cancel()})
|
|
26
|
+
return {busy,error,run,cancel}
|
|
27
|
+
}
|
|
@@ -12,3 +12,20 @@
|
|
|
12
12
|
[data-ui-app] .integration-actions {display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px;}
|
|
13
13
|
[data-ui-app] .integration-field {display: grid; gap: 6px; margin: 12px 0;}
|
|
14
14
|
[data-ui-app] .integration-notice {color: var(--ui-text);}
|
|
15
|
+
[data-ui-app] .nas-summary {display: grid; grid-template-columns: minmax(6ch, max-content) minmax(0, 1fr); gap: var(--ui-space-2, 8px) var(--ui-space-4, 16px);}
|
|
16
|
+
[data-ui-app] .nas-summary dd {margin: 0; overflow-wrap: anywhere;}
|
|
17
|
+
[data-ui-app] .nas-breadcrumb {display: flex; flex-wrap: wrap; gap: var(--ui-space-2, 8px); margin-block: var(--ui-space-3, 12px);}
|
|
18
|
+
[data-ui-app] .nas-browser {display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--ui-space-3, 12px);}
|
|
19
|
+
[data-ui-app] .nas-tree {display: none;}
|
|
20
|
+
[data-ui-app] .nas-folder-list {min-width: 0; overflow: auto;}
|
|
21
|
+
[data-ui-app] .nas-folder-name {white-space: normal; overflow-wrap: anywhere; text-align: start;}
|
|
22
|
+
[data-ui-app] .nas-folder-action {white-space: normal; max-width: 100%;}
|
|
23
|
+
[data-ui-app] .nas-picker {max-height: 55vh; overflow: auto; overflow-wrap: anywhere;}
|
|
24
|
+
[data-ui-app] .nas-grants {list-style: none; padding: 0;}
|
|
25
|
+
[data-ui-app] .nas-grants > li {padding-block: var(--ui-space-4, 16px); border-bottom: 1px solid var(--ui-border);}
|
|
26
|
+
[data-ui-app] .nas-grant-heading {display: flex; align-items: start; justify-content: space-between; gap: var(--ui-space-3, 12px);}
|
|
27
|
+
[data-ui-app] .nas-grant-heading strong {overflow-wrap: anywhere;}
|
|
28
|
+
@media (min-width: 960px) {
|
|
29
|
+
[data-ui-app] .nas-browser {grid-template-columns: minmax(8ch, 1fr) minmax(0, 3fr);}
|
|
30
|
+
[data-ui-app] .nas-tree {display: block; overflow: auto;}
|
|
31
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {expect, test, vi} from 'vitest'
|
|
3
3
|
import {nextTick} from 'vue'
|
|
4
4
|
import {mount} from '../src/main'
|
|
5
|
+
import {nasCopy} from '../src/nas/i18n'
|
|
5
6
|
import type {Context, IntegrationState} from '../src/context'
|
|
6
7
|
|
|
7
8
|
function fixture(id: string, admin: boolean, state: IntegrationState = 'ready') {
|
|
@@ -21,8 +22,8 @@ function fixture(id: string, admin: boolean, state: IntegrationState = 'ready')
|
|
|
21
22
|
async function settle(){for(let i=0;i<12;i++){await Promise.resolve();await nextTick()}await new Promise(resolve=>setTimeout(resolve,25))}
|
|
22
23
|
function click(root:HTMLElement,text:string){const node=[...root.querySelectorAll('button')].find(node=>node.textContent?.trim()===text);expect(node, text).toBeTruthy();node!.click()}
|
|
23
24
|
|
|
24
|
-
test('
|
|
25
|
-
for(const state of ['not-enabled','ready','needs-approval','incompatible','stopped','failed'] as IntegrationState[]){
|
|
25
|
+
test('seven states and ordinary-user errors give actionable status without approval controls',async()=>{
|
|
26
|
+
for(const state of ['not-enabled','ready','needs-approval','incompatible','stopped','failed','pending'] as IntegrationState[]){
|
|
26
27
|
const f=fixture('Ordinary',false,state);await settle()
|
|
27
28
|
expect(f.root.querySelector('[data-integration-state]')?.getAttribute('data-integration-state')).toBe(state)
|
|
28
29
|
expect(f.root.textContent).not.toContain('Approve and activate')
|
|
@@ -146,3 +147,44 @@ test('unmount releases its sizing class and preserves an existing host class',as
|
|
|
146
147
|
expect(f.root.classList.contains('dsmapp-vue-root')).toBe(true)
|
|
147
148
|
f.root.remove()
|
|
148
149
|
})
|
|
150
|
+
|
|
151
|
+
test('repair is administrator-only and cancellation or refusal never starts recovery',async()=>{
|
|
152
|
+
const user=fixture('UserRepair',false,'failed'),admin=fixture('AdminRepair',true,'failed')
|
|
153
|
+
try {
|
|
154
|
+
await settle()
|
|
155
|
+
expect(user.root.textContent).not.toContain('Repair cleanup')
|
|
156
|
+
admin.ctx.root!.authorize=vi.fn(async()=> 'cancelled')
|
|
157
|
+
click(admin.root,'Repair cleanup');await settle()
|
|
158
|
+
expect(admin.ctx.root!.authorize).toHaveBeenCalledWith('helper_repair')
|
|
159
|
+
expect(admin.ctx.root!.run).not.toHaveBeenCalled()
|
|
160
|
+
admin.ctx.root!.authorize=vi.fn(async()=> 'ready')
|
|
161
|
+
admin.ctx.root!.run=vi.fn(async()=>{throw {code:'root_task_failed'}})
|
|
162
|
+
click(admin.root,'Repair cleanup');await settle()
|
|
163
|
+
expect(admin.ctx.root!.run).toHaveBeenCalledWith('helper_repair')
|
|
164
|
+
expect(admin.ctx.root!.run).not.toHaveBeenCalledWith('helper_recover')
|
|
165
|
+
expect(admin.root.textContent).toContain('Repair could not verify safe cleanup')
|
|
166
|
+
expect(admin.call.mock.calls.filter(args=>args[0]==='integration.status').length).toBeGreaterThan(1)
|
|
167
|
+
admin.ctx.root!.run=vi.fn(async()=>{throw {code:'root_cancelled'}})
|
|
168
|
+
click(admin.root,'Repair cleanup');await settle()
|
|
169
|
+
expect(admin.root.textContent).toContain('Repair confirmation cancelled. No operation was run.')
|
|
170
|
+
admin.ctx.root!.run=vi.fn(async()=>{})
|
|
171
|
+
click(admin.root,'Repair cleanup');await settle()
|
|
172
|
+
expect(admin.root.textContent).toContain('Cleanup repaired. Use Recover or Approve and activate')
|
|
173
|
+
expect(admin.ctx.root!.run).not.toHaveBeenCalledWith('helper_recover')
|
|
174
|
+
} finally {user.app.unmount();admin.app.unmount();user.root.remove();admin.root.remove()}
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
test('pending health shows localized waiting copy without exposing sample authority',async()=>{
|
|
178
|
+
const locales=['en','zh-TW','zh-CN','ja','ko','de','fr','es','it']
|
|
179
|
+
for(const locale of locales){
|
|
180
|
+
const f=fixture('Pending',false,'pending');f.ctx.locale=locale
|
|
181
|
+
f.reopen.forEach(fn=>fn());await settle()
|
|
182
|
+
expect(f.root.querySelector('[data-integration-state="pending"]')).toBeTruthy()
|
|
183
|
+
expect(f.root.textContent).not.toContain('Administrator action needed')
|
|
184
|
+
expect(f.root.textContent).not.toContain('My sample data')
|
|
185
|
+
expect(f.call.mock.calls.some(args=>args[0]==='personal.list')).toBe(false)
|
|
186
|
+
expect(f.root.querySelector('.integration-status h2')?.textContent?.trim()).toBe(nasCopy(locale).healthPending)
|
|
187
|
+
expect(f.root.textContent).toContain(nasCopy(locale).healthPendingHelp)
|
|
188
|
+
f.app.unmount();f.root.remove()
|
|
189
|
+
}
|
|
190
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {expect, test} from 'vitest'
|
|
2
|
+
import {createMockNasService} from '../src/nas/mock-provider'
|
|
3
|
+
|
|
4
|
+
const signal = () => new AbortController().signal
|
|
5
|
+
async function selection(provider: ReturnType<ReturnType<typeof createMockNasService>['connect']>) {
|
|
6
|
+
const context = await provider.begin('user-personal', signal())
|
|
7
|
+
const page = await provider.list(context.contextId, undefined, undefined, signal())
|
|
8
|
+
return {context, candidate: page.entries[0]!}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
test('two ordinary subjects cannot exchange contexts, candidates, intents or personal grants', async () => {
|
|
12
|
+
const service = createMockNasService(), a = service.connect('normal1'), b = service.connect('normal2')
|
|
13
|
+
const picked = await selection(a)
|
|
14
|
+
await expect(b.list(picked.context.contextId, undefined, undefined, signal())).rejects.toEqual({code:'denied'})
|
|
15
|
+
await expect(b.prepare(picked.candidate.nodeId, signal())).rejects.toEqual({code:'denied'})
|
|
16
|
+
const intent = await a.prepare(picked.candidate.nodeId, signal())
|
|
17
|
+
await expect(b.confirm(intent.intentId, intent.summaryDigest, signal())).rejects.toEqual({code:'denied'})
|
|
18
|
+
const grant = await a.confirm(intent.intentId, intent.summaryDigest, signal())
|
|
19
|
+
expect(await b.grants(signal())).toEqual([])
|
|
20
|
+
await expect(b.revoke(grant.grantId, grant.revision, signal())).rejects.toEqual({code:'denied'})
|
|
21
|
+
expect(grant.actions).toEqual(['nas.files.list','nas.files.stat','nas.files.read'])
|
|
22
|
+
expect(JSON.stringify(grant)).not.toMatch(/\/volume|uid|sessionRef/)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('intent expires at 30 seconds; candidate expires at 60; cancel never creates a grant', async () => {
|
|
26
|
+
let now = 1000
|
|
27
|
+
const service = createMockNasService({now:() => now}), a = service.connect('normal1')
|
|
28
|
+
const picked = await selection(a), intent = await a.prepare(picked.candidate.nodeId, signal())
|
|
29
|
+
now += 30_000
|
|
30
|
+
await expect(a.confirm(intent.intentId, intent.summaryDigest, signal())).rejects.toEqual({code:'expired'})
|
|
31
|
+
now += 30_000
|
|
32
|
+
await expect(a.prepare(picked.candidate.nodeId, signal())).rejects.toEqual({code:'expired'})
|
|
33
|
+
const next = await selection(a), cancelIntent = await a.prepare(next.candidate.nodeId, signal())
|
|
34
|
+
await a.cancel(next.context.contextId)
|
|
35
|
+
await expect(a.confirm(cancelIntent.intentId, cancelIntent.summaryDigest, signal())).rejects.toEqual({code:'canceled'})
|
|
36
|
+
expect(await a.grants(signal())).toEqual([])
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('summary mismatch, replay, abort, role and session changes refuse; reconfirm gets a new ID', async () => {
|
|
40
|
+
const service = createMockNasService(), admin = service.connect('admin'), a = service.connect('normal1')
|
|
41
|
+
await expect(a.begin('app-shared', signal())).rejects.toEqual({code:'denied'})
|
|
42
|
+
const picked = await selection(a), intent = await a.prepare(picked.candidate.nodeId, signal())
|
|
43
|
+
await expect(a.confirm(intent.intentId, '0'.repeat(64), signal())).rejects.toEqual({code:'stale'})
|
|
44
|
+
const grant = await a.confirm(intent.intentId, intent.summaryDigest, signal())
|
|
45
|
+
await expect(a.confirm(intent.intentId, intent.summaryDigest, signal())).rejects.toEqual({code:'replay'})
|
|
46
|
+
await a.revoke(grant.grantId, grant.revision, signal())
|
|
47
|
+
const second = await a.prepare(picked.candidate.nodeId, signal())
|
|
48
|
+
expect((await a.confirm(second.intentId, second.summaryDigest, signal())).grantId).not.toBe(grant.grantId)
|
|
49
|
+
const aborted = new AbortController(); aborted.abort()
|
|
50
|
+
await expect(a.begin('user-personal', aborted.signal)).rejects.toEqual({code:'canceled'})
|
|
51
|
+
const context = await admin.begin('app-shared', signal()), page = await admin.list(context.contextId, undefined, undefined, signal())
|
|
52
|
+
const shared = await admin.prepare(page.entries[0]!.nodeId, signal())
|
|
53
|
+
service.setAdmin('admin', false)
|
|
54
|
+
await expect(admin.confirm(shared.intentId, shared.summaryDigest, signal())).rejects.toEqual({code:'denied'})
|
|
55
|
+
service.invalidate('normal1')
|
|
56
|
+
await expect(a.list(picked.context.contextId, undefined, undefined, signal())).rejects.toEqual({code:'stale'})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('shared grant discovery is visible without becoming revoke authority; R and Q stay separate', async () => {
|
|
60
|
+
const service = createMockNasService({drained:false}), admin = service.connect('admin'), a = service.connect('normal1')
|
|
61
|
+
const ctx = await admin.begin('app-shared', signal()), page = await admin.list(ctx.contextId, undefined, undefined, signal())
|
|
62
|
+
const intent = await admin.prepare(page.entries[0]!.nodeId, signal()), grant = await admin.confirm(intent.intentId, intent.summaryDigest, signal())
|
|
63
|
+
expect((await a.grants(signal()))[0]?.grantId).toBe(grant.grantId)
|
|
64
|
+
await expect(a.revoke(grant.grantId, grant.revision, signal())).rejects.toEqual({code:'denied'})
|
|
65
|
+
expect(await admin.revoke(grant.grantId, grant.revision, signal())).toMatchObject({revoked:true,drained:false})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('pagination binds cursor to actor/context/node and revision CAS rejects stale revoke',async()=> {
|
|
69
|
+
const service=createMockNasService({pageSize:1}), a=service.connect('normal1'), b=service.connect('normal2')
|
|
70
|
+
const {context,candidate}=await selection(a)
|
|
71
|
+
const page=await a.list(context.contextId,undefined,undefined,signal())
|
|
72
|
+
expect(page.entries).toHaveLength(1);expect(page.nextCursor).toBeTruthy()
|
|
73
|
+
expect((await a.list(context.contextId,undefined,page.nextCursor,signal())).entries[0]!.displayName).toBe('Empty folder')
|
|
74
|
+
await expect(a.list(context.contextId,candidate.nodeId,page.nextCursor,signal())).rejects.toEqual({code:'denied'})
|
|
75
|
+
await expect(b.list(context.contextId,undefined,page.nextCursor,signal())).rejects.toEqual({code:'denied'})
|
|
76
|
+
const intent=await a.prepare(candidate.nodeId,signal()), grant=await a.confirm(intent.intentId,intent.summaryDigest,signal())
|
|
77
|
+
await expect(a.revoke(grant.grantId,grant.revision+1,signal())).rejects.toEqual({code:'stale'})
|
|
78
|
+
service.newEpoch()
|
|
79
|
+
expect((await a.grants(signal()))[0]!.effectiveState).toBe('suspended')
|
|
80
|
+
await expect(a.prepare(candidate.nodeId,signal())).rejects.toEqual({code:'stale'})
|
|
81
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import {expect,test,vi} from 'vitest'
|
|
3
|
+
import {createApp,defineComponent} from 'vue'
|
|
4
|
+
import {useNasRequest} from '../src/nas/use-request'
|
|
5
|
+
|
|
6
|
+
test('unresponsive discovery times out in five seconds, aborts, and ignores late metadata',async()=> {
|
|
7
|
+
vi.useFakeTimers()
|
|
8
|
+
let request!:ReturnType<typeof useNasRequest>
|
|
9
|
+
const root=document.createElement('div')
|
|
10
|
+
const app=createApp(defineComponent({setup(){request=useNasRequest();return ()=>null}}));app.mount(root)
|
|
11
|
+
try {
|
|
12
|
+
let resolve!:(s:string)=>void, signal!:AbortSignal
|
|
13
|
+
const pending=request.run(s=>{signal=s;return new Promise<string>(r=>resolve=r)})
|
|
14
|
+
await vi.advanceTimersByTimeAsync(5000)
|
|
15
|
+
expect(await pending).toBeUndefined()
|
|
16
|
+
expect(request.busy.value).toBe(false);expect(signal.aborted).toBe(true)
|
|
17
|
+
expect(request.error.value).toEqual({code:'timeout'})
|
|
18
|
+
resolve('late private metadata');await Promise.resolve()
|
|
19
|
+
expect(request.error.value).toEqual({code:'timeout'})
|
|
20
|
+
} finally {app.unmount();vi.useRealTimers()}
|
|
21
|
+
})
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import {expect, test, vi} from 'vitest'
|
|
3
|
+
import {nextTick} from 'vue'
|
|
4
|
+
import {mount} from '../src/main'
|
|
5
|
+
import {createMockNasService} from '../src/nas/mock-provider'
|
|
6
|
+
import {catalogs, nasCopy, format, errorCopy} from '../src/nas/i18n'
|
|
7
|
+
import type {NasProvider} from '../src/nas/provider'
|
|
8
|
+
import type {Context} from '../src/context'
|
|
9
|
+
|
|
10
|
+
async function settle() {for(let i=0;i<20;i++){await Promise.resolve();await nextTick()}await new Promise(r=>setTimeout(r,25))}
|
|
11
|
+
function click(root:HTMLElement, text:string) {const b=[...root.querySelectorAll('button')].find(b=>b.textContent?.trim()===text || b.getAttribute('aria-label')===text);expect(b,text).toBeTruthy();b!.click()}
|
|
12
|
+
function fixture(nas:NasProvider | undefined, user='normal1', locale='en') {
|
|
13
|
+
const root=document.createElement('div');document.body.append(root)
|
|
14
|
+
const reopen:Array<()=>void>=[]
|
|
15
|
+
const ctx={locale,reopen(fn:()=>void){reopen.push(fn)},call:vi.fn(async(method:string)=> {
|
|
16
|
+
if(method==='app.status') return {package:'SamePackage',version:'1',user,admin:user==='admin'}
|
|
17
|
+
if(method==='integration.status') return {state:'not-enabled',reason:'not-requested',packageRunning:'unknown',scheduler:'not-probed'}
|
|
18
|
+
throw {code:'unknown_method'}
|
|
19
|
+
})} as Context
|
|
20
|
+
const app=mount(root,ctx,{nasProvider:nas})
|
|
21
|
+
return {root,app,ctx,reopen}
|
|
22
|
+
}
|
|
23
|
+
function cleanup(...fixtures:ReturnType<typeof fixture>[]) {for(const f of fixtures){f.app.unmount();f.root.remove()}}
|
|
24
|
+
|
|
25
|
+
test('managed account pilot stays visibly off with maintenance and approval restrictions',async()=> {
|
|
26
|
+
const f=fixture(createMockNasService().connect('admin'),'admin')
|
|
27
|
+
try {
|
|
28
|
+
await settle()
|
|
29
|
+
expect(f.root.textContent).toContain('Managed account pilot: off')
|
|
30
|
+
expect(f.root.textContent).toContain('Account changes or unverified identity revoke access')
|
|
31
|
+
expect(f.root.textContent).toContain('Enabling this pilot is unavailable in this build')
|
|
32
|
+
expect([...f.root.querySelectorAll('button')].some(b=>b.textContent?.includes('Enable managed'))).toBe(false)
|
|
33
|
+
} finally {cleanup(f)}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('nine complete catalogs resolve DSM locale aliases with English fallback',()=> {
|
|
37
|
+
expect(Object.keys(catalogs)).toEqual(['en','zh-TW','zh-CN','ja','ko','de','fr','es','it'])
|
|
38
|
+
for(const catalog of Object.values(catalogs)) {
|
|
39
|
+
expect(Object.keys(catalog).sort()).toEqual(Object.keys(catalogs.en).sort())
|
|
40
|
+
expect(Object.values(catalog).every(s=>typeof s==='string' && s.trim().length>0)).toBe(true)
|
|
41
|
+
for(const key of Object.keys(catalogs.en) as Array<keyof typeof catalogs.en>) {
|
|
42
|
+
expect(catalog[key].match(/\{\w+\}/g) ?? []).toEqual(catalogs.en[key].match(/\{\w+\}/g) ?? [])
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
expect(nasCopy('cht')).toEqual(catalogs['zh-TW'])
|
|
46
|
+
expect(nasCopy('zh_CN')).toEqual(catalogs['zh-CN'])
|
|
47
|
+
expect(nasCopy('unknown')).toEqual(catalogs.en)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('account changes discard the bound provider UI and its pending candidate context',async()=> {
|
|
51
|
+
const provider=createMockNasService().connect('normal1'), cancel=vi.spyOn(provider,'cancel'), f=fixture(provider)
|
|
52
|
+
try {
|
|
53
|
+
await settle();click(f.root,'Choose my folder');await settle()
|
|
54
|
+
f.ctx.call=vi.fn(async(method:string)=>method==='app.status'?{package:'SamePackage',version:'1',user:'normal2',admin:false}:{state:'not-enabled',reason:'not-requested',packageRunning:'unknown',scheduler:'not-probed'})
|
|
55
|
+
f.reopen.forEach(fn=>fn());await settle()
|
|
56
|
+
expect(f.root.textContent).toContain('normal2')
|
|
57
|
+
expect(f.root.querySelector('[role="dialog"]')).toBeNull()
|
|
58
|
+
expect(f.root.textContent).not.toContain('Choose my folder')
|
|
59
|
+
expect(cancel).toHaveBeenCalled()
|
|
60
|
+
} finally {cleanup(f)}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('all nine locale UIs localize dialog controls and allow keyboard review without mouse-only actions',async()=> {
|
|
64
|
+
for(const locale of Object.keys(catalogs)) {
|
|
65
|
+
const copy=nasCopy(locale), f=fixture(createMockNasService().connect('normal1'),'normal1',locale)
|
|
66
|
+
try {
|
|
67
|
+
await settle();click(f.root,copy.choosePersonal);await settle()
|
|
68
|
+
const selected=[...f.root.querySelectorAll('button')].find(b=>b.getAttribute('aria-label')===format(copy.select,'Projects'))!
|
|
69
|
+
selected.focus();expect(document.activeElement).toBe(selected)
|
|
70
|
+
// Native Button remains a keyboard-activatable button (browser verifies Enter).
|
|
71
|
+
expect(selected.tagName).toBe('BUTTON');selected.click();await settle()
|
|
72
|
+
click(f.root,copy.review);await settle();expect(f.root.textContent).toContain(copy.persistent)
|
|
73
|
+
expect(f.root.querySelector(`button[aria-label="${copy.close}"]`)).toBeTruthy()
|
|
74
|
+
click(f.root,copy.cancel);await settle()
|
|
75
|
+
} finally {cleanup(f)}
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('home unavailable is recoverable and native diagnostics are never rendered',async()=> {
|
|
80
|
+
const provider=createMockNasService({homeAvailable:false}).connect('normal1'), f=fixture(provider)
|
|
81
|
+
try {
|
|
82
|
+
await settle();click(f.root,'Choose my folder');await settle()
|
|
83
|
+
expect(f.root.textContent).toContain('Required native identity or permission proof is unavailable')
|
|
84
|
+
provider.begin=async()=>{throw {code:'unknown',message:'/volume1/private-secret stderr'}}
|
|
85
|
+
click(f.root,'Retry');await settle()
|
|
86
|
+
expect(f.root.textContent).toContain('The request failed')
|
|
87
|
+
expect(f.root.textContent).not.toMatch(/private-secret|stderr/)
|
|
88
|
+
} finally {cleanup(f)}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('picker browse, separate selection/summary, cancel, empty and confirm use injected provider only',async()=> {
|
|
92
|
+
const service=createMockNasService(), provider=service.connect('normal1'), cancel=vi.spyOn(provider,'cancel'), f=fixture(provider)
|
|
93
|
+
try {
|
|
94
|
+
await settle();click(f.root,'Choose my folder');await settle()
|
|
95
|
+
expect(f.root.textContent).toContain('Mock preview')
|
|
96
|
+
expect(f.root.querySelector('[role="dialog"]')).toBeTruthy()
|
|
97
|
+
click(f.root,'Select Projects');await settle()
|
|
98
|
+
expect(f.root.textContent).not.toContain('Persistent access')
|
|
99
|
+
click(f.root,'Review access');await settle()
|
|
100
|
+
expect(f.root.textContent).toContain('Persistent access')
|
|
101
|
+
click(f.root,'Cancel');await settle()
|
|
102
|
+
expect(cancel).toHaveBeenCalled()
|
|
103
|
+
expect(await provider.grants(new AbortController().signal)).toEqual([])
|
|
104
|
+
click(f.root,'Choose my folder');await settle();click(f.root,'Open Empty folder');await settle()
|
|
105
|
+
expect(f.root.textContent).toContain('This folder is empty')
|
|
106
|
+
click(f.root,'Back');await settle();click(f.root,'Select Projects');await settle();click(f.root,'Review access');await settle();click(f.root,'Confirm access');await settle()
|
|
107
|
+
expect(f.root.querySelector('[role="dialog"]')).toBeNull()
|
|
108
|
+
expect(f.root.textContent).toContain('Active')
|
|
109
|
+
} finally {cleanup(f)}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test('two same-package apps retain local modality, unique ARIA IDs and cancellation isolation',async()=> {
|
|
113
|
+
const service=createMockNasService(), a=fixture(service.connect('normal1')), b=fixture(service.connect('normal2'))
|
|
114
|
+
const body=document.body.getAttribute('style')
|
|
115
|
+
try {
|
|
116
|
+
await settle();click(a.root,'Choose my folder');click(b.root,'Choose my folder');await settle()
|
|
117
|
+
const ids=[...document.querySelectorAll('[id]')].map(n=>n.id).filter(Boolean)
|
|
118
|
+
expect(new Set(ids).size).toBe(ids.length)
|
|
119
|
+
for(const f of [a,b]) {
|
|
120
|
+
const dialog=f.root.querySelector('[role="dialog"]')!
|
|
121
|
+
expect(dialog.hasAttribute('aria-modal')).toBe(false)
|
|
122
|
+
for(const name of ['aria-labelledby','aria-describedby']) expect(f.root.querySelector(`[id="${dialog.getAttribute(name)}"]`)).toBeTruthy()
|
|
123
|
+
}
|
|
124
|
+
expect(getComputedStyle(b.root).pointerEvents).not.toBe('none')
|
|
125
|
+
click(a.root,'Cancel');await settle()
|
|
126
|
+
expect(a.root.querySelector('[role="dialog"]')).toBeNull()
|
|
127
|
+
expect(b.root.querySelector('[role="dialog"]')).toBeTruthy()
|
|
128
|
+
expect(document.body.getAttribute('style')).toBe(body)
|
|
129
|
+
} finally {cleanup(a,b)}
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('intent expiry is actionable; revoked but not drained never claims transfers stopped',async()=> {
|
|
133
|
+
let now=1000
|
|
134
|
+
const service=createMockNasService({now:()=>now,drained:false}), provider=service.connect('normal1'), f=fixture(provider)
|
|
135
|
+
try {
|
|
136
|
+
await settle();click(f.root,'Choose my folder');await settle();click(f.root,'Select Projects');await settle();click(f.root,'Review access');await settle()
|
|
137
|
+
now+=30_000;click(f.root,'Confirm access');await settle()
|
|
138
|
+
expect(f.root.textContent).toContain('expired')
|
|
139
|
+
expect(await provider.grants(new AbortController().signal)).toEqual([])
|
|
140
|
+
click(f.root,'Cancel');await settle();click(f.root,'Choose my folder');await settle();click(f.root,'Select Projects');await settle();click(f.root,'Review access');await settle();click(f.root,'Confirm access');await settle()
|
|
141
|
+
click(f.root,'Revoke');await settle()
|
|
142
|
+
expect(f.root.textContent).toContain('already sent or written')
|
|
143
|
+
click(f.root,'Confirm revoke');await settle()
|
|
144
|
+
expect(f.root.textContent).toContain('Revoked; stopping transfers')
|
|
145
|
+
} finally {cleanup(f)}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('unmount aborts late browse and cancels its context without creating a grant',async()=> {
|
|
149
|
+
const provider=createMockNasService().connect('normal1'), base=provider.list.bind(provider)
|
|
150
|
+
let resolve!:()=>void, requestSignal!:AbortSignal
|
|
151
|
+
provider.list=async(...args)=>{requestSignal=args[3];await new Promise<void>(r=>resolve=r);return base(...args)}
|
|
152
|
+
const cancel=vi.spyOn(provider,'cancel'), f=fixture(provider)
|
|
153
|
+
await settle();click(f.root,'Choose my folder');await settle();f.app.unmount()
|
|
154
|
+
expect(requestSignal.aborted).toBe(true);expect(cancel).toHaveBeenCalled()
|
|
155
|
+
resolve();await settle();expect(f.root.childElementCount).toBe(0);f.root.remove()
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test('late begin after close is canceled and rapid double review consumes one intent',async()=> {
|
|
159
|
+
const provider=createMockNasService().connect('normal1'), begin=provider.begin.bind(provider)
|
|
160
|
+
let release!:()=>void
|
|
161
|
+
provider.begin=async(kind,signal)=>{await new Promise<void>(r=>release=r);return begin(kind,new AbortController().signal)}
|
|
162
|
+
const cancel=vi.spyOn(provider,'cancel'), prepare=vi.spyOn(provider,'prepare'), f=fixture(provider)
|
|
163
|
+
try {
|
|
164
|
+
await settle();click(f.root,'Choose my folder');await settle();click(f.root,'Cancel');await settle()
|
|
165
|
+
release();await settle();expect(cancel).toHaveBeenCalled();expect(f.root.querySelector('[role="dialog"]')).toBeNull()
|
|
166
|
+
provider.begin=begin
|
|
167
|
+
click(f.root,'Choose my folder');await settle();click(f.root,'Select Projects');await settle()
|
|
168
|
+
click(f.root,'Review access');click(f.root,'Review access');await settle()
|
|
169
|
+
expect(prepare).toHaveBeenCalledTimes(1)
|
|
170
|
+
} finally {cleanup(f)}
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('long folder names stay text, and revoked without Q uses pending cleanup copy',async()=> {
|
|
174
|
+
const name='<img src=x onerror=alert(1)> '+ '\u5e74\u5ea6\u5831\u544a'.repeat(20)
|
|
175
|
+
const provider=createMockNasService({personalFolders:[name]}).connect('normal1'), f=fixture(provider)
|
|
176
|
+
try {
|
|
177
|
+
await settle();click(f.root,'Choose my folder');await settle()
|
|
178
|
+
expect(f.root.textContent).toContain(name);expect(f.root.querySelector('img')).toBeNull()
|
|
179
|
+
click(f.root,'Cancel');await settle()
|
|
180
|
+
provider.settings=async()=>({canManageShared:false,items:[{grantId:'fixture',revision:2,scopeKind:'user-personal',displayName:name,targetKind:'directory',actions:['nas.files.read'],effectiveState:'revoked',createdAt:1000,canRevoke:true,cleanupRequired:true}]})
|
|
181
|
+
click(f.root,'Refresh access');await settle()
|
|
182
|
+
expect(f.root.textContent).toContain('Revoked; cleanup needs verification')
|
|
183
|
+
expect(f.root.textContent).toContain('Read files')
|
|
184
|
+
expect(f.root.textContent).not.toContain('List folders')
|
|
185
|
+
} finally {cleanup(f)}
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
test('S8 production defaults display unavailable for normal/admin even when lifecycle is ready',async()=> {
|
|
190
|
+
for(const user of ['normal1','admin']) for(const locale of Object.keys(catalogs)) {
|
|
191
|
+
const f=fixture(undefined,user,locale),copy=nasCopy(locale)
|
|
192
|
+
try {
|
|
193
|
+
f.ctx.call=vi.fn(async(method:string)=> {
|
|
194
|
+
if(method==='app.status') return {package:'SamePackage',version:'1',user,admin:user==='admin'}
|
|
195
|
+
if(method==='integration.status') return {state:'ready',packageRunning:'running',scheduler:'ready'}
|
|
196
|
+
if(method==='personal.list') return {items:[]}
|
|
197
|
+
throw {code:'unknown_method'}
|
|
198
|
+
})
|
|
199
|
+
f.reopen.forEach(fn=>fn());await settle()
|
|
200
|
+
expect(f.root.textContent).toContain(copy.unavailableMessage)
|
|
201
|
+
expect(f.root.textContent).toContain(copy.pilotUnavailable)
|
|
202
|
+
expect(f.root.textContent).toContain(copy.noUnlock)
|
|
203
|
+
expect(f.root.textContent).not.toContain(copy.choosePersonal)
|
|
204
|
+
expect(f.root.textContent).not.toContain(copy.chooseShared)
|
|
205
|
+
expect(f.root.textContent).not.toContain(copy.emptyGrant)
|
|
206
|
+
expect(f.ctx.call.mock.calls.some(([method])=>method.startsWith('nas.'))).toBe(false)
|
|
207
|
+
} finally {cleanup(f)}
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('S8 unavailable connected providers are ignored; only development mocks have a picker',async()=> {
|
|
212
|
+
const connected=createMockNasService().connect('normal1')
|
|
213
|
+
Object.defineProperty(connected,'mode',{value:'connected'})
|
|
214
|
+
const settings=vi.spyOn(connected,'settings'),f=fixture(connected)
|
|
215
|
+
try {
|
|
216
|
+
await settle()
|
|
217
|
+
expect(f.root.textContent).toContain(nasCopy('en').unavailableMessage)
|
|
218
|
+
expect(f.root.textContent).not.toContain('Choose my folder')
|
|
219
|
+
expect(settings).not.toHaveBeenCalled()
|
|
220
|
+
} finally {cleanup(f)}
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
test('S8 safe error codes distinguish missing proof, denial, missing method and app authentication',()=> {
|
|
225
|
+
for(const copy of Object.values(catalogs)) {
|
|
226
|
+
for(const [code,key] of [['unavailable','errorNativeProof'],['denied','errorDenied'],['unknown_method','errorUnknownMethod'],['unsupported','errorUnsupported'],['session_required','errorSession'],['auth_unavailable','errorAuthentication']] as const) {
|
|
227
|
+
expect(errorCopy({code,message:'/volume1/private cookie=sid'},copy)).toBe(copy[key])
|
|
228
|
+
expect(errorCopy({code,message:'/volume1/private cookie=sid'},copy)).not.toMatch(/volume1|cookie=sid/)
|
|
229
|
+
}
|
|
230
|
+
expect(new Set(['unavailable','denied','unknown_method','unsupported'].map(code=>errorCopy({code},copy))).size).toBe(4)
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
test('S8 app request failures use localized safe-code copy without native diagnostics',async()=> {
|
|
236
|
+
for(const code of ['denied','unknown_method','unavailable','unsupported','session_required','auth_unavailable']) {
|
|
237
|
+
const f=fixture(undefined,'normal1','cht')
|
|
238
|
+
try {
|
|
239
|
+
await settle()
|
|
240
|
+
f.ctx.call=vi.fn(async()=>{throw {code,message:'/volume1/private cookie=sid'}})
|
|
241
|
+
f.reopen.forEach(fn=>fn());await settle()
|
|
242
|
+
expect(f.root.querySelector('[role="alert"]')?.textContent).toBe(errorCopy({code},nasCopy('cht')))
|
|
243
|
+
expect(f.root.textContent).not.toMatch(/volume1|cookie=sid/)
|
|
244
|
+
} finally {cleanup(f)}
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('S1R risk and reauthorization are disclosed in nine unavailable UIs without data actions',async()=> {
|
|
249
|
+
for(const locale of Object.keys(catalogs)) {
|
|
250
|
+
const copy=nasCopy(locale) as any, f=fixture(undefined,'normal1',locale)
|
|
251
|
+
try {
|
|
252
|
+
await settle()
|
|
253
|
+
for(const key of ['relaxedRisk','relaxedReauthorize','relaxedPermissions']) {
|
|
254
|
+
expect(copy[key],`${locale}:${key}`).toBeTypeOf('string')
|
|
255
|
+
expect(f.root.textContent).toContain(copy[key])
|
|
256
|
+
}
|
|
257
|
+
expect(f.root.textContent).not.toContain(copy.choosePersonal)
|
|
258
|
+
expect(f.ctx.call.mock.calls.some(([method])=>method.startsWith('nas.'))).toBe(false)
|
|
259
|
+
} finally {cleanup(f)}
|
|
260
|
+
}
|
|
261
|
+
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"compilerOptions":{"target":"ES2022","module":"ESNext","moduleResolution":"Bundler","strict":true,"lib":["ES2022","DOM"],"skipLibCheck":true,"types":["vite/client"]},"include":["src/**/*.ts","src/**/*.vue","vite.config.ts"]}
|
|
1
|
+
{"compilerOptions":{"target":"ES2022","module":"ESNext","moduleResolution":"Bundler","resolveJsonModule":true,"strict":true,"lib":["ES2022","DOM"],"skipLibCheck":true,"types":["vite/client"]},"include":["src/**/*.ts","src/**/*.vue","vite.config.ts"]}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { defineConfig } from 'vite'
|
|
2
2
|
import vue from '@vitejs/plugin-vue'
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
// @ts-expect-error The shared notice tool is a plain Node module.
|
|
4
|
+
import { writeThirdPartyNotices } from './third-party-notices.mjs'
|
|
5
|
+
export default defineConfig(({command}) => ({
|
|
6
|
+
plugins: [vue(), {name: 'dependency-notices', closeBundle() {writeThirdPartyNotices('.', 'dist/LICENSES', {ui: true})}}],
|
|
5
7
|
// Library mode must replace Vue dependency env guards for the DSM browser.
|
|
6
|
-
define: {'process.env.NODE_ENV': JSON.stringify('production')},
|
|
8
|
+
define: command === 'build' ? {'process.env.NODE_ENV': JSON.stringify('production')} : {},
|
|
7
9
|
build: {lib: {entry: 'src/main.ts', formats: ['iife'], name: 'DsmVueApp', fileName: () => 'app.js'}, cssCodeSplit: false, rollupOptions: {output: {assetFileNames: 'app.[ext]'}}},
|
|
8
|
-
})
|
|
10
|
+
}))
|