@flashcatcloud/hvigor-plugin
Advanced tools
+14
-0
| # Changelog | ||
| ## 0.1.3 | ||
| - Default symbol-upload host is now `https://ci.flashcat.cloud` (was | ||
| `https://browser.flashcat.cloud`, which 404s — that host is RUM ingest only). | ||
| - Honour `FLASHCAT_SOURCEMAP_INTAKE_URL` first (same variable as Android / | ||
| flashcat-cli); `FLASHCAT_ENDPOINT` remains a legacy alias and emits a warning. | ||
| - Explicit empty `endpoint` / empty env vars **skip** upload instead of falling | ||
| back to SaaS. Values are validated with `new URL()` (http(s), no query/hash). | ||
| - Warn when the resolved host is a known RUM-ingest-only host | ||
| (`browser.flashcat.cloud` / `jira.flashcat.cloud`). | ||
| - If the configured endpoint already ends with `/sourcemap/upload`, do not | ||
| append the path again. | ||
| - `pluginVersion` fallback reads `package.json` (no hand-copied literal). | ||
| ## 0.1.2 | ||
@@ -4,0 +18,0 @@ |
+4
-3
@@ -15,5 +15,6 @@ export interface HvigorNode { | ||
| export interface FlashcatPluginOptions { | ||
| /** RUM ingest base URL. Optional — defaults to $FLASHCAT_ENDPOINT, else the SaaS | ||
| * ingest (https://browser.flashcat.cloud). Set it (or the env var) for staging / | ||
| * self-hosted. */ | ||
| /** Symbol-upload base URL. Optional — when omitted, uses $FLASHCAT_SOURCEMAP_INTAKE_URL, | ||
| * then legacy $FLASHCAT_ENDPOINT, then SaaS `https://ci.flashcat.cloud`. | ||
| * When set (including empty string), that value is used alone: empty/invalid skips | ||
| * upload instead of falling back to SaaS. Private deploys: scheme + host, no path. */ | ||
| endpoint?: string; | ||
@@ -20,0 +21,0 @@ apiKey: string; |
+17
-4
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.flashcatSymbolUploadPlugin = flashcatSymbolUploadPlugin; | ||
| const node_module_1 = require("node:module"); | ||
| const index_ts_1 = require("./index.js"); | ||
| const upload_ts_1 = require("./upload.js"); | ||
| // Keep the reported plugin version in sync with package.json (no hand-copied literal). | ||
| const requirePackageJson = (0, node_module_1.createRequire)(__filename); | ||
| const PACKAGE_VERSION = requirePackageJson('../package.json').version; | ||
| /** | ||
@@ -44,10 +49,18 @@ * Registers an `uploadFlashcatSymbols` task on the module. Runs AFTER the module | ||
| } | ||
| // endpoint may be omitted in config; fall back to env then the SaaS ingest. | ||
| const endpoint = options.endpoint ?? process.env.FLASHCAT_ENDPOINT ?? 'https://browser.flashcat.cloud'; | ||
| const resolved = (0, upload_ts_1.resolveUploadEndpoint)(options.endpoint); | ||
| for (const w of resolved.warnings) { | ||
| // eslint-disable-next-line no-console | ||
| console.warn(`flashcat: ${w}`); | ||
| } | ||
| if (!resolved.ok) { | ||
| // eslint-disable-next-line no-console | ||
| console.warn(`flashcat: ${resolved.reason}`); | ||
| return; | ||
| } | ||
| const cfg = { | ||
| endpoint, | ||
| endpoint: resolved.endpoint, | ||
| apiKey: options.apiKey, | ||
| service: options.service, | ||
| version: options.version, | ||
| pluginVersion: options.pluginVersion ?? '0.1.2' // keep in sync with package.json version | ||
| pluginVersion: options.pluginVersion ?? PACKAGE_VERSION | ||
| }; | ||
@@ -54,0 +67,0 @@ const buildDir = `${node.getNodePath()}/${options.buildDir ?? 'build/default'}`; |
+26
-1
@@ -8,4 +8,29 @@ import type { ArktsSourcemap, NativeSymbol } from './collect.ts'; | ||
| export declare const TYPE_SYMBOL_FILE = "harmony_symbol_file"; | ||
| export type EndpointResolution = { | ||
| ok: true; | ||
| endpoint: string; | ||
| warnings: string[]; | ||
| } | { | ||
| ok: false; | ||
| reason: string; | ||
| warnings: string[]; | ||
| }; | ||
| /** | ||
| * Resolve the symbol-upload base URL. | ||
| * | ||
| * Priority when `explicit` is **omitted** (`undefined`): | ||
| * `FLASHCAT_SOURCEMAP_INTAKE_URL` → legacy `FLASHCAT_ENDPOINT` → SaaS default. | ||
| * | ||
| * When `explicit` is provided (`''` included; `null` counts as omitted — plain-JS | ||
| * hvigorfiles pass it from config lookups), it is the only source — an empty | ||
| * or invalid value skips the upload instead of falling back to SaaS. | ||
| */ | ||
| export declare function resolveUploadEndpoint(explicit?: string | null, env?: NodeJS.ProcessEnv): EndpointResolution; | ||
| /** | ||
| * Build the upload URL. Accepts either a base host or a full | ||
| * `.../sourcemap/upload` path so private-deploy pastes don't double the suffix. | ||
| */ | ||
| export declare function resolveSourcemapUploadUrl(endpoint: string): string; | ||
| export interface UploadConfig { | ||
| /** Ingest host, e.g. https://browser.flashcat.cloud (prod) or https://jira.flashcat.cloud (staging). */ | ||
| /** Symbol-upload base URL, e.g. https://ci.flashcat.cloud (SaaS) or a private ingest host. */ | ||
| endpoint: string; | ||
@@ -12,0 +37,0 @@ apiKey: string; |
+93
-1
@@ -37,2 +37,4 @@ "use strict"; | ||
| exports.TYPE_SYMBOL_FILE = exports.TYPE_SOURCEMAP = exports.ORIGIN = void 0; | ||
| exports.resolveUploadEndpoint = resolveUploadEndpoint; | ||
| exports.resolveSourcemapUploadUrl = resolveSourcemapUploadUrl; | ||
| exports.sourcemapEvent = sourcemapEvent; | ||
@@ -50,2 +52,92 @@ exports.symbolFileEvent = symbolFileEvent; | ||
| exports.TYPE_SYMBOL_FILE = 'harmony_symbol_file'; | ||
| /** SaaS sourcemap/symbol upload host. Distinct from RUM ingest (`browser.flashcat.cloud`). */ | ||
| const DEFAULT_UPLOAD_ENDPOINT = 'https://ci.flashcat.cloud'; | ||
| /** Known RUM-ingest-only hosts that 404 on `/sourcemap/upload`. */ | ||
| const RUM_INGEST_ONLY_HOSTS = new Set(['browser.flashcat.cloud', 'jira.flashcat.cloud']); | ||
| /** | ||
| * Resolve the symbol-upload base URL. | ||
| * | ||
| * Priority when `explicit` is **omitted** (`undefined`): | ||
| * `FLASHCAT_SOURCEMAP_INTAKE_URL` → legacy `FLASHCAT_ENDPOINT` → SaaS default. | ||
| * | ||
| * When `explicit` is provided (`''` included; `null` counts as omitted — plain-JS | ||
| * hvigorfiles pass it from config lookups), it is the only source — an empty | ||
| * or invalid value skips the upload instead of falling back to SaaS. | ||
| */ | ||
| function resolveUploadEndpoint(explicit, env = process.env) { | ||
| const warnings = []; | ||
| let raw; | ||
| let source; | ||
| if (explicit != null) { | ||
| raw = explicit.trim(); | ||
| source = 'option'; | ||
| if (!raw) { | ||
| return { ok: false, reason: 'endpoint is empty — skipping symbol upload (will not fall back to SaaS)', warnings }; | ||
| } | ||
| } | ||
| else if (env.FLASHCAT_SOURCEMAP_INTAKE_URL !== undefined) { | ||
| raw = env.FLASHCAT_SOURCEMAP_INTAKE_URL.trim(); | ||
| source = 'FLASHCAT_SOURCEMAP_INTAKE_URL'; | ||
| if (!raw) { | ||
| return { | ||
| ok: false, | ||
| reason: 'FLASHCAT_SOURCEMAP_INTAKE_URL is empty — skipping symbol upload (will not fall back to SaaS)', | ||
| warnings | ||
| }; | ||
| } | ||
| } | ||
| else if (env.FLASHCAT_ENDPOINT !== undefined) { | ||
| raw = env.FLASHCAT_ENDPOINT.trim(); | ||
| source = 'FLASHCAT_ENDPOINT'; | ||
| if (!raw) { | ||
| return { | ||
| ok: false, | ||
| reason: 'FLASHCAT_ENDPOINT is empty — skipping symbol upload (will not fall back to SaaS)', | ||
| warnings | ||
| }; | ||
| } | ||
| warnings.push('FLASHCAT_ENDPOINT is deprecated for symbol upload; prefer FLASHCAT_SOURCEMAP_INTAKE_URL (or omit for SaaS)'); | ||
| } | ||
| else { | ||
| raw = DEFAULT_UPLOAD_ENDPOINT; | ||
| source = 'default'; | ||
| } | ||
| let url; | ||
| try { | ||
| url = new URL(raw); | ||
| } | ||
| catch { | ||
| return { ok: false, reason: `invalid symbol-upload endpoint from ${source}: ${JSON.stringify(raw)}`, warnings }; | ||
| } | ||
| if (url.protocol !== 'http:' && url.protocol !== 'https:') { | ||
| return { | ||
| ok: false, | ||
| reason: `symbol-upload endpoint from ${source} must be http(s), got ${url.protocol}`, | ||
| warnings | ||
| }; | ||
| } | ||
| if (url.search || url.hash) { | ||
| return { | ||
| ok: false, | ||
| reason: `symbol-upload endpoint from ${source} must not include query or hash`, | ||
| warnings | ||
| }; | ||
| } | ||
| if (RUM_INGEST_ONLY_HOSTS.has(url.hostname)) { | ||
| warnings.push(`${url.hostname} is a RUM ingest host and returns 404 on /sourcemap/upload — use https://ci.flashcat.cloud (SaaS) or your private symbol-upload base URL`); | ||
| } | ||
| const endpoint = raw.replace(/\/+$/, ''); | ||
| return { ok: true, endpoint, warnings }; | ||
| } | ||
| /** | ||
| * Build the upload URL. Accepts either a base host or a full | ||
| * `.../sourcemap/upload` path so private-deploy pastes don't double the suffix. | ||
| */ | ||
| function resolveSourcemapUploadUrl(endpoint) { | ||
| const normalized = endpoint.trim().replace(/\/+$/, ''); | ||
| if (normalized.endsWith('/sourcemap/upload')) { | ||
| return normalized; | ||
| } | ||
| return `${normalized}/sourcemap/upload`; | ||
| } | ||
| /** event metadata for an ArkTS sourcemap upload. */ | ||
@@ -83,3 +175,3 @@ function sourcemapEvent(cfg) { | ||
| function uploadUrl(cfg) { | ||
| return `${cfg.endpoint.replace(/\/+$/, '')}/sourcemap/upload`; | ||
| return resolveSourcemapUploadUrl(cfg.endpoint); | ||
| } | ||
@@ -86,0 +178,0 @@ function fileBlob(filePath) { |
+1
-1
| { | ||
| "name": "@flashcatcloud/hvigor-plugin", | ||
| "version": "0.1.2", | ||
| "version": "0.1.3", | ||
| "description": "FlashCat hvigor plugin: upload HarmonyOS ArkTS sourcemaps + native .so debug symbols to fc-rum for crash symbolication.", | ||
@@ -5,0 +5,0 @@ "license": "Apache-2.0", |
+24
-6
@@ -15,6 +15,12 @@ # @flashcatcloud/hvigor-plugin | ||
| hvigor plugins are declared in the project's `hvigor/hvigor-config.json5` | ||
| `dependencies` (this is the hvigor mechanism — **not** `ohpm install`, which is for | ||
| ArkTS/ohpm packages). hvigor installs it from npm and resolves the import below. | ||
| The plugin is published on **npm**, not ohpm. Install it as a devDependency in | ||
| the project root `package.json` (not `oh-package.json5`): | ||
| ```sh | ||
| npm install -D @flashcatcloud/hvigor-plugin | ||
| ``` | ||
| Alternatively, declare it in `hvigor/hvigor-config.json5` `dependencies` — | ||
| hvigor still fetches it from npm: | ||
| ```json5 | ||
@@ -24,3 +30,3 @@ { | ||
| "dependencies": { | ||
| "@flashcatcloud/hvigor-plugin": "^0.1.0" | ||
| "@flashcatcloud/hvigor-plugin": "^0.1.3" | ||
| } | ||
@@ -42,3 +48,5 @@ } | ||
| flashcatSymbolUploadPlugin({ | ||
| endpoint: 'https://browser.flashcat.cloud', // staging: https://jira.flashcat.cloud | ||
| // Omit endpoint for SaaS (defaults to https://ci.flashcat.cloud). | ||
| // Private deploy: set FLASHCAT_SOURCEMAP_INTAKE_URL=https://rum.example.com | ||
| // (scheme + host, no path), or pass endpoint: 'https://rum.example.com'. | ||
| apiKey: process.env.FLASHCAT_API_KEY ?? '', | ||
@@ -60,2 +68,11 @@ service: 'my-app', | ||
| Endpoint resolution (first match wins): | ||
| 1. `endpoint` option — if provided (even as `''`), it is the only source; empty/invalid **skips** upload (no SaaS fallback) | ||
| 2. `FLASHCAT_SOURCEMAP_INTAKE_URL` (preferred for private deploys; same name as Android / flashcat-cli) | ||
| 3. Legacy `FLASHCAT_ENDPOINT` (deprecated; emits a warning — historically often set to the RUM host `browser.flashcat.cloud`, which 404s on symbol upload) | ||
| 4. SaaS default `https://ci.flashcat.cloud` | ||
| Do **not** use `browser.flashcat.cloud` / `jira.flashcat.cloud` for symbol upload — those are RUM ingest hosts only. | ||
| The task is registered with `dependencies: ['assembleHap','assembleHar']` (it runs | ||
@@ -70,3 +87,4 @@ after the assemble tasks), so the sourcemap + native libs exist when it runs. A missing artifact or upload | ||
| const result = await uploadAll('entry/build/default', { | ||
| endpoint, apiKey, service, version, pluginVersion: '0.1.0' | ||
| endpoint: process.env.FLASHCAT_SOURCEMAP_INTAKE_URL || 'https://ci.flashcat.cloud', | ||
| apiKey, service, version, pluginVersion: '0.1.3' | ||
| }, console.log); | ||
@@ -73,0 +91,0 @@ ``` |
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
37411
22.67%723
22.13%117
18.18%6
20%