@absolutejs/absolute 0.20.0-beta.67 → 0.20.0-beta.69
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/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/cli/index.js +1581 -577
- package/dist/mobile/index.js +1789 -190
- package/dist/mobile/index.js.map +9 -5
- package/dist/mobile/remoteMacAgentEntry.js +580 -209
- package/dist/mobile/shellBootstrap.js +152 -0
- package/dist/src/mobile/expoNativeObservability.d.ts +3 -0
- package/dist/src/mobile/index.d.ts +3 -0
- package/dist/src/mobile/nativeObservability.d.ts +4 -0
- package/dist/src/mobile/shellNativeObservability.d.ts +21 -0
- package/package.json +8 -7
|
@@ -6710,6 +6710,141 @@ var installAbsoluteMobileShellObservability = (manifest, engine, fetch2 = global
|
|
|
6710
6710
|
};
|
|
6711
6711
|
};
|
|
6712
6712
|
|
|
6713
|
+
// src/mobile/shellNativeObservability.ts
|
|
6714
|
+
import { Capacitor, registerPlugin } from "@capacitor/core";
|
|
6715
|
+
var MAX_REPORTS = 8;
|
|
6716
|
+
var MAX_DETAIL_BYTES = 64 * 1024;
|
|
6717
|
+
var MAX_STRING_LENGTH = 8 * 1024;
|
|
6718
|
+
var MAX_REDACTION_DEPTH = 8;
|
|
6719
|
+
var REPORT_ID = /^[A-Za-z0-9._:-]{1,128}$/u;
|
|
6720
|
+
var REPORT_KINDS = new Set([
|
|
6721
|
+
"anr",
|
|
6722
|
+
"app-launch",
|
|
6723
|
+
"cpu-exception",
|
|
6724
|
+
"crash",
|
|
6725
|
+
"disk-write-exception",
|
|
6726
|
+
"hang",
|
|
6727
|
+
"initialization-failure",
|
|
6728
|
+
"low-memory",
|
|
6729
|
+
"native-crash",
|
|
6730
|
+
"resource-exhaustion",
|
|
6731
|
+
"signal"
|
|
6732
|
+
]);
|
|
6733
|
+
var nativeObservability = registerPlugin("AbsoluteMobileObservability");
|
|
6734
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6735
|
+
var redactText = (value) => value.slice(0, MAX_STRING_LENGTH).replace(/\b(Bearer)\s+[A-Za-z0-9._~+/-]+=*/giu, "$1 [REDACTED]").replace(/([?&](?:access_token|api_?key|authorization|code|id_token|password|refresh_token|secret|token)=)[^&#\s]*/giu, "$1[REDACTED]").replace(/\b((?:access_?token|api_?key|authorization|client_?secret|cookie|id_?token|password|passwd|refresh_?token|secret|token)\s*[:=]\s*)[^,\s;]+/giu, "$1[REDACTED]").replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, "[REDACTED]");
|
|
6736
|
+
var redactValue2 = (value, depth = 0) => {
|
|
6737
|
+
if (depth >= MAX_REDACTION_DEPTH)
|
|
6738
|
+
return "[TRUNCATED]";
|
|
6739
|
+
if (typeof value === "string")
|
|
6740
|
+
return redactText(value);
|
|
6741
|
+
if (value === null || typeof value === "number" || typeof value === "boolean")
|
|
6742
|
+
return value;
|
|
6743
|
+
if (Array.isArray(value))
|
|
6744
|
+
return value.slice(0, 64).map((entry) => redactValue2(entry, depth + 1));
|
|
6745
|
+
if (!isRecord3(value))
|
|
6746
|
+
return String(value).slice(0, MAX_STRING_LENGTH);
|
|
6747
|
+
return Object.fromEntries(Object.entries(value).slice(0, 128).map(([key, entry]) => [
|
|
6748
|
+
key,
|
|
6749
|
+
/(?:authorization|cookie|password|secret|token)/iu.test(key) ? "[REDACTED]" : redactValue2(entry, depth + 1)
|
|
6750
|
+
]));
|
|
6751
|
+
};
|
|
6752
|
+
var parseReport = (value) => {
|
|
6753
|
+
if (!isRecord3(value) || !isRecord3(value.details))
|
|
6754
|
+
return;
|
|
6755
|
+
if (typeof value.id !== "string" || !REPORT_ID.test(value.id))
|
|
6756
|
+
return;
|
|
6757
|
+
if (typeof value.kind !== "string" || !REPORT_KINDS.has(value.kind))
|
|
6758
|
+
return;
|
|
6759
|
+
if (typeof value.occurredAt !== "number" || !Number.isFinite(value.occurredAt) || value.occurredAt <= 0)
|
|
6760
|
+
return;
|
|
6761
|
+
if (value.platform !== "android" && value.platform !== "ios")
|
|
6762
|
+
return;
|
|
6763
|
+
const details = redactValue2(value.details);
|
|
6764
|
+
if (!isRecord3(details))
|
|
6765
|
+
return;
|
|
6766
|
+
if (new TextEncoder().encode(JSON.stringify(details)).byteLength > MAX_DETAIL_BYTES)
|
|
6767
|
+
return;
|
|
6768
|
+
return {
|
|
6769
|
+
details,
|
|
6770
|
+
id: value.id,
|
|
6771
|
+
kind: value.kind,
|
|
6772
|
+
occurredAt: value.occurredAt,
|
|
6773
|
+
platform: value.platform
|
|
6774
|
+
};
|
|
6775
|
+
};
|
|
6776
|
+
var sampled = (id, sampleRate) => {
|
|
6777
|
+
if (sampleRate >= 1)
|
|
6778
|
+
return true;
|
|
6779
|
+
let hash = 2166136261;
|
|
6780
|
+
for (const character of id) {
|
|
6781
|
+
hash ^= character.codePointAt(0) ?? 0;
|
|
6782
|
+
hash = Math.imul(hash, 16777619);
|
|
6783
|
+
}
|
|
6784
|
+
return (hash >>> 0) / 4294967296 < sampleRate;
|
|
6785
|
+
};
|
|
6786
|
+
var reportEvent = (report, manifest) => ({
|
|
6787
|
+
at: report.occurredAt,
|
|
6788
|
+
extra: {
|
|
6789
|
+
nativeDiagnostic: report.details,
|
|
6790
|
+
nativeDiagnosticId: report.id
|
|
6791
|
+
},
|
|
6792
|
+
groupingKey: `absolute-native:${report.platform}:${report.kind}`,
|
|
6793
|
+
level: [
|
|
6794
|
+
"app-launch",
|
|
6795
|
+
"cpu-exception",
|
|
6796
|
+
"disk-write-exception",
|
|
6797
|
+
"low-memory",
|
|
6798
|
+
"resource-exhaustion",
|
|
6799
|
+
"signal"
|
|
6800
|
+
].includes(report.kind) ? "warning" : "error",
|
|
6801
|
+
message: `Native process diagnostic — ${report.kind}`,
|
|
6802
|
+
name: "AbsoluteMobileNativeDiagnostic",
|
|
6803
|
+
tags: {
|
|
6804
|
+
absoluteMobile: "true",
|
|
6805
|
+
mobileAppBuild: manifest.appBuild,
|
|
6806
|
+
mobileFailurePhase: "native-process",
|
|
6807
|
+
mobileManifestFormat: String(manifest.format),
|
|
6808
|
+
mobileNativeRuntime: manifest.nativeRuntime,
|
|
6809
|
+
mobilePlatform: report.platform,
|
|
6810
|
+
mobileRuntime: manifest.runtime
|
|
6811
|
+
}
|
|
6812
|
+
});
|
|
6813
|
+
var drainAbsoluteMobileNativeObservability = async (manifest, fetch2, options = {}) => {
|
|
6814
|
+
const config = manifest.observability;
|
|
6815
|
+
if (!config || !(options.native ?? Capacitor.isNativePlatform()))
|
|
6816
|
+
return 0;
|
|
6817
|
+
const plugin = options.plugin ?? nativeObservability;
|
|
6818
|
+
const pending = await plugin.pending();
|
|
6819
|
+
const reports = pending.reports.slice(0, MAX_REPORTS).map(parseReport).filter((report) => report !== undefined);
|
|
6820
|
+
const discarded = reports.filter((report) => !sampled(report.id, config.sampleRate));
|
|
6821
|
+
if (discarded.length > 0)
|
|
6822
|
+
await plugin.acknowledge({
|
|
6823
|
+
ids: discarded.map(({ id }) => id)
|
|
6824
|
+
});
|
|
6825
|
+
const selected = reports.filter((report) => sampled(report.id, config.sampleRate));
|
|
6826
|
+
if (selected.length === 0)
|
|
6827
|
+
return 0;
|
|
6828
|
+
const envelope = {
|
|
6829
|
+
...config.environment ? { environment: config.environment } : {},
|
|
6830
|
+
events: selected.map((report) => reportEvent(report, manifest)),
|
|
6831
|
+
project: config.project,
|
|
6832
|
+
release: manifest.appBuild,
|
|
6833
|
+
v: 1
|
|
6834
|
+
};
|
|
6835
|
+
const response = await fetch2(config.endpoint, {
|
|
6836
|
+
body: JSON.stringify(envelope),
|
|
6837
|
+
headers: { "content-type": "application/json" },
|
|
6838
|
+
method: "POST"
|
|
6839
|
+
});
|
|
6840
|
+
if (!response.ok)
|
|
6841
|
+
return 0;
|
|
6842
|
+
await plugin.acknowledge({
|
|
6843
|
+
ids: selected.map(({ id }) => id)
|
|
6844
|
+
});
|
|
6845
|
+
return selected.length;
|
|
6846
|
+
};
|
|
6847
|
+
|
|
6713
6848
|
// src/mobile/shellBootstrap.ts
|
|
6714
6849
|
var MANIFEST_PATH = "./absolute-mobile-manifest.json";
|
|
6715
6850
|
var STATUS_ID = "absolute-mobile-status";
|
|
@@ -6956,6 +7091,23 @@ var startAbsoluteMobileShell = async (options = {}) => {
|
|
|
6956
7091
|
}) : undefined;
|
|
6957
7092
|
const applicationFetch = auth?.fetch ?? options.createFetch?.(manifest) ?? globalThis.fetch;
|
|
6958
7093
|
const observability = installAbsoluteMobileShellObservability(manifest, options.engine ?? "capacitor", applicationFetch);
|
|
7094
|
+
if ((options.engine ?? "capacitor") === "capacitor") {
|
|
7095
|
+
let nativeObservabilityDrain;
|
|
7096
|
+
const drainNativeObservability = () => {
|
|
7097
|
+
nativeObservabilityDrain ??= drainAbsoluteMobileNativeObservability(manifest, applicationFetch).catch(() => 0).finally(() => {
|
|
7098
|
+
nativeObservabilityDrain = undefined;
|
|
7099
|
+
});
|
|
7100
|
+
return nativeObservabilityDrain;
|
|
7101
|
+
};
|
|
7102
|
+
drainNativeObservability();
|
|
7103
|
+
addEventListener("online", () => void drainNativeObservability());
|
|
7104
|
+
App.addListener("appStateChange", ({ isActive }) => {
|
|
7105
|
+
if (isActive)
|
|
7106
|
+
drainNativeObservability();
|
|
7107
|
+
}).catch(() => {
|
|
7108
|
+
return;
|
|
7109
|
+
});
|
|
7110
|
+
}
|
|
6959
7111
|
installAbsoluteMobileShellHttp(manifest.productionOrigin, applicationFetch);
|
|
6960
7112
|
if (auth)
|
|
6961
7113
|
options.connectPush?.(auth);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const expoNativeObservabilityRuntimeSource = "// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.\nimport { requireNativeModule } from 'expo';\nimport { AppState, Platform } from 'react-native';\nimport { ABSOLUTE_MOBILE_MANIFEST } from './webAssets';\n\ntype NativeObservabilityModule = {\n\tacknowledge(options: { ids: string[] }): Promise<void>;\n\tpending(): Promise<{ reports: unknown[] }>;\n};\ntype NativeReport = { details: Record<string, unknown>; id: string; kind: string; occurredAt: number; platform: 'android' | 'ios' };\nconst native = requireNativeModule<NativeObservabilityModule>('AbsoluteMobileObservability');\nconst KINDS = new Set(['anr', 'app-launch', 'cpu-exception', 'crash', 'disk-write-exception', 'hang', 'initialization-failure', 'low-memory', 'native-crash', 'resource-exhaustion', 'signal']);\nconst ID = /^[A-Za-z0-9._:-]{1,128}$/;\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value);\nconst redactText = (value: string) => value.slice(0, 8192)\n\t.replace(/\\b(Bearer)\\s+[A-Za-z0-9._~+/-]+=*/gi, '$1 [REDACTED]')\n\t.replace(/([?&](?:access_token|api_?key|authorization|code|id_token|password|refresh_token|secret|token)=)[^&#\\s]*/gi, '$1[REDACTED]')\n\t.replace(/\\b((?:access_?token|api_?key|authorization|client_?secret|cookie|id_?token|password|passwd|refresh_?token|secret|token)\\s*[:=]\\s*)[^,\\s;]+/gi, '$1[REDACTED]')\n\t.replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/g, '[REDACTED]');\nconst redact = (value: unknown, depth = 0): unknown => {\n\tif (depth >= 8) return '[TRUNCATED]';\n\tif (typeof value === 'string') return redactText(value);\n\tif (value === null || typeof value === 'number' || typeof value === 'boolean') return value;\n\tif (Array.isArray(value)) return value.slice(0, 64).map(entry => redact(entry, depth + 1));\n\tif (!isRecord(value)) return String(value).slice(0, 8192);\n\treturn Object.fromEntries(Object.entries(value).slice(0, 128).map(([key, entry]) => [key, /(?:authorization|cookie|password|secret|token)/i.test(key) ? '[REDACTED]' : redact(entry, depth + 1)]));\n};\nconst parse = (value: unknown): NativeReport | undefined => {\n\tif (!isRecord(value) || !isRecord(value.details) || typeof value.id !== 'string' || !ID.test(value.id) || typeof value.kind !== 'string' || !KINDS.has(value.kind) || typeof value.occurredAt !== 'number' || !Number.isFinite(value.occurredAt) || value.occurredAt <= 0 || value.platform !== 'android' && value.platform !== 'ios') return;\n\tconst details = redact(value.details);\n\tif (!isRecord(details) || new TextEncoder().encode(JSON.stringify(details)).byteLength > 64 * 1024) return;\n\treturn { details, id: value.id, kind: value.kind, occurredAt: value.occurredAt, platform: value.platform };\n};\nconst sampled = (id: string, rate: number) => {\n\tif (rate >= 1) return true;\n\tlet hash = 2166136261;\n\tfor (const character of id) { hash ^= character.codePointAt(0) ?? 0; hash = Math.imul(hash, 16777619); }\n\treturn (hash >>> 0) / 4294967296 < rate;\n};\nlet drainPromise: Promise<void> | undefined;\nlet started = false;\nconst drain = () => {\n\tdrainPromise ??= (async () => {\n\t\tconst manifest = ABSOLUTE_MOBILE_MANIFEST;\n\t\tconst config = manifest?.observability;\n\t\tif (!manifest || !config || Platform.OS !== 'android' && Platform.OS !== 'ios') return;\n\t\tconst reports = (await native.pending()).reports.slice(0, 8).map(parse).filter((report): report is NativeReport => report !== undefined);\n\t\tconst discarded = reports.filter(report => !sampled(report.id, config.sampleRate));\n\t\tif (discarded.length) await native.acknowledge({ ids: discarded.map(report => report.id) });\n\t\tconst selected = reports.filter(report => sampled(report.id, config.sampleRate));\n\t\tif (!selected.length) return;\n\t\tconst response = await fetch(config.endpoint, {\n\t\t\tbody: JSON.stringify({ ...(config.environment ? { environment: config.environment } : {}), events: selected.map(report => ({ at: report.occurredAt, extra: { nativeDiagnostic: report.details, nativeDiagnosticId: report.id }, groupingKey: 'absolute-native:' + report.platform + ':' + report.kind, level: ['app-launch', 'cpu-exception', 'disk-write-exception', 'low-memory', 'resource-exhaustion', 'signal'].includes(report.kind) ? 'warning' : 'error', message: 'Native process diagnostic \u2014 ' + report.kind, name: 'AbsoluteMobileNativeDiagnostic', tags: { absoluteMobile: 'true', mobileAppBuild: manifest.appBuild, mobileEngine: 'expo', mobileFailurePhase: 'native-process', mobileManifestFormat: '1', mobileNativeRuntime: 'expo', mobilePlatform: report.platform, mobileRuntime: manifest.runtime } })), project: config.project, release: manifest.appBuild, v: 1 }),\n\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\tmethod: 'POST'\n\t\t});\n\t\tif (response.ok) await native.acknowledge({ ids: selected.map(report => report.id) });\n\t})().catch(() => undefined).finally(() => { drainPromise = undefined; });\n\treturn drainPromise;\n};\nexport const startAbsoluteExpoNativeObservability = () => {\n\tif (!started) {\n\t\tstarted = true;\n\t\tAppState.addEventListener('change', state => { if (state === 'active') void drain(); });\n\t}\n\treturn drain();\n};\n";
|
|
2
|
+
export declare const absoluteExpoNativeObservabilityFiles: (project: string) => Map<string, string>;
|
|
3
|
+
export declare const absoluteExpoNativeObservabilityPaths: (project: string) => string[];
|
|
@@ -21,6 +21,7 @@ export * from './expoBridge';
|
|
|
21
21
|
export * from './expoCodeSigning';
|
|
22
22
|
export * from './expoDevController';
|
|
23
23
|
export * from './expoProject';
|
|
24
|
+
export * from './expoNativeObservability';
|
|
24
25
|
export * from './expoUpdate';
|
|
25
26
|
export * from './capacitorProject';
|
|
26
27
|
export * from './config';
|
|
@@ -34,6 +35,7 @@ export * from './nativeDeviceCapabilities';
|
|
|
34
35
|
export * from './nativeAuth';
|
|
35
36
|
export * from './nativeRoute';
|
|
36
37
|
export * from './nativeUpdates';
|
|
38
|
+
export * from './nativeObservability';
|
|
37
39
|
export * from './pageProtocol';
|
|
38
40
|
export * from './producerContext';
|
|
39
41
|
export * from './releaseArtifact';
|
|
@@ -41,6 +43,7 @@ export * from './releasePublisher';
|
|
|
41
43
|
export * from './routeMetadataTransform';
|
|
42
44
|
export * from './routeMatcher';
|
|
43
45
|
export * from './shellHttp';
|
|
46
|
+
export * from './shellNativeObservability';
|
|
44
47
|
export * from './shellObservability';
|
|
45
48
|
export * from './transport';
|
|
46
49
|
export * from './uiPrimitives';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AbsoluteMobileClientManifest, AbsoluteMobileFetch } from './transport';
|
|
2
|
+
export type AbsoluteMobileNativeDiagnostic = {
|
|
3
|
+
details: Record<string, unknown>;
|
|
4
|
+
id: string;
|
|
5
|
+
kind: string;
|
|
6
|
+
occurredAt: number;
|
|
7
|
+
platform: 'android' | 'ios';
|
|
8
|
+
};
|
|
9
|
+
export type AbsoluteMobileNativeObservabilityPlugin = {
|
|
10
|
+
acknowledge(options: {
|
|
11
|
+
ids: string[];
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
pending(): Promise<{
|
|
14
|
+
reports: unknown[];
|
|
15
|
+
}>;
|
|
16
|
+
};
|
|
17
|
+
export type AbsoluteMobileNativeObservabilityDrainOptions = {
|
|
18
|
+
native?: boolean;
|
|
19
|
+
plugin?: AbsoluteMobileNativeObservabilityPlugin;
|
|
20
|
+
};
|
|
21
|
+
export declare const drainAbsoluteMobileNativeObservability: (manifest: AbsoluteMobileClientManifest, fetch: AbsoluteMobileFetch, options?: AbsoluteMobileNativeObservabilityDrainOptions) => Promise<number>;
|
package/package.json
CHANGED
|
@@ -296,12 +296,12 @@
|
|
|
296
296
|
"main": "./dist/index.js",
|
|
297
297
|
"name": "@absolutejs/absolute",
|
|
298
298
|
"optionalDependencies": {
|
|
299
|
-
"@absolutejs/native-darwin-arm64": "0.20.0-beta.
|
|
300
|
-
"@absolutejs/native-darwin-x64": "0.20.0-beta.
|
|
301
|
-
"@absolutejs/native-linux-arm64": "0.20.0-beta.
|
|
302
|
-
"@absolutejs/native-linux-x64": "0.20.0-beta.
|
|
303
|
-
"@absolutejs/native-windows-arm64": "0.20.0-beta.
|
|
304
|
-
"@absolutejs/native-windows-x64": "0.20.0-beta.
|
|
299
|
+
"@absolutejs/native-darwin-arm64": "0.20.0-beta.69",
|
|
300
|
+
"@absolutejs/native-darwin-x64": "0.20.0-beta.69",
|
|
301
|
+
"@absolutejs/native-linux-arm64": "0.20.0-beta.69",
|
|
302
|
+
"@absolutejs/native-linux-x64": "0.20.0-beta.69",
|
|
303
|
+
"@absolutejs/native-windows-arm64": "0.20.0-beta.69",
|
|
304
|
+
"@absolutejs/native-windows-x64": "0.20.0-beta.69"
|
|
305
305
|
},
|
|
306
306
|
"overrides": {
|
|
307
307
|
"@sinclair/typebox": "0.34.52",
|
|
@@ -463,6 +463,7 @@
|
|
|
463
463
|
"test:native:android:bundle": "ABSOLUTE_TEST_NATIVE_ANDROID=1 bun test tests/native/android-embedded-bundle-conformance.test.ts",
|
|
464
464
|
"test:native:android:hmr": "ABSOLUTE_TEST_NATIVE_ANDROID=1 bun test tests/native/android-hmr-conformance.test.ts",
|
|
465
465
|
"test:native:android:lifecycle": "ABSOLUTE_TEST_NATIVE_ANDROID=1 bun test tests/native/android-lifecycle-conformance.test.ts",
|
|
466
|
+
"test:native:android:observability": "ABSOLUTE_TEST_NATIVE_ANDROID=1 bun test tests/native/android-embedded-bundle-conformance.test.ts -t 'native process crash'",
|
|
466
467
|
"test:native:android:updates": "ABSOLUTE_TEST_NATIVE_ANDROID=1 bun test tests/native/android-embedded-bundle-conformance.test.ts -t 'signed web updates'",
|
|
467
468
|
"test:native:android:upgrade": "ABSOLUTE_TEST_NATIVE_ANDROID=1 bun test tests/native/android-embedded-bundle-conformance.test.ts -t 'installed APK upgrade'",
|
|
468
469
|
"test:native:ios": "bun run test:native:ios:lifecycle && bun run test:native:ios:bundle",
|
|
@@ -519,7 +520,7 @@
|
|
|
519
520
|
]
|
|
520
521
|
}
|
|
521
522
|
},
|
|
522
|
-
"version": "0.20.0-beta.
|
|
523
|
+
"version": "0.20.0-beta.69",
|
|
523
524
|
"workspaces": [
|
|
524
525
|
"tests/fixtures/*",
|
|
525
526
|
"tests/fixtures/_packages/*"
|