import { randomUUID } from "node:crypto"; const commandRoutes = new Map([ ["owner-scopes:ensure", "/internal/v1/management/owner-scopes:ensure"], ["projects:ensure", "/internal/v1/management/projects:ensure"], ["collections:ensure", "/internal/v1/management/collections:ensure"], ["project-grants:upsert", "/internal/v1/management/project-grants:upsert"], ["adapter-packages:ensure", "/internal/v1/management/adapter-packages:ensure"], ["adapter-versions:register", "/internal/v1/management/adapter-versions:register"], ["model-profiles:register", "/internal/v1/management/model-profiles:register"], ["edges:ensure", "/internal/v1/management/edges:ensure"], ["routes:ensure", "/internal/v1/management/routes:ensure"], ["enrollment-intents:ensure", "/internal/v1/management/enrollment-intents:ensure"], ["devices:claim", "/internal/v1/management/devices:claim"], ["device-bindings:ensure", "/internal/v1/management/device-bindings:ensure"], ["device-bindings:revoke", "/internal/v1/management/device-bindings:revoke"], [ "device-configuration-revisions:create", "/internal/v1/management/device-configuration-revisions:create", ], [ "device-configurations:set-desired", "/internal/v1/management/device-configurations:set-desired", ], ["commands:service-ping", "/internal/v1/commands:service-ping"], ]); export function createDeviceCoreClient({ baseUrl, token, fetchImpl = fetch } = {}) { const endpoint = normalizeBaseUrl(baseUrl); if (typeof token !== "string" || token.length < 32) { throw serviceError("device_core_token_invalid", 503); } async function request(pathname, actor, init = {}) { const response = await fetchImpl(new URL(pathname, endpoint), { ...init, headers: { Accept: "application/json", Authorization: `Bearer ${token}`, ...actorHeaders(actor), ...(init.headers ?? {}), }, signal: AbortSignal.timeout(10_000), }); const body = await response.json().catch(() => null); if (!response.ok || body?.ok !== true) { throw serviceError( safeCoreError(body?.error), response.status >= 400 && response.status < 600 ? response.status : 502, ); } return body; } return { configured: true, async listProjects(actor) { return request("/internal/v1/query/projects", actor) .then((body) => body.projects); }, async getWorkspace(actor, projectRef) { const projectId = entityId(projectRef, "project"); return request(`/internal/v1/query/projects/${projectId}/workspace`, actor) .then((body) => body.workspace); }, async execute(command, actor, input, idempotencyKey) { const pathname = commandRoutes.get(command); if (!pathname) throw serviceError("device_manager_command_invalid", 404); if (!/^[\x21-\x7e]{8,256}$/.test(idempotencyKey || "")) { throw serviceError("device_idempotency_key_invalid", 400); } return request(pathname, actor, { method: "POST", headers: { "Content-Type": "application/json", "Idempotency-Key": idempotencyKey, }, body: JSON.stringify(input), }).then(({ replayed, result }) => ({ replayed, result })); }, }; } export function createLocalPreviewDeviceCore({ fixture = null } = {}) { const ownerScopes = new Map(); const projects = new Map(); const collections = new Map(); const adapterPackages = new Map(); const adapterVersions = new Map(); const modelProfiles = new Map(); const edges = new Map(); const routes = new Map(); const enrollments = new Map(); const devices = new Map(); const sessions = new Map(); const bindings = new Map(); const grants = new Map(); const configurationRevisions = new Map(); const configurationStates = new Map(); const auditEvents = []; const commands = new Map(); function now() { return new Date().toISOString(); } function projectValues(store, projectRef) { return [...store.values()].filter((value) => value.projectRef === projectRef); } function audit(actor, projectRef, eventType, refs = {}) { auditEvents.unshift({ auditEventRef: `audit-event:${randomUUID()}`, eventType, actorRef: actor.userRef, deviceRef: refs.deviceRef ?? null, discoveryRef: refs.discoveryRef ?? null, projectRef, occurredAt: now(), }); } function projectSummary(project) { const projectCollections = [...collections.values()] .filter((collection) => collection.projectRef === project.projectRef); return { ...project, counts: { devices: projectValues(devices, project.projectRef).length, collections: projectCollections.length, discoveries: 0, }, }; } function workspace(projectRef) { const project = projects.get(projectRef); if (!project) throw serviceError("device_project_not_found", 404); return { project: projectSummary(project), devices: projectValues(devices, projectRef) .map(({ projectRef: _projectRef, ...device }) => device), discoveries: [], enrollments: projectValues(enrollments, projectRef), collections: projectValues(collections, projectRef) .map(({ projectRef: _projectRef, ...collection }) => collection), adapterPackages: [...adapterPackages.values()], adapterVersions: [...adapterVersions.values()], modelProfiles: [...modelProfiles.values()], edges: [...edges.values()], routes: projectValues(routes, projectRef), sessions: projectValues(sessions, projectRef) .map(({ projectRef: _projectRef, ...session }) => session), bindings: projectValues(bindings, projectRef), configurationRevisions: projectValues(configurationRevisions, projectRef), configurationStates: projectValues(configurationStates, projectRef), commands: projectValues(commands, projectRef), auditEvents: auditEvents.filter((event) => event.projectRef === projectRef), grants: projectValues(grants, projectRef), policies: { commandTransport: fixture === "arusnavi-b2" ? "typed-service-ping-v1" : "disabled", commandPlanningApi: fixture === "arusnavi-b2" ? "enabled" : "disabled", identifierProjection: "masked-only", auditPayloadProjection: "metadata-only", }, }; } if (fixture === "arusnavi-b2") seedArusnaviB2Preview({ ownerScopes, projects, devices, modelProfiles, edges, routes, sessions, configurationStates, }); else if (fixture != null && fixture !== "") { throw serviceError("device_manager_preview_fixture_invalid", 400); } return { configured: true, async listProjects() { return [...projects.values()].map(projectSummary); }, async getWorkspace(_actor, projectRef) { return workspace(projectRef); }, async execute(command, actor, input) { if (command === "commands:service-ping") { if (fixture !== "arusnavi-b2") { throw serviceError("device_command_transport_disabled", 409); } const device = devices.get(input.deviceRef); if (!device || device.projectRef !== input.projectRef) { throw serviceError("device_command_route_unavailable", 409); } if (typeof input.accessCode !== "string" || !/^\d{6}$/.test(input.accessCode)) { throw serviceError("device_service_ping_access_code_invalid", 400); } const commandRef = `command:${randomUUID()}`; const at = now(); const view = { commandRef, projectRef: input.projectRef, deviceRef: input.deviceRef, deviceName: device.displayName, commandKey: `preview-service-ping-${randomUUID()}`, commandCatalogRef: "arusnavi.b2.internal.v1:service-ping", commandType: "service.ping", riskClass: "low", lifecycleState: "queued", plannedAt: at, expiresAt: new Date(Date.now() + Number(input.expiresInSeconds) * 1000).toISOString(), confirmedAt: null, dispatchedAt: null, acknowledgedAt: null, terminalAt: null, terminalReasonCode: null, createdAt: at, updatedAt: at, }; commands.set(commandRef, view); return { replayed: false, result: view }; } if (command === "owner-scopes:ensure") { const key = `${input.scopeKind}:${input.ownerRef}`; const created = !ownerScopes.has(key); const scope = { ownerScopeRef: ownerScopes.get(key)?.ownerScopeRef || `owner-scope:${randomUUID()}`, scopeKind: input.scopeKind, ownerRef: input.ownerRef, displayName: input.displayName, lifecycleState: "active", }; ownerScopes.set(key, scope); return { replayed: false, result: { created, ownerScope: scope } }; } if (command === "projects:ensure") { const scope = ownerScopes.get(`${input.scopeKind}:${input.ownerRef}`); if (!scope) throw serviceError("device_owner_scope_not_found", 404); const existing = [...projects.values()].find((project) => project.ownerScope.ownerRef === input.ownerRef && project.projectKey === input.projectKey ); const projectRef = existing?.projectRef || `project:${randomUUID()}`; const project = { projectRef, projectKey: input.projectKey, name: input.name, description: input.description ?? null, lifecycleState: "active", ownerScope: scope, access: { projectRole: "owner", capabilities: ownerCapabilities }, counts: { devices: 0, collections: 0, discoveries: 0 }, createdAt: existing?.createdAt || new Date().toISOString(), updatedAt: new Date().toISOString(), }; projects.set(projectRef, project); if (!existing) { const grantRef = `grant:${randomUUID()}`; grants.set(grantRef, { grantRef, projectRef, principalKind: "user", principalRef: actor.userRef, projectRole: "owner", capabilityAllow: [], capabilityDeny: [], lifecycleState: "active", }); audit(actor, projectRef, "project.created"); } return { replayed: false, result: { created: !existing, project } }; } if (command === "collections:ensure") { const projectRef = input.projectRef; if (!projects.has(projectRef)) throw serviceError("device_project_not_found", 404); const existing = [...collections.values()].find((collection) => collection.projectRef === projectRef && collection.collectionKey === input.collectionKey ); const collectionRef = existing?.collectionRef || `collection:${randomUUID()}`; const collection = { collectionRef, projectRef, collectionKey: input.collectionKey, name: input.name, description: input.description ?? null, lifecycleState: "active", memberCount: existing?.memberCount || 0, createdAt: existing?.createdAt || new Date().toISOString(), updatedAt: new Date().toISOString(), }; collections.set(collectionRef, collection); audit(actor, projectRef, createdEvent(existing, "collection")); return { replayed: false, result: { created: !existing, collection } }; } if (command === "project-grants:upsert") { if (!projects.has(input.projectRef)) { throw serviceError("device_project_not_found", 404); } const existing = [...grants.values()].find((grant) => grant.projectRef === input.projectRef && grant.principalKind === input.principalKind && grant.principalRef === input.principalRef ); const grantRef = existing?.grantRef || `grant:${randomUUID()}`; const grant = { grantRef, projectRef: input.projectRef, principalKind: input.principalKind, principalRef: input.principalRef, projectRole: input.projectRole, capabilityAllow: input.capabilityAllow ?? [], capabilityDeny: input.capabilityDeny ?? [], lifecycleState: input.lifecycleState ?? "active", }; grants.set(grantRef, grant); audit(actor, input.projectRef, createdEvent(existing, "project_grant")); return { replayed: false, result: { created: !existing, grant } }; } if (command === "adapter-packages:ensure") { requirePlatformOwner(actor); const existing = [...adapterPackages.values()].find( (entry) => entry.packageKey === input.packageKey, ); const adapterPackageRef = existing?.adapterPackageRef || `adapter-package:${randomUUID()}`; assertPreviewTransition( existing?.lifecycleState, input.lifecycleState ?? "active", previewTransitions.adapterPackage, "device_adapter_package_transition_invalid", ); const adapterPackage = { adapterPackageRef, packageKey: input.packageKey, displayName: input.displayName, publisherRef: input.publisherRef, lifecycleState: input.lifecycleState ?? "active", createdAt: existing?.createdAt || now(), updatedAt: now(), }; adapterPackages.set(adapterPackageRef, adapterPackage); return { replayed: false, result: { created: !existing, adapterPackage } }; } if (command === "adapter-versions:register") { requirePlatformOwner(actor); const adapterPackage = adapterPackages.get(input.adapterPackageRef); if (!adapterPackage) { throw serviceError("device_adapter_package_not_found", 404); } if (adapterPackage.lifecycleState !== "active") { throw serviceError("device_adapter_package_inactive", 409); } const existing = [...adapterVersions.values()].find((entry) => entry.adapterPackageRef === input.adapterPackageRef && entry.version === input.version ); const adapterVersionRef = existing?.adapterVersionRef || `adapter-version:${randomUUID()}`; assertPreviewTransition( existing?.lifecycleState, input.lifecycleState ?? "draft", previewTransitions.catalogVersion, "device_adapter_version_transition_invalid", ); const adapterVersion = { adapterVersionRef, adapterPackageRef: input.adapterPackageRef, version: input.version, runtimePackageRef: input.runtimePackageRef, contentDigest: input.contentDigest, contractVersion: input.contractVersion, capabilities: input.capabilities ?? [], lifecycleState: input.lifecycleState ?? "draft", createdAt: existing?.createdAt || now(), updatedAt: now(), }; adapterVersions.set(adapterVersionRef, adapterVersion); return { replayed: false, result: { created: !existing, adapterVersion } }; } if (command === "model-profiles:register") { requirePlatformOwner(actor); const adapterVersion = adapterVersions.get(input.adapterVersionRef); if (!adapterVersion) { throw serviceError("device_adapter_version_not_found", 404); } const existing = modelProfiles.get(input.profileRef); const lifecycleState = input.lifecycleState ?? "draft"; if (lifecycleState === "active" && adapterVersion.lifecycleState !== "active") { throw serviceError("device_model_profile_adapter_not_active", 409); } assertPreviewTransition( existing?.lifecycleState, lifecycleState, previewTransitions.catalogVersion, "device_model_profile_transition_invalid", ); const modelProfile = { modelProfileRef: input.profileRef, adapterVersionRef: input.adapterVersionRef, schemaVersion: input.schemaVersion, vendor: input.vendor, model: input.model, deviceType: input.deviceType, protocol: input.protocol, schemaArtifactRef: input.schemaArtifactRef, profileDigest: input.profileDigest, capabilities: input.capabilities ?? [], lifecycleState, createdAt: existing?.createdAt || now(), updatedAt: now(), }; modelProfiles.set(input.profileRef, modelProfile); return { replayed: false, result: { created: !existing, modelProfile } }; } if (command === "edges:ensure") { requirePlatformOwner(actor); const existing = [...edges.values()].find( (entry) => entry.edgeKey === input.edgeKey, ); const edgeRef = existing?.edgeRef || `edge:${randomUUID()}`; assertPreviewTransition( existing?.lifecycleState, input.lifecycleState ?? "provisioning", previewTransitions.edge, "device_edge_transition_invalid", ); const edge = { edgeRef, edgeKey: input.edgeKey, displayName: input.displayName, deploymentRef: input.deploymentRef ?? null, lifecycleState: input.lifecycleState ?? "provisioning", createdAt: existing?.createdAt || now(), updatedAt: now(), }; edges.set(edgeRef, edge); return { replayed: false, result: { created: !existing, edge } }; } if (command === "routes:ensure") { if (!projects.has(input.projectRef)) { throw serviceError("device_project_not_found", 404); } const edge = edges.get(input.edgeRef); const profile = modelProfiles.get(input.modelProfileRef); if (!edge) throw serviceError("device_edge_not_found", 404); if (!profile) throw serviceError("device_model_profile_not_found", 404); const existing = projectValues(routes, input.projectRef).find( (entry) => entry.routeKey === input.routeKey, ); const routeRef = existing?.routeRef || `route:${randomUUID()}`; const lifecycleState = input.lifecycleState ?? "draft"; if ( lifecycleState === "active" && (edge.lifecycleState !== "active" || profile.lifecycleState !== "active") ) { throw serviceError("device_route_dependency_not_active", 409); } assertPreviewTransition( existing?.lifecycleState, lifecycleState, previewTransitions.route, "device_route_transition_invalid", ); const route = { routeRef, projectRef: input.projectRef, routeKey: input.routeKey, displayName: input.displayName, edgeRef: input.edgeRef, edgeName: edge.displayName, modelProfileRef: input.modelProfileRef, profileName: `${profile.vendor} ${profile.model}`, listenerRef: input.listenerRef, protocol: input.protocol, direction: input.direction ?? "telemetry", lifecycleState, sessionCount: 0, activeSessionCount: 0, createdAt: existing?.createdAt || now(), updatedAt: now(), }; routes.set(routeRef, route); audit(actor, input.projectRef, createdEvent(existing, "route")); return { replayed: false, result: { created: !existing, route } }; } if (command === "device-bindings:ensure") { if (!projects.has(input.projectRef)) { throw serviceError("device_project_not_found", 404); } if (input.source?.kind !== "collection" || !collections.has(input.source.ref)) { throw serviceError("device_binding_source_not_found", 404); } const existing = projectValues(bindings, input.projectRef).find( (entry) => entry.bindingKey === input.bindingKey, ); const bindingRef = existing?.bindingRef || `binding:${randomUUID()}`; const source = collections.get(input.source.ref); const binding = { bindingRef, projectRef: input.projectRef, bindingKey: input.bindingKey, displayName: input.displayName, source: { kind: input.source.kind, ref: input.source.ref, displayName: source.name, }, target: { kind: input.targetKind, ref: input.targetRef }, capabilities: input.capabilities, lifecycleState: "pending_external_approval", sourceApprovedAt: now(), createdAt: existing?.createdAt || now(), updatedAt: now(), }; bindings.set(bindingRef, binding); audit(actor, input.projectRef, createdEvent(existing, "device_binding")); return { replayed: false, result: { created: !existing, binding } }; } if (command === "device-bindings:revoke") { const binding = bindings.get(input.bindingRef); if (!binding || binding.projectRef !== input.projectRef) { throw serviceError("device_binding_not_found", 404); } const revoked = { ...binding, lifecycleState: "revoked", updatedAt: now() }; bindings.set(binding.bindingRef, revoked); audit(actor, input.projectRef, "device_binding.revoked"); return { replayed: false, result: { revoked: true, binding: revoked } }; } if (command === "device-configuration-revisions:create") { throw serviceError("device_not_found", 404); } if (command === "device-configurations:set-desired") { throw serviceError("device_configuration_revision_not_found", 404); } if (command === "enrollment-intents:ensure") { if (!projects.has(input.projectRef)) { throw serviceError("device_project_not_found", 404); } const route = routes.get(input.routeRef); if (!route || route.projectRef !== input.projectRef) { throw serviceError("device_route_not_found", 404); } if (route.lifecycleState !== "active") { throw serviceError("device_enrollment_route_inactive", 409); } if (route.modelProfileRef !== input.modelProfileRef) { throw serviceError("device_enrollment_profile_mismatch", 409); } if ( input.identifier?.kind !== "imei" || typeof input.identifier.value !== "string" || !/^\d{15}$/.test(input.identifier.value) ) { throw serviceError("restricted_identifier_imei_invalid", 400); } const existing = projectValues(enrollments, input.projectRef).find( (entry) => entry.enrollmentKey === input.enrollmentKey, ); const enrollmentIntentRef = existing?.enrollmentIntentRef || `enrollment-intent:${randomUUID()}`; const enrollment = { enrollmentIntentRef, projectRef: input.projectRef, enrollmentKey: input.enrollmentKey, displayName: input.displayName, routeRef: input.routeRef, modelProfileRef: input.modelProfileRef, expectedIdentifier: { kind: "imei", masked: `***********${input.identifier.value.slice(-4)}`, }, lifecycleState: "pending", observedDiscoveryRef: null, claimedDeviceRef: null, expiresAt: input.expiresAt ?? null, createdAt: existing?.createdAt || now(), updatedAt: now(), }; enrollments.set(enrollmentIntentRef, enrollment); audit(actor, input.projectRef, createdEvent(existing, "enrollment_intent")); const { expectedIdentifier, ...safeEnrollment } = enrollment; return { replayed: false, result: { created: !existing, enrollmentIntent: { ...safeEnrollment, identifier: expectedIdentifier, }, }, }; } if (command === "devices:claim") { throw serviceError("device_discovery_not_found", 404); } throw serviceError("device_manager_command_invalid", 404); }, snapshot() { return { ownerScopes, projects, collections, adapterPackages, adapterVersions, modelProfiles, edges, routes, enrollments, devices, sessions, bindings, grants, configurationRevisions, configurationStates, commands, auditEvents, }; }, }; } function createdEvent(existing, resource) { return `${resource}.${existing ? "updated" : "created"}`; } function requirePlatformOwner(actor) { if (actor?.hubRole !== "owner") { throw serviceError("device_platform_catalog_access_denied", 403); } } function seedArusnaviB2Preview({ ownerScopes, projects, devices, modelProfiles, edges, routes, sessions, configurationStates, }) { const timestamp = new Date().toISOString(); const ownerScopeRef = "owner-scope:78da71d5-f48f-4de0-8e47-729f6d644151"; const projectRef = "project:ad7b357c-c7ac-4bf8-a638-c7f956e9aa71"; const deviceRef = "device:b6a55921-7888-44b5-a93e-241aa2fdd3d7"; const edgeRef = "edge:73da0c42-a641-4559-b8f7-23509b60bfe9"; const routeRef = "route:fef9b7a0-a462-4d68-9991-af026203368b"; const sessionRef = "session:57ead610-47de-45f7-a42d-fbe4fa0aba38"; const scope = { ownerScopeRef, scopeKind: "personal", ownerRef: "user:local-device-admin", displayName: "Local Device Admin", lifecycleState: "active", }; ownerScopes.set("personal:user:local-device-admin", scope); projects.set(projectRef, { projectRef, projectKey: "arusnavi-b2-preview", name: "ARUSNAVI B2 preview", description: "Локальная визуальная фикстура без реальных идентификаторов и команд", lifecycleState: "active", ownerScope: scope, access: { projectRole: "owner", capabilities: ownerCapabilities }, counts: { devices: 1, collections: 0, discoveries: 0 }, createdAt: timestamp, updatedAt: timestamp, }); modelProfiles.set("arusnavi.b2.internal.v1", { modelProfileRef: "arusnavi.b2.internal.v1", adapterVersionRef: null, schemaVersion: "1.0.0", vendor: "ARUSNAVI", model: "B2", deviceType: "tracker", protocol: "INTERNAL", schemaArtifactRef: "schema:arusnavi.b2.internal.v1", profileDigest: null, capabilities: ["telemetry", "configuration", "commands"], lifecycleState: "active", createdAt: timestamp, updatedAt: timestamp, }); edges.set(edgeRef, { edgeRef, edgeKey: "preview-edge", displayName: "Preview VPS edge", deploymentRef: "deployment:preview", lifecycleState: "active", createdAt: timestamp, updatedAt: timestamp, }); routes.set(routeRef, { routeRef, projectRef, routeKey: "preview-b2-route", displayName: "B2 direct preview", edgeRef, edgeName: "Preview VPS edge", modelProfileRef: "arusnavi.b2.internal.v1", profileName: "ARUSNAVI B2", listenerRef: "listener:preview", protocol: "INTERNAL", direction: "bidirectional", lifecycleState: "active", sessionCount: 1, activeSessionCount: 1, createdAt: timestamp, updatedAt: timestamp, }); devices.set(deviceRef, { projectRef, deviceRef, deviceKey: "pilot-b2-preview", displayName: "Пилотный B2", modelProfileRef: "arusnavi.b2.internal.v1", lifecycleState: "active", identifier: { kind: "imei", masked: "***********0001" }, session: { state: "online", lastSeenAt: timestamp }, reported: { observedAt: timestamp, identity: { iccid1: "****************1111", iccid2: "****************2222" }, firmware: { currentVersion: "0.02", appliedAt: timestamp, availableVersion: "0.05" }, configuration: { monitoring: { servers: [ { host: "legacy.example.invalid", port: 20623, protocol: "INTERNAL", identity: "0" }, { host: "direct.example.invalid", port: 9921, protocol: "INTERNAL", identity: "0" }, ], }, transmission: { navigation: { position: true, motion: true, hdop: false } }, trajectory: { normal: { courseDeltaDegrees: 15, speedDeltaKph: 10, distanceMeters: 15, parkingIntervalSeconds: 15 }, roaming: { courseDeltaDegrees: 20, speedDeltaKph: 50, distanceMeters: 1000, parkingIntervalSeconds: 300 }, }, navigation: { sources: { satellite: true, wifi: false, lbs: false, tag: false }, constellations: { gps: true, glonass: true, galileo: false, beidou: false }, filter: { minimumSatellites: 4, maximumHdopTimesTen: 30 }, }, }, telemetry: { navigation: { latitude: "55.7500", longitude: "37.6200", speedKph: 18, altitudeMeters: 156, satellites: 12, courseDegrees: 84, hdop: 1.2, }, gsm: { signal: 79, operator: "preview", lac: "masked", cid: "masked" }, system: { externalVoltageMv: 13240, internalVoltageMv: 4120, status: "Норма" }, }, }, createdAt: timestamp, updatedAt: timestamp, }); sessions.set(sessionRef, { sessionRef, projectRef, routeRef, routeName: "B2 direct preview", deviceRef, deviceName: "Пилотный B2", protocol: "INTERNAL", lifecycleState: "online", connectedAt: timestamp, lastSeenAt: timestamp, disconnectedAt: null, closeReasonCode: null, frameCount: 1842, byteCount: 734208, }); configurationStates.set(deviceRef, { projectRef, deviceRef, deviceName: "Пилотный B2", desiredConfigurationRevisionRef: null, appliedConfigurationRevisionRef: null, appliedAt: null, updatedAt: timestamp, }); } const previewTransitions = Object.freeze({ adapterPackage: Object.freeze({ active: Object.freeze(["active", "retired"]), retired: Object.freeze(["retired"]), }), catalogVersion: Object.freeze({ draft: Object.freeze(["draft", "active", "retired"]), active: Object.freeze(["active", "retired"]), retired: Object.freeze(["retired"]), }), edge: Object.freeze({ provisioning: Object.freeze(["provisioning", "active", "retired"]), active: Object.freeze(["active", "suspended", "retired"]), suspended: Object.freeze(["suspended", "active", "retired"]), retired: Object.freeze(["retired"]), }), route: Object.freeze({ draft: Object.freeze(["draft", "active", "retired"]), active: Object.freeze(["active", "suspended", "retired"]), suspended: Object.freeze(["suspended", "active", "retired"]), retired: Object.freeze(["retired"]), }), }); function assertPreviewTransition(previous, next, transitions, code) { if (!previous) return; if (!transitions[previous]?.includes(next)) { throw serviceError(code, 409); } } const ownerCapabilities = Object.freeze([ "project.read", "project.manage", "access.manage", "inventory.read", "device.enroll", "device.claim", "device.transfer", "collection.manage", "route.manage", "binding.manage", "telemetry.observe", "configuration.read", "configuration.manage", "command.plan", "command.confirm", "command.dispatch", "credential.manage", "audit.read", ]); function actorHeaders(actor) { if (!actor || typeof actor !== "object") throw serviceError("device_actor_required", 401); return { "X-NODEDC-User-Ref": actor.userRef, "X-NODEDC-Hub-Role": actor.hubRole, "X-NODEDC-Group-Refs": (actor.groupRefs ?? []).join(","), "X-NODEDC-Owner-Scopes": (actor.ownerScopes ?? []) .map((scope) => `${scope.scopeKind}=${scope.ownerRef}`) .join(","), }; } function entityId(value, prefix) { const match = String(value || "").match(new RegExp( `^${prefix}:([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$`, "i", )); if (!match) throw serviceError(`device_${prefix}_ref_invalid`, 400); return match[1].toLowerCase(); } function normalizeBaseUrl(value) { if (typeof value !== "string" || value.trim() === "") { throw serviceError("device_core_url_required", 503); } const url = new URL(value); if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) { throw serviceError("device_core_url_invalid", 503); } url.pathname = url.pathname.replace(/\/$/, "") || "/"; return url; } function safeCoreError(value) { return typeof value === "string" && /^device_[a-z0-9._:-]{2,120}$/.test(value) ? value : "device_core_unavailable"; } function serviceError(code, statusCode) { const error = new Error(code); error.statusCode = statusCode; return error; }