@absolutejs/absolute 0.20.0-beta.63 → 0.20.0-beta.64
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 +464 -250
- package/dist/mobile/index.js +1189 -1023
- package/dist/mobile/index.js.map +9 -8
- package/dist/mobile/remoteMacAgentEntry.js +233 -200
- package/dist/src/mobile/expoUpdate.d.ts +27 -0
- package/dist/src/mobile/index.d.ts +1 -0
- package/dist/src/mobile/updateRuntime.d.ts +12 -2
- package/package.json +7 -7
package/dist/mobile/index.js
CHANGED
|
@@ -401,770 +401,770 @@ var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-
|
|
|
401
401
|
return normalizeSlug(str).split(/[-_]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase()).join("");
|
|
402
402
|
};
|
|
403
403
|
|
|
404
|
-
// src/mobile/
|
|
405
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
406
|
-
import { join as join11 } from "path";
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
404
|
+
// src/mobile/deviceCapabilities.ts
|
|
405
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
406
|
+
import { extname as extname3, join as join11, relative as relative9, resolve as resolve10 } from "path";
|
|
407
|
+
import { fileURLToPath } from "url";
|
|
408
|
+
import ts from "typescript";
|
|
409
|
+
var DEVICES_PACKAGE = "@absolutejs/devices", ABSOLUTE_MOBILE_SHELL_DEVICE_CAPABILITIES, ADAPTERS, SOURCE_GLOB, IGNORED_DIRECTORIES, IDENTIFIER_PATTERN, providerModulePattern = (provider) => new RegExp(`^@absolutejs/devices-${provider}/[a-z][a-z0-9-]*$`, "u"), providerPackagePattern = (provider) => provider === "capacitor" ? /^@capacitor\/[a-z][a-z0-9-]*@\d+\.\d+\.\d+$/u : /^(?:expo-[a-z][a-z0-9-]*|@react-native-[a-z0-9-]+\/[a-z][a-z0-9-]*)@\d+\.\d+\.\d+$/u, providerLabel = (provider) => provider === "capacitor" ? "Capacitor" : "Expo", ANDROID_PERMISSION_PATTERN, IOS_USAGE_DESCRIPTIONS, IOS_PRIVACY_ACCESSED_API_REASONS, IOS_PRIVACY_ACCESSED_APIS, object = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson = (path) => {
|
|
410
|
+
const value = JSON.parse(readFileSync2(path, "utf8"));
|
|
411
|
+
if (!object(value))
|
|
412
|
+
throw new TypeError(`${path} must contain an object.`);
|
|
413
|
+
return value;
|
|
414
|
+
}, text = (value, field2) => {
|
|
415
|
+
if (typeof value !== "string" || value.length === 0)
|
|
416
|
+
throw new TypeError(`${field2} must be a non-empty string.`);
|
|
417
|
+
return value;
|
|
418
|
+
}, androidPermissions = (value, field2) => {
|
|
419
|
+
if (value === undefined)
|
|
411
420
|
return;
|
|
421
|
+
if (!object(value))
|
|
422
|
+
throw new TypeError(`${field2} must be an object.`);
|
|
423
|
+
const { permissions } = value;
|
|
424
|
+
if (!Array.isArray(permissions) || !permissions.every((permission) => typeof permission === "string" && ANDROID_PERMISSION_PATTERN.test(permission)))
|
|
425
|
+
throw new TypeError(`${field2}.permissions must contain Android permission names.`);
|
|
426
|
+
return [...permissions];
|
|
427
|
+
}, iosPrivacyAccessedApis = (value, field2) => {
|
|
428
|
+
if (value === undefined)
|
|
429
|
+
return;
|
|
430
|
+
if (!object(value))
|
|
431
|
+
throw new TypeError(`${field2} must be an object.`);
|
|
432
|
+
const privacy = {};
|
|
433
|
+
for (const api of IOS_PRIVACY_ACCESSED_APIS) {
|
|
434
|
+
const reasons = value[api];
|
|
435
|
+
if (reasons === undefined)
|
|
436
|
+
continue;
|
|
437
|
+
const supported = IOS_PRIVACY_ACCESSED_API_REASONS[api];
|
|
438
|
+
if (!Array.isArray(reasons) || reasons.length === 0 || !reasons.every((reason) => typeof reason === "string" && supported.has(reason)))
|
|
439
|
+
throw new TypeError(`${field2} contains an unsupported API or reason.`);
|
|
440
|
+
privacy[api] = [...reasons];
|
|
412
441
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
442
|
+
if (Object.keys(value).some((api) => !IOS_PRIVACY_ACCESSED_APIS.some((known) => known === api)))
|
|
443
|
+
throw new TypeError(`${field2} contains an unsupported API or reason.`);
|
|
444
|
+
return privacy;
|
|
445
|
+
}, iosNativeRequirements = (value, field2) => {
|
|
446
|
+
if (value === undefined)
|
|
447
|
+
return;
|
|
448
|
+
if (!object(value))
|
|
449
|
+
throw new TypeError(`${field2} must be an object.`);
|
|
450
|
+
const {
|
|
451
|
+
privacyAccessedApis,
|
|
452
|
+
pushNotifications,
|
|
453
|
+
systemBars: systemBars2,
|
|
454
|
+
usageDescriptions
|
|
455
|
+
} = value;
|
|
456
|
+
if (pushNotifications !== undefined && pushNotifications !== true)
|
|
457
|
+
throw new TypeError(`${field2}.pushNotifications must be true.`);
|
|
458
|
+
if (systemBars2 !== undefined && systemBars2 !== true)
|
|
459
|
+
throw new TypeError(`${field2}.systemBars must be true.`);
|
|
460
|
+
if (usageDescriptions !== undefined && (!Array.isArray(usageDescriptions) || !usageDescriptions.every((purpose) => typeof purpose === "string" && IOS_USAGE_DESCRIPTIONS.has(purpose))))
|
|
461
|
+
throw new TypeError(`${field2}.usageDescriptions contains an unsupported purpose.`);
|
|
462
|
+
const privacy = iosPrivacyAccessedApis(privacyAccessedApis, `${field2}.privacyAccessedApis`);
|
|
424
463
|
return {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
464
|
+
...privacy === undefined ? {} : { privacyAccessedApis: privacy },
|
|
465
|
+
...pushNotifications === true ? { pushNotifications: true } : {},
|
|
466
|
+
...systemBars2 === true ? { systemBars: true } : {},
|
|
467
|
+
...usageDescriptions === undefined ? {} : { usageDescriptions: [...usageDescriptions] }
|
|
429
468
|
};
|
|
430
|
-
},
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if (
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
if (policy.maxBytesPerNamespace !== undefined && (!Number.isSafeInteger(policy.maxBytesPerNamespace) || policy.maxBytesPerNamespace < 1))
|
|
458
|
-
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.maxBytesPerNamespace must be a positive safe integer.`);
|
|
459
|
-
for (const [index, rule] of (policy.collections ?? []).entries()) {
|
|
460
|
-
validatePolicyMatch(rule.match, `${label}.collections[${index}]`);
|
|
461
|
-
if (rule.maxAgeMs !== undefined && (!Number.isSafeInteger(rule.maxAgeMs) || rule.maxAgeMs < 1))
|
|
462
|
-
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}].maxAgeMs must be a positive safe integer.`);
|
|
463
|
-
if (rule.persistence === "memory-only" && rule.protection === "required")
|
|
464
|
-
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}] cannot require at-rest protection when it is memory-only.`);
|
|
465
|
-
if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
|
|
466
|
-
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}] declares ${rule.sensitivity} data without required protection or memory-only persistence.`);
|
|
467
|
-
}
|
|
468
|
-
for (const [index, rule] of (policy.mutations ?? []).entries()) {
|
|
469
|
-
validatePolicyMatch(rule.match, `${label}.mutations[${index}]`);
|
|
470
|
-
if (rule.conflict !== undefined && rule.conflict.strategy !== "client-wins" && rule.conflict.strategy !== "manual" && rule.conflict.strategy !== "server-wins")
|
|
471
|
-
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.strategy is invalid.`);
|
|
472
|
-
if (rule.conflict?.maxAttempts !== undefined && (!Number.isSafeInteger(rule.conflict.maxAttempts) || rule.conflict.maxAttempts < 1))
|
|
473
|
-
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts must be a positive safe integer.`);
|
|
474
|
-
if (rule.conflict?.maxAttempts !== undefined && rule.conflict.strategy !== "client-wins")
|
|
475
|
-
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts is only valid for client-wins.`);
|
|
476
|
-
if (rule.persistence === "memory-only" && rule.protection === "required")
|
|
477
|
-
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] cannot require at-rest protection when it is memory-only.`);
|
|
478
|
-
if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
|
|
479
|
-
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] declares ${rule.sensitivity} arguments without required protection.`);
|
|
480
|
-
}
|
|
481
|
-
return policy;
|
|
482
|
-
}, normalizeSyncLocalSchemaComponents = (schema = { version: 1 }) => {
|
|
483
|
-
const components = isSchemaBundle(schema) ? [...schema.components] : [{ ...schema, id: "@absolutejs/app" }];
|
|
484
|
-
const ids = new Set;
|
|
485
|
-
for (const component of components) {
|
|
486
|
-
if (typeof component.id !== "string" || component.id.trim() !== component.id || component.id.length === 0)
|
|
487
|
-
throw new SyncLocalStoreSchemaError("INVALID_PLAN", "Sync schema component id must be non-empty and trimmed");
|
|
488
|
-
if (ids.has(component.id))
|
|
489
|
-
throw new SyncLocalStoreSchemaError("INVALID_PLAN", `Sync schema component "${component.id}" is declared more than once`);
|
|
490
|
-
ids.add(component.id);
|
|
491
|
-
if (component.localData)
|
|
492
|
-
validateSyncLocalDataPolicy(component.localData, `${component.id}.localData`);
|
|
493
|
-
}
|
|
494
|
-
return components.sort((a, b) => a.id.localeCompare(b.id));
|
|
495
|
-
}, resolveSyncLocalSchemaComponents = (storedVersions, schema = { version: 1 }) => {
|
|
496
|
-
const components = normalizeSyncLocalSchemaComponents(schema).map((component) => {
|
|
497
|
-
const current = resolveSyncLocalMigrations(component.version, component);
|
|
498
|
-
return {
|
|
499
|
-
id: component.id,
|
|
500
|
-
...resolveSyncLocalMigrations(storedVersions[component.id] ?? current.minimumCompatibleVersion, component)
|
|
469
|
+
}, parseProvider = (name, value, providerName) => {
|
|
470
|
+
if (!IDENTIFIER_PATTERN.test(name))
|
|
471
|
+
throw new TypeError("Device capability names must be identifiers.");
|
|
472
|
+
if (!object(value))
|
|
473
|
+
throw new TypeError(`Device capability ${name} must be an object.`);
|
|
474
|
+
const factory = text(value.factory, `${name}.factory`);
|
|
475
|
+
const module = text(value.module, `${name}.module`);
|
|
476
|
+
if (!IDENTIFIER_PATTERN.test(factory))
|
|
477
|
+
throw new TypeError(`${name}.factory must be a JavaScript identifier.`);
|
|
478
|
+
if (!providerModulePattern(providerName).test(module))
|
|
479
|
+
throw new TypeError(`${name}.module must be an official devices-${providerName} subpath.`);
|
|
480
|
+
if (!Array.isArray(value.packages) || !value.packages.every((spec) => typeof spec === "string" && providerPackagePattern(providerName).test(spec)))
|
|
481
|
+
throw new TypeError(`${name}.packages must contain exact official ${providerLabel(providerName)} package versions.`);
|
|
482
|
+
const { plugins } = value;
|
|
483
|
+
if (plugins !== undefined && (!Array.isArray(plugins) || !plugins.every((plugin) => typeof plugin === "string" && /^expo-[a-z][a-z0-9-]*$/u.test(plugin))))
|
|
484
|
+
throw new TypeError(`${name}.plugins must contain Expo config plugin names.`);
|
|
485
|
+
let native;
|
|
486
|
+
const { native: nativeMetadata } = value;
|
|
487
|
+
if (nativeMetadata !== undefined) {
|
|
488
|
+
if (!object(nativeMetadata))
|
|
489
|
+
throw new TypeError(`${name}.native must be an object.`);
|
|
490
|
+
const { android, ios } = nativeMetadata;
|
|
491
|
+
const permissions = androidPermissions(android, `${name}.native.android`);
|
|
492
|
+
const iosRequirements = iosNativeRequirements(ios, `${name}.native.ios`);
|
|
493
|
+
native = {
|
|
494
|
+
...permissions === undefined ? {} : { android: { permissions } },
|
|
495
|
+
...iosRequirements === undefined ? {} : { ios: iosRequirements }
|
|
501
496
|
};
|
|
502
|
-
});
|
|
503
|
-
const active = new Set(components.map((component) => component.id));
|
|
504
|
-
const orphanedComponents = Object.keys(storedVersions).filter((id) => !active.has(id)).sort();
|
|
505
|
-
return { components, orphanedComponents };
|
|
506
|
-
}, resolveSyncLocalMigrations = (storedVersion, schema = { version: 1 }) => {
|
|
507
|
-
positiveVersion(storedVersion, "Stored Sync schema version");
|
|
508
|
-
const targetVersion = positiveVersion(schema.version, "Target Sync schema version");
|
|
509
|
-
const migrations = [...schema.migrations ?? []].sort((a, b) => a.toVersion - b.toVersion);
|
|
510
|
-
const versions = new Set;
|
|
511
|
-
for (const migration of migrations) {
|
|
512
|
-
positiveVersion(migration.toVersion, "Sync migration toVersion");
|
|
513
|
-
if (versions.has(migration.toVersion))
|
|
514
|
-
throw new SyncLocalStoreSchemaError("INVALID_PLAN", `Sync migration ${migration.toVersion} is declared more than once`);
|
|
515
|
-
versions.add(migration.toVersion);
|
|
516
|
-
}
|
|
517
|
-
const inferredMinimum = migrations[0] ? migrations[0].toVersion - 1 : targetVersion;
|
|
518
|
-
const minimumCompatibleVersion = positiveVersion(schema.minimumCompatibleVersion ?? inferredMinimum, "Minimum compatible Sync schema version");
|
|
519
|
-
if (minimumCompatibleVersion > targetVersion)
|
|
520
|
-
throw new SyncLocalStoreSchemaError("INVALID_PLAN", "Minimum compatible Sync schema version cannot exceed its target");
|
|
521
|
-
if (storedVersion > targetVersion)
|
|
522
|
-
throw new SyncLocalStoreSchemaError("SCHEMA_TOO_NEW", `Stored Sync schema ${storedVersion} is newer than this runtime's schema ${targetVersion}`, { storedVersion, targetVersion });
|
|
523
|
-
if (storedVersion < minimumCompatibleVersion)
|
|
524
|
-
throw new SyncLocalStoreSchemaError("SCHEMA_TOO_OLD", `Stored Sync schema ${storedVersion} is older than the minimum compatible schema ${minimumCompatibleVersion}`, { storedVersion, targetVersion });
|
|
525
|
-
const steps = [];
|
|
526
|
-
for (let version = storedVersion + 1;version <= targetVersion; version++) {
|
|
527
|
-
const migration = migrations.find((candidate) => candidate.toVersion === version);
|
|
528
|
-
if (migration === undefined)
|
|
529
|
-
throw new SyncLocalStoreSchemaError("MIGRATION_MISSING", `Sync migration ${version - 1} -> ${version} is missing`, { storedVersion, targetVersion });
|
|
530
|
-
steps.push(migration);
|
|
531
497
|
}
|
|
532
|
-
return {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const existing = host[RUNTIME_TRANSPORT];
|
|
539
|
-
if (isRegistry(existing))
|
|
540
|
-
return existing;
|
|
541
|
-
if (typeof existing === "object" && existing !== null && Array.isArray(Reflect.get(existing, "installations"))) {
|
|
542
|
-
Reflect.set(existing, "clients", []);
|
|
543
|
-
return existing;
|
|
544
|
-
}
|
|
545
|
-
const created = { clients: [], installations: [] };
|
|
546
|
-
Object.defineProperty(host, RUNTIME_TRANSPORT, {
|
|
547
|
-
configurable: false,
|
|
548
|
-
enumerable: false,
|
|
549
|
-
value: created,
|
|
550
|
-
writable: false
|
|
551
|
-
});
|
|
552
|
-
return created;
|
|
553
|
-
})();
|
|
554
|
-
pools = new Map;
|
|
555
|
-
SyncLocalDataPolicyError = class SyncLocalDataPolicyError extends Error {
|
|
556
|
-
code;
|
|
557
|
-
constructor(code, message) {
|
|
558
|
-
super(message);
|
|
559
|
-
this.name = "SyncLocalDataPolicyError";
|
|
560
|
-
this.code = code;
|
|
561
|
-
}
|
|
562
|
-
};
|
|
563
|
-
SyncLocalStoreSchemaError = class SyncLocalStoreSchemaError extends Error {
|
|
564
|
-
code;
|
|
565
|
-
storedVersion;
|
|
566
|
-
targetVersion;
|
|
567
|
-
constructor(code, message, versions = {}) {
|
|
568
|
-
super(message);
|
|
569
|
-
this.name = "SyncLocalStoreSchemaError";
|
|
570
|
-
this.code = code;
|
|
571
|
-
this.storedVersion = versions.storedVersion;
|
|
572
|
-
this.targetVersion = versions.targetVersion;
|
|
573
|
-
}
|
|
498
|
+
return {
|
|
499
|
+
factory,
|
|
500
|
+
module,
|
|
501
|
+
...native === undefined ? {} : { native },
|
|
502
|
+
...plugins === undefined ? {} : { plugins: [...plugins] },
|
|
503
|
+
packages: [...value.packages]
|
|
574
504
|
};
|
|
575
|
-
})
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
},
|
|
588
|
-
const absolutejs = Reflect.get(manifest, "absolutejs");
|
|
589
|
-
if (!object(absolutejs))
|
|
590
|
-
return;
|
|
591
|
-
const sync = Reflect.get(absolutejs, "sync");
|
|
592
|
-
if (!object(sync))
|
|
593
|
-
return;
|
|
594
|
-
return Reflect.get(sync, "localSchema");
|
|
595
|
-
}, packageManifestPath = (projectRoot, packageName) => {
|
|
596
|
-
let directory = resolve10(projectRoot);
|
|
597
|
-
while (true) {
|
|
598
|
-
const candidate = join12(directory, "node_modules", packageName, "package.json");
|
|
599
|
-
const manifest = manifestAt(candidate);
|
|
600
|
-
if (manifest && Reflect.get(manifest, "name") === packageName)
|
|
601
|
-
return candidate;
|
|
602
|
-
const parent = dirname9(directory);
|
|
603
|
-
if (parent === directory)
|
|
604
|
-
return;
|
|
605
|
-
directory = parent;
|
|
606
|
-
}
|
|
607
|
-
}, metadataError = (id, detail) => new TypeError(`Invalid AbsoluteJS Sync schema metadata for ${id}: ${detail}`), positiveVersion2 = (value, id, field2) => {
|
|
608
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)
|
|
609
|
-
throw metadataError(id, `${field2} must be a positive safe integer.`);
|
|
610
|
-
return value;
|
|
611
|
-
}, nonEmpty = (value, id, field2) => {
|
|
612
|
-
if (typeof value !== "string" || value.trim() !== value || value.length === 0)
|
|
613
|
-
throw metadataError(id, `${field2} must be a non-empty trimmed string.`);
|
|
614
|
-
return value;
|
|
615
|
-
}, requireObject = (value, id, detail) => {
|
|
616
|
-
if (!object(value))
|
|
617
|
-
throw metadataError(id, detail);
|
|
618
|
-
return value;
|
|
619
|
-
}, unknownField = (record, key) => record[key], normalizeJsonValue2 = (value, id, field2) => {
|
|
620
|
-
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
621
|
-
return value;
|
|
622
|
-
if (typeof value === "number" && Number.isFinite(value))
|
|
623
|
-
return value;
|
|
624
|
-
if (Array.isArray(value))
|
|
625
|
-
return value.map((entry) => normalizeJsonValue2(entry, id, field2));
|
|
626
|
-
if (object(value))
|
|
627
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
628
|
-
key,
|
|
629
|
-
normalizeJsonValue2(entry, id, field2)
|
|
630
|
-
]));
|
|
631
|
-
throw metadataError(id, `${field2} must be JSON-safe.`);
|
|
632
|
-
}, operation = (value, id, index) => {
|
|
633
|
-
const record = requireObject(value, id, `migration operation ${index} must be an object.`);
|
|
634
|
-
const type = Reflect.get(record, "type");
|
|
635
|
-
const collection = nonEmpty(Reflect.get(record, "collection"), id, `operation ${index}.collection`);
|
|
636
|
-
if (type === "delete-collection")
|
|
637
|
-
return { collection, type };
|
|
638
|
-
if (type === "rename-field")
|
|
639
|
-
return {
|
|
640
|
-
collection,
|
|
641
|
-
from: nonEmpty(Reflect.get(record, "from"), id, `operation ${index}.from`),
|
|
642
|
-
to: nonEmpty(Reflect.get(record, "to"), id, `operation ${index}.to`),
|
|
643
|
-
type
|
|
644
|
-
};
|
|
645
|
-
const field2 = nonEmpty(Reflect.get(record, "field"), id, `operation ${index}.field`);
|
|
646
|
-
if (type === "remove-field")
|
|
647
|
-
return { collection, field: field2, type };
|
|
648
|
-
if (type === "set-default")
|
|
649
|
-
return {
|
|
650
|
-
collection,
|
|
651
|
-
field: field2,
|
|
652
|
-
type,
|
|
653
|
-
value: normalizeJsonValue2(Reflect.get(record, "value"), id, `operation ${index}.value`)
|
|
654
|
-
};
|
|
655
|
-
throw metadataError(id, `operation ${index}.type is not supported.`);
|
|
656
|
-
}, migration = (value, id, index) => {
|
|
657
|
-
const record = requireObject(value, id, `migration ${index} must be an object.`);
|
|
658
|
-
const allowed = new Set(["operations", "toVersion"]);
|
|
659
|
-
const unsupported = Object.keys(record).find((key) => !allowed.has(key));
|
|
660
|
-
if (unsupported)
|
|
661
|
-
throw metadataError(id, `migration ${index}.${unsupported} is not declarative metadata.`);
|
|
662
|
-
const declaredOperations = Reflect.get(record, "operations");
|
|
663
|
-
if (declaredOperations !== undefined && !Array.isArray(declaredOperations))
|
|
664
|
-
throw metadataError(id, `migration ${index}.operations must be an array.`);
|
|
665
|
-
const operations = Array.isArray(declaredOperations) ? declaredOperations : [];
|
|
505
|
+
}, absoluteDeviceNativeRequirements = (plan) => {
|
|
506
|
+
const privacy = plan.capabilities.reduce((requirements, name) => {
|
|
507
|
+
for (const api of IOS_PRIVACY_ACCESSED_APIS) {
|
|
508
|
+
const reasons = plan.providers[name]?.native?.ios?.privacyAccessedApis?.[api] ?? [];
|
|
509
|
+
if (reasons.length === 0)
|
|
510
|
+
continue;
|
|
511
|
+
const current = requirements[api] ?? new Set;
|
|
512
|
+
for (const reason of reasons)
|
|
513
|
+
current.add(reason);
|
|
514
|
+
requirements[api] = current;
|
|
515
|
+
}
|
|
516
|
+
return requirements;
|
|
517
|
+
}, {});
|
|
666
518
|
return {
|
|
667
|
-
|
|
668
|
-
|
|
519
|
+
androidPermissions: [
|
|
520
|
+
...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.android?.permissions ?? []))
|
|
521
|
+
].sort(),
|
|
522
|
+
iosPrivacyAccessedApis: IOS_PRIVACY_ACCESSED_APIS.flatMap((api) => {
|
|
523
|
+
const reasons = privacy[api];
|
|
524
|
+
return reasons ? [{ api, reasons: [...reasons].sort() }] : [];
|
|
525
|
+
}),
|
|
526
|
+
iosPushNotifications: plan.capabilities.some((name) => plan.providers[name]?.native?.ios?.pushNotifications === true),
|
|
527
|
+
iosSystemBars: plan.capabilities.some((name) => plan.providers[name]?.native?.ios?.systemBars === true),
|
|
528
|
+
iosUsageDescriptions: [
|
|
529
|
+
...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.ios?.usageDescriptions ?? []))
|
|
530
|
+
].sort()
|
|
669
531
|
};
|
|
670
|
-
},
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
"
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
const persistence = unknownField(rule, "persistence");
|
|
702
|
-
const sensitivity = unknownField(rule, "sensitivity");
|
|
703
|
-
const protection = unknownField(rule, "protection");
|
|
704
|
-
const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
|
|
705
|
-
const evictionPriority = unknownField(rule, "evictionPriority");
|
|
706
|
-
const maxAge = unknownField(rule, "maxAgeMs");
|
|
707
|
-
if (persistence !== undefined && persistence !== "durable" && persistence !== "memory-only")
|
|
708
|
-
throw metadataError(id, `localData.collections[${index}].persistence is invalid.`);
|
|
709
|
-
if (sensitivity !== undefined && sensitivity !== "public" && sensitivity !== "private" && sensitivity !== "secret")
|
|
710
|
-
throw metadataError(id, `localData.collections[${index}].sensitivity is invalid.`);
|
|
711
|
-
if (protection !== undefined && protection !== "none" && protection !== "required")
|
|
712
|
-
throw metadataError(id, `localData.collections[${index}].protection is invalid.`);
|
|
713
|
-
if (onProtectionUnavailable !== undefined && onProtectionUnavailable !== "error" && onProtectionUnavailable !== "memory-only")
|
|
714
|
-
throw metadataError(id, `localData.collections[${index}].onProtectionUnavailable is invalid.`);
|
|
715
|
-
if (evictionPriority !== undefined && evictionPriority !== "critical" && evictionPriority !== "normal" && evictionPriority !== "disposable")
|
|
716
|
-
throw metadataError(id, `localData.collections[${index}].evictionPriority is invalid.`);
|
|
717
|
-
return {
|
|
718
|
-
match,
|
|
719
|
-
...sensitivity ? { sensitivity } : {},
|
|
720
|
-
...persistence ? { persistence } : {},
|
|
721
|
-
...protection ? { protection } : {},
|
|
722
|
-
...onProtectionUnavailable ? {
|
|
723
|
-
onProtectionUnavailable
|
|
724
|
-
} : {},
|
|
725
|
-
...evictionPriority ? { evictionPriority } : {},
|
|
726
|
-
...maxAge === undefined ? {} : {
|
|
727
|
-
maxAgeMs: positiveVersion2(maxAge, id, `localData.collections[${index}].maxAgeMs`)
|
|
532
|
+
}, loadAbsoluteDeviceCapabilityProviders = (projectRoot, provider = "capacitor") => {
|
|
533
|
+
const adapter = ADAPTERS[provider];
|
|
534
|
+
let path = join11(resolve10(projectRoot), "node_modules", adapter, "package.json");
|
|
535
|
+
try {
|
|
536
|
+
readFileSync2(path, "utf8");
|
|
537
|
+
} catch {
|
|
538
|
+
path = fileURLToPath(import.meta.resolve(`${adapter}/package.json`));
|
|
539
|
+
}
|
|
540
|
+
const manifest = readJson(path);
|
|
541
|
+
const { absolutejs } = manifest;
|
|
542
|
+
const devices = object(absolutejs) ? absolutejs.devices : undefined;
|
|
543
|
+
if (!object(devices) || devices.format !== 1 || devices.provider !== provider || !object(devices.capabilities))
|
|
544
|
+
throw new TypeError(`${adapter} does not publish supported capability metadata.`);
|
|
545
|
+
const entries = Object.entries(devices.capabilities).map(([name, capability]) => ({
|
|
546
|
+
name,
|
|
547
|
+
provider: parseProvider(name, capability, provider)
|
|
548
|
+
}));
|
|
549
|
+
return Object.fromEntries(entries.sort((left, right) => left.name.localeCompare(right.name)).map(({ name, provider: capabilityProvider }) => [
|
|
550
|
+
name,
|
|
551
|
+
capabilityProvider
|
|
552
|
+
]));
|
|
553
|
+
}, isIgnored = (path) => path.split("/").some((segment) => IGNORED_DIRECTORIES.has(segment)), importedCapabilities = (source, file) => {
|
|
554
|
+
const names = new Set;
|
|
555
|
+
const namespaces = new Set;
|
|
556
|
+
const visit = (node) => {
|
|
557
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === DEVICES_PACKAGE && !node.importClause?.isTypeOnly) {
|
|
558
|
+
const bindings = node.importClause?.namedBindings;
|
|
559
|
+
if (bindings && ts.isNamedImports(bindings)) {
|
|
560
|
+
for (const element of bindings.elements)
|
|
561
|
+
if (!element.isTypeOnly)
|
|
562
|
+
names.add((element.propertyName ?? element.name).text);
|
|
728
563
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const mutations = Array.isArray(mutationRules) ? mutationRules.map((entry, index) => {
|
|
732
|
-
const rule = requireObject(entry, id, `localData.mutations[${index}] must be an object.`);
|
|
733
|
-
const allowedRuleKeys = new Set([
|
|
734
|
-
"conflict",
|
|
735
|
-
"match",
|
|
736
|
-
"onProtectionUnavailable",
|
|
737
|
-
"persistence",
|
|
738
|
-
"protection",
|
|
739
|
-
"sensitivity"
|
|
740
|
-
]);
|
|
741
|
-
const unsupportedRuleKey = Object.keys(rule).find((key) => !allowedRuleKeys.has(key));
|
|
742
|
-
if (unsupportedRuleKey)
|
|
743
|
-
throw metadataError(id, `localData.mutations[${index}].${unsupportedRuleKey} is not supported.`);
|
|
744
|
-
const protection = unknownField(rule, "protection");
|
|
745
|
-
const sensitivity = unknownField(rule, "sensitivity");
|
|
746
|
-
const persistence = unknownField(rule, "persistence");
|
|
747
|
-
const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
|
|
748
|
-
const declaredConflict = unknownField(rule, "conflict");
|
|
749
|
-
let conflict;
|
|
750
|
-
if (declaredConflict !== undefined) {
|
|
751
|
-
const conflictRecord = requireObject(declaredConflict, id, `localData.mutations[${index}].conflict must be an object.`);
|
|
752
|
-
const unsupportedConflictKey = Object.keys(conflictRecord).find((key) => key !== "maxAttempts" && key !== "strategy");
|
|
753
|
-
if (unsupportedConflictKey)
|
|
754
|
-
throw metadataError(id, `localData.mutations[${index}].conflict.${unsupportedConflictKey} is not supported.`);
|
|
755
|
-
const strategy = unknownField(conflictRecord, "strategy");
|
|
756
|
-
if (strategy !== "client-wins" && strategy !== "manual" && strategy !== "server-wins")
|
|
757
|
-
throw metadataError(id, `localData.mutations[${index}].conflict.strategy is invalid.`);
|
|
758
|
-
const maxAttempts = unknownField(conflictRecord, "maxAttempts");
|
|
759
|
-
if (maxAttempts !== undefined && strategy !== "client-wins")
|
|
760
|
-
throw metadataError(id, `localData.mutations[${index}].conflict.maxAttempts requires client-wins.`);
|
|
761
|
-
conflict = {
|
|
762
|
-
strategy,
|
|
763
|
-
...maxAttempts === undefined ? {} : {
|
|
764
|
-
maxAttempts: positiveVersion2(maxAttempts, id, `localData.mutations[${index}].conflict.maxAttempts`)
|
|
765
|
-
}
|
|
766
|
-
};
|
|
564
|
+
if (bindings && ts.isNamespaceImport(bindings))
|
|
565
|
+
namespaces.add(bindings.name.text);
|
|
767
566
|
}
|
|
768
|
-
if (
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
567
|
+
if (ts.isExportDeclaration(node) && !node.isTypeOnly && node.moduleSpecifier !== undefined && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === DEVICES_PACKAGE && node.exportClause && ts.isNamedExports(node.exportClause)) {
|
|
568
|
+
for (const element of node.exportClause.elements)
|
|
569
|
+
if (!element.isTypeOnly)
|
|
570
|
+
names.add((element.propertyName ?? element.name).text);
|
|
571
|
+
}
|
|
572
|
+
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && namespaces.has(node.expression.text))
|
|
573
|
+
names.add(node.name.text);
|
|
574
|
+
ts.forEachChild(node, visit);
|
|
575
|
+
};
|
|
576
|
+
const extension = extname3(file).toLowerCase();
|
|
577
|
+
const sources = extension === ".svelte" || extension === ".vue" ? [...source.matchAll(/<script\b[^>]*>([\s\S]*?)<\/script\s*>/giu)].map((match) => match[1]).filter((value) => value !== undefined) : [source];
|
|
578
|
+
for (const [index, script] of sources.entries())
|
|
579
|
+
visit(ts.createSourceFile(`${file}#script-${index}`, script, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX));
|
|
580
|
+
return names;
|
|
581
|
+
}, assertAbsoluteDeviceCapabilityPackages = (projectRoot, plan) => {
|
|
582
|
+
const missing = missingAbsoluteDeviceCapabilityPackages(plan, directAbsoluteProjectPackages(projectRoot));
|
|
583
|
+
const mismatched = plan.requiredPackages.filter((spec) => {
|
|
584
|
+
const separator = spec.lastIndexOf("@");
|
|
585
|
+
const packageName = spec.slice(0, separator);
|
|
586
|
+
if (missing.includes(spec))
|
|
587
|
+
return false;
|
|
588
|
+
try {
|
|
589
|
+
return readJson(join11(resolve10(projectRoot), "node_modules", packageName, "package.json")).version !== spec.slice(separator + 1);
|
|
590
|
+
} catch {
|
|
591
|
+
return true;
|
|
793
592
|
}
|
|
593
|
+
});
|
|
594
|
+
const unmet = [...missing, ...mismatched];
|
|
595
|
+
if (unmet.length > 0)
|
|
596
|
+
throw new TypeError(`Device capabilities ${plan.capabilities.join(", ")} require ${unmet.join(", ")}. Run absolute mobile sync and approve the detected capability plugins.`);
|
|
597
|
+
}, directAbsoluteProjectPackages = (projectRoot) => {
|
|
598
|
+
const manifest = readJson(join11(resolve10(projectRoot), "package.json"));
|
|
599
|
+
const packages = new Set;
|
|
600
|
+
for (const field2 of ["dependencies", "devDependencies"]) {
|
|
601
|
+
const dependencies = manifest[field2];
|
|
602
|
+
if (object(dependencies))
|
|
603
|
+
for (const name of Object.keys(dependencies))
|
|
604
|
+
packages.add(name);
|
|
605
|
+
}
|
|
606
|
+
return packages;
|
|
607
|
+
}, discoverAbsoluteDeviceCapabilities = (projectRoot, providers = loadAbsoluteDeviceCapabilityProviders(projectRoot)) => {
|
|
608
|
+
const root = resolve10(projectRoot);
|
|
609
|
+
if (!existsSync3(root))
|
|
610
|
+
return [];
|
|
611
|
+
const known = new Set(Object.keys(providers));
|
|
612
|
+
const capabilities = new Set;
|
|
613
|
+
for (const path of SOURCE_GLOB.scanSync({ cwd: root })) {
|
|
614
|
+
const portable = relative9(root, resolve10(root, path)).replaceAll("\\", "/");
|
|
615
|
+
if (isIgnored(portable))
|
|
616
|
+
continue;
|
|
617
|
+
const source = readFileSync2(resolve10(root, portable), "utf8");
|
|
618
|
+
for (const name of importedCapabilities(source, portable))
|
|
619
|
+
if (known.has(name))
|
|
620
|
+
capabilities.add(name);
|
|
621
|
+
}
|
|
622
|
+
return [...capabilities].sort();
|
|
623
|
+
}, missingAbsoluteDeviceCapabilityPackages = (plan, directPackages) => plan.requiredPackages.filter((spec) => {
|
|
624
|
+
const packageName = spec.slice(0, spec.lastIndexOf("@"));
|
|
625
|
+
return !directPackages.has(packageName);
|
|
626
|
+
}), projectImportsAbsoluteDeviceCapability = (projectRoot, capability) => {
|
|
627
|
+
const root = resolve10(projectRoot);
|
|
628
|
+
for (const path of SOURCE_GLOB.scanSync({ cwd: root })) {
|
|
629
|
+
const portable = relative9(root, resolve10(root, path)).replaceAll("\\", "/");
|
|
630
|
+
if (isIgnored(portable))
|
|
631
|
+
continue;
|
|
632
|
+
const source = readFileSync2(resolve10(root, portable), "utf8");
|
|
633
|
+
if (importedCapabilities(source, portable).has(capability))
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
return false;
|
|
637
|
+
}, resolveAbsoluteDeviceCapabilityPlan = (projectRoot, provider = "capacitor") => {
|
|
638
|
+
const allProviders = loadAbsoluteDeviceCapabilityProviders(projectRoot, provider);
|
|
639
|
+
const capabilities = discoverAbsoluteDeviceCapabilities(projectRoot, allProviders);
|
|
640
|
+
for (const capability of ABSOLUTE_MOBILE_SHELL_DEVICE_CAPABILITIES) {
|
|
641
|
+
if (allProviders[capability] && !capabilities.includes(capability))
|
|
642
|
+
capabilities.push(capability);
|
|
643
|
+
}
|
|
644
|
+
capabilities.sort();
|
|
645
|
+
const providers = {};
|
|
646
|
+
for (const name of capabilities) {
|
|
647
|
+
const capabilityProvider = allProviders[name];
|
|
648
|
+
if (capabilityProvider)
|
|
649
|
+
providers[name] = capabilityProvider;
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
capabilities,
|
|
653
|
+
providers,
|
|
654
|
+
requiredPackages: [
|
|
655
|
+
...new Set(capabilities.flatMap((name) => providers[name]?.packages ?? []))
|
|
656
|
+
].sort()
|
|
794
657
|
};
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
"
|
|
799
|
-
"
|
|
800
|
-
|
|
801
|
-
|
|
658
|
+
};
|
|
659
|
+
var init_deviceCapabilities = __esm(() => {
|
|
660
|
+
ABSOLUTE_MOBILE_SHELL_DEVICE_CAPABILITIES = [
|
|
661
|
+
"keyboard",
|
|
662
|
+
"systemBars"
|
|
663
|
+
];
|
|
664
|
+
ADAPTERS = {
|
|
665
|
+
capacitor: "@absolutejs/devices-capacitor",
|
|
666
|
+
expo: "@absolutejs/devices-expo"
|
|
667
|
+
};
|
|
668
|
+
SOURCE_GLOB = new Bun.Glob("**/*.{js,jsx,ts,tsx,svelte,vue}");
|
|
669
|
+
IGNORED_DIRECTORIES = new Set([
|
|
670
|
+
".absolutejs",
|
|
671
|
+
".git",
|
|
672
|
+
".test-builds",
|
|
673
|
+
".test-shards",
|
|
674
|
+
"build",
|
|
675
|
+
"dist",
|
|
676
|
+
"node_modules",
|
|
677
|
+
"test",
|
|
678
|
+
"tests"
|
|
802
679
|
]);
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
680
|
+
IDENTIFIER_PATTERN = /^[A-Za-z_$][\w$]*$/u;
|
|
681
|
+
ANDROID_PERMISSION_PATTERN = /^android\.permission\.[A-Z][A-Z0-9_]*$/u;
|
|
682
|
+
IOS_USAGE_DESCRIPTIONS = new Set([
|
|
683
|
+
"camera",
|
|
684
|
+
"location-always",
|
|
685
|
+
"location-when-in-use",
|
|
686
|
+
"photo-library",
|
|
687
|
+
"photo-library-add"
|
|
688
|
+
]);
|
|
689
|
+
IOS_PRIVACY_ACCESSED_API_REASONS = {
|
|
690
|
+
NSPrivacyAccessedAPICategoryFileTimestamp: new Set(["C617.1"])
|
|
691
|
+
};
|
|
692
|
+
IOS_PRIVACY_ACCESSED_APIS = [
|
|
693
|
+
"NSPrivacyAccessedAPICategoryFileTimestamp"
|
|
694
|
+
];
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
// src/mobile/nativeAuth.ts
|
|
698
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
699
|
+
import { join as join12 } from "path";
|
|
700
|
+
var ABSOLUTE_AUTH_PACKAGE = "@absolutejs/auth", ABSOLUTE_EXPO_AUTH_CORE_VERSION = "0.75.6", ABSOLUTE_EXPO_AUTH_PACKAGE = "@absolutejs/auth-expo", ABSOLUTE_EXPO_AUTH_VERSION = "0.0.2", ABSOLUTE_EXPO_SYNC_CORE_VERSION = "2.31.0", ABSOLUTE_EXPO_SYNC_PACKAGE = "@absolutejs/sync-expo", ABSOLUTE_EXPO_SYNC_VERSION = "0.0.2", ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV = "ABSOLUTE_AUTH_NATIVE_CLIENTS", ABSOLUTE_NATIVE_AUTH_SCOPES, ABSOLUTE_SYNC_PACKAGE = "@absolutejs/sync", readPackageManifest = (projectRoot) => {
|
|
701
|
+
try {
|
|
702
|
+
return JSON.parse(readFileSync3(join12(projectRoot, "package.json"), "utf8"));
|
|
703
|
+
} catch {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
}, packageManifestHas = (manifest, packageName) => {
|
|
707
|
+
if (typeof manifest !== "object" || manifest === null)
|
|
708
|
+
return false;
|
|
709
|
+
return [
|
|
710
|
+
Reflect.get(manifest, "dependencies"),
|
|
711
|
+
Reflect.get(manifest, "devDependencies"),
|
|
712
|
+
Reflect.get(manifest, "optionalDependencies"),
|
|
713
|
+
Reflect.get(manifest, "peerDependencies")
|
|
714
|
+
].some((dependencies) => typeof dependencies === "object" && dependencies !== null && Object.hasOwn(dependencies, packageName));
|
|
715
|
+
}, createAbsoluteMobileAuthManifest = (config) => {
|
|
716
|
+
const scheme = config.deepLinkScheme ?? config.appId.toLowerCase();
|
|
814
717
|
return {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
...
|
|
819
|
-
migrations: migrations.map((entry, index) => migration(entry, id, index))
|
|
820
|
-
} : {},
|
|
821
|
-
version
|
|
718
|
+
clientId: `absolutejs-native:${config.appId}`,
|
|
719
|
+
issuer: config.productionOrigin,
|
|
720
|
+
redirectUri: `${scheme}://auth/callback`,
|
|
721
|
+
scopes: [...ABSOLUTE_NATIVE_AUTH_SCOPES]
|
|
822
722
|
};
|
|
823
|
-
},
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
723
|
+
}, installAbsoluteMobileAuthEnvironment = (projectRoot, config) => {
|
|
724
|
+
const auth = resolveAbsoluteMobileAuthManifest(projectRoot, config);
|
|
725
|
+
const serialized = serializeAbsoluteMobileAuthEnvironment(config, auth);
|
|
726
|
+
if (serialized === undefined)
|
|
727
|
+
delete process.env[ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV];
|
|
728
|
+
else
|
|
729
|
+
process.env[ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV] = serialized;
|
|
730
|
+
return auth;
|
|
731
|
+
}, projectUsesAbsoluteAuth = (projectRoot) => packageManifestHas(readPackageManifest(projectRoot), ABSOLUTE_AUTH_PACKAGE), projectUsesAbsoluteSync = (projectRoot) => packageManifestHas(readPackageManifest(projectRoot), ABSOLUTE_SYNC_PACKAGE), resolveAbsoluteMobileAuthManifest = (projectRoot, config) => projectUsesAbsoluteAuth(projectRoot) ? createAbsoluteMobileAuthManifest(config) : undefined, serializeAbsoluteMobileAuthEnvironment = (config, auth) => auth === undefined ? undefined : JSON.stringify([
|
|
732
|
+
{
|
|
733
|
+
...auth,
|
|
734
|
+
name: `${config.appName} native app`
|
|
735
|
+
}
|
|
736
|
+
]);
|
|
737
|
+
var init_nativeAuth = __esm(() => {
|
|
738
|
+
ABSOLUTE_NATIVE_AUTH_SCOPES = ["openid", "profile"];
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
// node_modules/@absolutejs/sync/dist/client/index.js
|
|
742
|
+
var RUNTIME_TRANSPORT, host, isRegistry = (value) => typeof value === "object" && value !== null && Array.isArray(Reflect.get(value, "installations")) && Array.isArray(Reflect.get(value, "clients")), registry, pools, SyncLocalDataPolicyError, SyncLocalStoreSchemaError, positiveVersion = (value, label) => {
|
|
743
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
744
|
+
throw new SyncLocalStoreSchemaError("INVALID_PLAN", `${label} must be a positive safe integer`);
|
|
745
|
+
return value;
|
|
746
|
+
}, isSchemaBundle = (schema) => ("components" in schema), validatePolicyMatch = (match, label) => {
|
|
747
|
+
if (match.length === 0 || match.trim() !== match || /^\*+$/.test(match) || match.includes("**"))
|
|
748
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.match must be an exact name or a non-empty glob without adjacent wildcards.`);
|
|
749
|
+
}, validateSyncLocalDataPolicy = (policy, label = "localData") => {
|
|
750
|
+
if (policy.maxBytesPerNamespace !== undefined && (!Number.isSafeInteger(policy.maxBytesPerNamespace) || policy.maxBytesPerNamespace < 1))
|
|
751
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.maxBytesPerNamespace must be a positive safe integer.`);
|
|
752
|
+
for (const [index, rule] of (policy.collections ?? []).entries()) {
|
|
753
|
+
validatePolicyMatch(rule.match, `${label}.collections[${index}]`);
|
|
754
|
+
if (rule.maxAgeMs !== undefined && (!Number.isSafeInteger(rule.maxAgeMs) || rule.maxAgeMs < 1))
|
|
755
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}].maxAgeMs must be a positive safe integer.`);
|
|
756
|
+
if (rule.persistence === "memory-only" && rule.protection === "required")
|
|
757
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}] cannot require at-rest protection when it is memory-only.`);
|
|
758
|
+
if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
|
|
759
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}] declares ${rule.sensitivity} data without required protection or memory-only persistence.`);
|
|
760
|
+
}
|
|
761
|
+
for (const [index, rule] of (policy.mutations ?? []).entries()) {
|
|
762
|
+
validatePolicyMatch(rule.match, `${label}.mutations[${index}]`);
|
|
763
|
+
if (rule.conflict !== undefined && rule.conflict.strategy !== "client-wins" && rule.conflict.strategy !== "manual" && rule.conflict.strategy !== "server-wins")
|
|
764
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.strategy is invalid.`);
|
|
765
|
+
if (rule.conflict?.maxAttempts !== undefined && (!Number.isSafeInteger(rule.conflict.maxAttempts) || rule.conflict.maxAttempts < 1))
|
|
766
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts must be a positive safe integer.`);
|
|
767
|
+
if (rule.conflict?.maxAttempts !== undefined && rule.conflict.strategy !== "client-wins")
|
|
768
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts is only valid for client-wins.`);
|
|
769
|
+
if (rule.persistence === "memory-only" && rule.protection === "required")
|
|
770
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] cannot require at-rest protection when it is memory-only.`);
|
|
771
|
+
if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
|
|
772
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] declares ${rule.sensitivity} arguments without required protection.`);
|
|
773
|
+
}
|
|
774
|
+
return policy;
|
|
775
|
+
}, normalizeSyncLocalSchemaComponents = (schema = { version: 1 }) => {
|
|
776
|
+
const components = isSchemaBundle(schema) ? [...schema.components] : [{ ...schema, id: "@absolutejs/app" }];
|
|
777
|
+
const ids = new Set;
|
|
778
|
+
for (const component of components) {
|
|
779
|
+
if (typeof component.id !== "string" || component.id.trim() !== component.id || component.id.length === 0)
|
|
780
|
+
throw new SyncLocalStoreSchemaError("INVALID_PLAN", "Sync schema component id must be non-empty and trimmed");
|
|
781
|
+
if (ids.has(component.id))
|
|
782
|
+
throw new SyncLocalStoreSchemaError("INVALID_PLAN", `Sync schema component "${component.id}" is declared more than once`);
|
|
783
|
+
ids.add(component.id);
|
|
784
|
+
if (component.localData)
|
|
785
|
+
validateSyncLocalDataPolicy(component.localData, `${component.id}.localData`);
|
|
786
|
+
}
|
|
787
|
+
return components.sort((a, b) => a.id.localeCompare(b.id));
|
|
788
|
+
}, resolveSyncLocalSchemaComponents = (storedVersions, schema = { version: 1 }) => {
|
|
789
|
+
const components = normalizeSyncLocalSchemaComponents(schema).map((component) => {
|
|
790
|
+
const current = resolveSyncLocalMigrations(component.version, component);
|
|
832
791
|
return {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
const
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
const manifestPath = packageManifestPath(projectRoot, name);
|
|
851
|
-
if (!manifestPath)
|
|
852
|
-
continue;
|
|
853
|
-
const manifest = manifestAt(manifestPath);
|
|
854
|
-
if (!manifest)
|
|
855
|
-
continue;
|
|
856
|
-
const metadata = localSchemaMetadata(manifest);
|
|
857
|
-
if (metadata === undefined)
|
|
858
|
-
continue;
|
|
859
|
-
components.push(component(name, metadata));
|
|
860
|
-
sources.push({ id: name, manifestPath });
|
|
792
|
+
id: component.id,
|
|
793
|
+
...resolveSyncLocalMigrations(storedVersions[component.id] ?? current.minimumCompatibleVersion, component)
|
|
794
|
+
};
|
|
795
|
+
});
|
|
796
|
+
const active = new Set(components.map((component) => component.id));
|
|
797
|
+
const orphanedComponents = Object.keys(storedVersions).filter((id) => !active.has(id)).sort();
|
|
798
|
+
return { components, orphanedComponents };
|
|
799
|
+
}, resolveSyncLocalMigrations = (storedVersion, schema = { version: 1 }) => {
|
|
800
|
+
positiveVersion(storedVersion, "Stored Sync schema version");
|
|
801
|
+
const targetVersion = positiveVersion(schema.version, "Target Sync schema version");
|
|
802
|
+
const migrations = [...schema.migrations ?? []].sort((a, b) => a.toVersion - b.toVersion);
|
|
803
|
+
const versions = new Set;
|
|
804
|
+
for (const migration of migrations) {
|
|
805
|
+
positiveVersion(migration.toVersion, "Sync migration toVersion");
|
|
806
|
+
if (versions.has(migration.toVersion))
|
|
807
|
+
throw new SyncLocalStoreSchemaError("INVALID_PLAN", `Sync migration ${migration.toVersion} is declared more than once`);
|
|
808
|
+
versions.add(migration.toVersion);
|
|
861
809
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
810
|
+
const inferredMinimum = migrations[0] ? migrations[0].toVersion - 1 : targetVersion;
|
|
811
|
+
const minimumCompatibleVersion = positiveVersion(schema.minimumCompatibleVersion ?? inferredMinimum, "Minimum compatible Sync schema version");
|
|
812
|
+
if (minimumCompatibleVersion > targetVersion)
|
|
813
|
+
throw new SyncLocalStoreSchemaError("INVALID_PLAN", "Minimum compatible Sync schema version cannot exceed its target");
|
|
814
|
+
if (storedVersion > targetVersion)
|
|
815
|
+
throw new SyncLocalStoreSchemaError("SCHEMA_TOO_NEW", `Stored Sync schema ${storedVersion} is newer than this runtime's schema ${targetVersion}`, { storedVersion, targetVersion });
|
|
816
|
+
if (storedVersion < minimumCompatibleVersion)
|
|
817
|
+
throw new SyncLocalStoreSchemaError("SCHEMA_TOO_OLD", `Stored Sync schema ${storedVersion} is older than the minimum compatible schema ${minimumCompatibleVersion}`, { storedVersion, targetVersion });
|
|
818
|
+
const steps = [];
|
|
819
|
+
for (let version = storedVersion + 1;version <= targetVersion; version++) {
|
|
820
|
+
const migration = migrations.find((candidate) => candidate.toVersion === version);
|
|
821
|
+
if (migration === undefined)
|
|
822
|
+
throw new SyncLocalStoreSchemaError("MIGRATION_MISSING", `Sync migration ${version - 1} -> ${version} is missing`, { storedVersion, targetVersion });
|
|
823
|
+
steps.push(migration);
|
|
824
|
+
}
|
|
825
|
+
return { minimumCompatibleVersion, steps, targetVersion };
|
|
866
826
|
};
|
|
867
|
-
var
|
|
868
|
-
|
|
827
|
+
var init_client = __esm(() => {
|
|
828
|
+
RUNTIME_TRANSPORT = Symbol.for("@absolutejs/sync/client-runtime-transport");
|
|
829
|
+
host = globalThis;
|
|
830
|
+
registry = (() => {
|
|
831
|
+
const existing = host[RUNTIME_TRANSPORT];
|
|
832
|
+
if (isRegistry(existing))
|
|
833
|
+
return existing;
|
|
834
|
+
if (typeof existing === "object" && existing !== null && Array.isArray(Reflect.get(existing, "installations"))) {
|
|
835
|
+
Reflect.set(existing, "clients", []);
|
|
836
|
+
return existing;
|
|
837
|
+
}
|
|
838
|
+
const created = { clients: [], installations: [] };
|
|
839
|
+
Object.defineProperty(host, RUNTIME_TRANSPORT, {
|
|
840
|
+
configurable: false,
|
|
841
|
+
enumerable: false,
|
|
842
|
+
value: created,
|
|
843
|
+
writable: false
|
|
844
|
+
});
|
|
845
|
+
return created;
|
|
846
|
+
})();
|
|
847
|
+
pools = new Map;
|
|
848
|
+
SyncLocalDataPolicyError = class SyncLocalDataPolicyError extends Error {
|
|
849
|
+
code;
|
|
850
|
+
constructor(code, message) {
|
|
851
|
+
super(message);
|
|
852
|
+
this.name = "SyncLocalDataPolicyError";
|
|
853
|
+
this.code = code;
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
SyncLocalStoreSchemaError = class SyncLocalStoreSchemaError extends Error {
|
|
857
|
+
code;
|
|
858
|
+
storedVersion;
|
|
859
|
+
targetVersion;
|
|
860
|
+
constructor(code, message, versions = {}) {
|
|
861
|
+
super(message);
|
|
862
|
+
this.name = "SyncLocalStoreSchemaError";
|
|
863
|
+
this.code = code;
|
|
864
|
+
this.storedVersion = versions.storedVersion;
|
|
865
|
+
this.targetVersion = versions.targetVersion;
|
|
866
|
+
}
|
|
867
|
+
};
|
|
869
868
|
});
|
|
870
869
|
|
|
871
|
-
// src/mobile/
|
|
872
|
-
import {
|
|
873
|
-
import {
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
throw new TypeError(`${path} must contain an object.`);
|
|
880
|
-
return value;
|
|
881
|
-
}, text = (value, field2) => {
|
|
882
|
-
if (typeof value !== "string" || value.length === 0)
|
|
883
|
-
throw new TypeError(`${field2} must be a non-empty string.`);
|
|
884
|
-
return value;
|
|
885
|
-
}, androidPermissions = (value, field2) => {
|
|
886
|
-
if (value === undefined)
|
|
887
|
-
return;
|
|
888
|
-
if (!object2(value))
|
|
889
|
-
throw new TypeError(`${field2} must be an object.`);
|
|
890
|
-
const { permissions } = value;
|
|
891
|
-
if (!Array.isArray(permissions) || !permissions.every((permission) => typeof permission === "string" && ANDROID_PERMISSION_PATTERN.test(permission)))
|
|
892
|
-
throw new TypeError(`${field2}.permissions must contain Android permission names.`);
|
|
893
|
-
return [...permissions];
|
|
894
|
-
}, iosPrivacyAccessedApis = (value, field2) => {
|
|
895
|
-
if (value === undefined)
|
|
870
|
+
// src/mobile/syncSchema.ts
|
|
871
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
872
|
+
import { dirname as dirname9, join as join13, resolve as resolve11 } from "path";
|
|
873
|
+
var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), manifestAt = (path) => {
|
|
874
|
+
try {
|
|
875
|
+
const value = JSON.parse(readFileSync4(path, "utf8"));
|
|
876
|
+
return object2(value) ? value : undefined;
|
|
877
|
+
} catch {
|
|
896
878
|
return;
|
|
897
|
-
if (!object2(value))
|
|
898
|
-
throw new TypeError(`${field2} must be an object.`);
|
|
899
|
-
const privacy = {};
|
|
900
|
-
for (const api of IOS_PRIVACY_ACCESSED_APIS) {
|
|
901
|
-
const reasons = value[api];
|
|
902
|
-
if (reasons === undefined)
|
|
903
|
-
continue;
|
|
904
|
-
const supported = IOS_PRIVACY_ACCESSED_API_REASONS[api];
|
|
905
|
-
if (!Array.isArray(reasons) || reasons.length === 0 || !reasons.every((reason) => typeof reason === "string" && supported.has(reason)))
|
|
906
|
-
throw new TypeError(`${field2} contains an unsupported API or reason.`);
|
|
907
|
-
privacy[api] = [...reasons];
|
|
908
879
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
}, iosNativeRequirements = (value, field2) => {
|
|
913
|
-
if (value === undefined)
|
|
880
|
+
}, localSchemaMetadata = (manifest) => {
|
|
881
|
+
const absolutejs = Reflect.get(manifest, "absolutejs");
|
|
882
|
+
if (!object2(absolutejs))
|
|
914
883
|
return;
|
|
884
|
+
const sync = Reflect.get(absolutejs, "sync");
|
|
885
|
+
if (!object2(sync))
|
|
886
|
+
return;
|
|
887
|
+
return Reflect.get(sync, "localSchema");
|
|
888
|
+
}, packageManifestPath = (projectRoot, packageName) => {
|
|
889
|
+
let directory = resolve11(projectRoot);
|
|
890
|
+
while (true) {
|
|
891
|
+
const candidate = join13(directory, "node_modules", packageName, "package.json");
|
|
892
|
+
const manifest = manifestAt(candidate);
|
|
893
|
+
if (manifest && Reflect.get(manifest, "name") === packageName)
|
|
894
|
+
return candidate;
|
|
895
|
+
const parent = dirname9(directory);
|
|
896
|
+
if (parent === directory)
|
|
897
|
+
return;
|
|
898
|
+
directory = parent;
|
|
899
|
+
}
|
|
900
|
+
}, metadataError = (id, detail) => new TypeError(`Invalid AbsoluteJS Sync schema metadata for ${id}: ${detail}`), positiveVersion2 = (value, id, field2) => {
|
|
901
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)
|
|
902
|
+
throw metadataError(id, `${field2} must be a positive safe integer.`);
|
|
903
|
+
return value;
|
|
904
|
+
}, nonEmpty = (value, id, field2) => {
|
|
905
|
+
if (typeof value !== "string" || value.trim() !== value || value.length === 0)
|
|
906
|
+
throw metadataError(id, `${field2} must be a non-empty trimmed string.`);
|
|
907
|
+
return value;
|
|
908
|
+
}, requireObject = (value, id, detail) => {
|
|
915
909
|
if (!object2(value))
|
|
916
|
-
throw
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
if (
|
|
924
|
-
|
|
925
|
-
if (
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
if (
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
if (!IDENTIFIER_PATTERN.test(factory))
|
|
944
|
-
throw new TypeError(`${name}.factory must be a JavaScript identifier.`);
|
|
945
|
-
if (!providerModulePattern(providerName).test(module))
|
|
946
|
-
throw new TypeError(`${name}.module must be an official devices-${providerName} subpath.`);
|
|
947
|
-
if (!Array.isArray(value.packages) || !value.packages.every((spec) => typeof spec === "string" && providerPackagePattern(providerName).test(spec)))
|
|
948
|
-
throw new TypeError(`${name}.packages must contain exact official ${providerLabel(providerName)} package versions.`);
|
|
949
|
-
const { plugins } = value;
|
|
950
|
-
if (plugins !== undefined && (!Array.isArray(plugins) || !plugins.every((plugin) => typeof plugin === "string" && /^expo-[a-z][a-z0-9-]*$/u.test(plugin))))
|
|
951
|
-
throw new TypeError(`${name}.plugins must contain Expo config plugin names.`);
|
|
952
|
-
let native;
|
|
953
|
-
const { native: nativeMetadata } = value;
|
|
954
|
-
if (nativeMetadata !== undefined) {
|
|
955
|
-
if (!object2(nativeMetadata))
|
|
956
|
-
throw new TypeError(`${name}.native must be an object.`);
|
|
957
|
-
const { android, ios } = nativeMetadata;
|
|
958
|
-
const permissions = androidPermissions(android, `${name}.native.android`);
|
|
959
|
-
const iosRequirements = iosNativeRequirements(ios, `${name}.native.ios`);
|
|
960
|
-
native = {
|
|
961
|
-
...permissions === undefined ? {} : { android: { permissions } },
|
|
962
|
-
...iosRequirements === undefined ? {} : { ios: iosRequirements }
|
|
910
|
+
throw metadataError(id, detail);
|
|
911
|
+
return value;
|
|
912
|
+
}, unknownField = (record, key) => record[key], normalizeJsonValue2 = (value, id, field2) => {
|
|
913
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
914
|
+
return value;
|
|
915
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
916
|
+
return value;
|
|
917
|
+
if (Array.isArray(value))
|
|
918
|
+
return value.map((entry) => normalizeJsonValue2(entry, id, field2));
|
|
919
|
+
if (object2(value))
|
|
920
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
921
|
+
key,
|
|
922
|
+
normalizeJsonValue2(entry, id, field2)
|
|
923
|
+
]));
|
|
924
|
+
throw metadataError(id, `${field2} must be JSON-safe.`);
|
|
925
|
+
}, operation = (value, id, index) => {
|
|
926
|
+
const record = requireObject(value, id, `migration operation ${index} must be an object.`);
|
|
927
|
+
const type = Reflect.get(record, "type");
|
|
928
|
+
const collection = nonEmpty(Reflect.get(record, "collection"), id, `operation ${index}.collection`);
|
|
929
|
+
if (type === "delete-collection")
|
|
930
|
+
return { collection, type };
|
|
931
|
+
if (type === "rename-field")
|
|
932
|
+
return {
|
|
933
|
+
collection,
|
|
934
|
+
from: nonEmpty(Reflect.get(record, "from"), id, `operation ${index}.from`),
|
|
935
|
+
to: nonEmpty(Reflect.get(record, "to"), id, `operation ${index}.to`),
|
|
936
|
+
type
|
|
963
937
|
};
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
938
|
+
const field2 = nonEmpty(Reflect.get(record, "field"), id, `operation ${index}.field`);
|
|
939
|
+
if (type === "remove-field")
|
|
940
|
+
return { collection, field: field2, type };
|
|
941
|
+
if (type === "set-default")
|
|
942
|
+
return {
|
|
943
|
+
collection,
|
|
944
|
+
field: field2,
|
|
945
|
+
type,
|
|
946
|
+
value: normalizeJsonValue2(Reflect.get(record, "value"), id, `operation ${index}.value`)
|
|
947
|
+
};
|
|
948
|
+
throw metadataError(id, `operation ${index}.type is not supported.`);
|
|
949
|
+
}, migration = (value, id, index) => {
|
|
950
|
+
const record = requireObject(value, id, `migration ${index} must be an object.`);
|
|
951
|
+
const allowed = new Set(["operations", "toVersion"]);
|
|
952
|
+
const unsupported = Object.keys(record).find((key) => !allowed.has(key));
|
|
953
|
+
if (unsupported)
|
|
954
|
+
throw metadataError(id, `migration ${index}.${unsupported} is not declarative metadata.`);
|
|
955
|
+
const declaredOperations = Reflect.get(record, "operations");
|
|
956
|
+
if (declaredOperations !== undefined && !Array.isArray(declaredOperations))
|
|
957
|
+
throw metadataError(id, `migration ${index}.operations must be an array.`);
|
|
958
|
+
const operations = Array.isArray(declaredOperations) ? declaredOperations : [];
|
|
985
959
|
return {
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
].sort(),
|
|
989
|
-
iosPrivacyAccessedApis: IOS_PRIVACY_ACCESSED_APIS.flatMap((api) => {
|
|
990
|
-
const reasons = privacy[api];
|
|
991
|
-
return reasons ? [{ api, reasons: [...reasons].sort() }] : [];
|
|
992
|
-
}),
|
|
993
|
-
iosPushNotifications: plan.capabilities.some((name) => plan.providers[name]?.native?.ios?.pushNotifications === true),
|
|
994
|
-
iosSystemBars: plan.capabilities.some((name) => plan.providers[name]?.native?.ios?.systemBars === true),
|
|
995
|
-
iosUsageDescriptions: [
|
|
996
|
-
...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.ios?.usageDescriptions ?? []))
|
|
997
|
-
].sort()
|
|
960
|
+
operations: operations.map((entry, operationIndex) => operation(entry, id, operationIndex)),
|
|
961
|
+
toVersion: positiveVersion2(Reflect.get(record, "toVersion"), id, `migration ${index}.toVersion`)
|
|
998
962
|
};
|
|
999
|
-
},
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
const
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
963
|
+
}, localDataPolicy = (value, id) => {
|
|
964
|
+
const record = requireObject(value, id, "localData must be an object.");
|
|
965
|
+
const allowed = new Set([
|
|
966
|
+
"collections",
|
|
967
|
+
"maxBytesPerNamespace",
|
|
968
|
+
"mutations"
|
|
969
|
+
]);
|
|
970
|
+
const unsupported = Object.keys(record).find((key) => !allowed.has(key));
|
|
971
|
+
if (unsupported)
|
|
972
|
+
throw metadataError(id, `localData.${unsupported} is not supported.`);
|
|
973
|
+
const collectionRules = Reflect.get(record, "collections");
|
|
974
|
+
const mutationRules = Reflect.get(record, "mutations");
|
|
975
|
+
if (collectionRules !== undefined && !Array.isArray(collectionRules))
|
|
976
|
+
throw metadataError(id, "localData.collections must be an array.");
|
|
977
|
+
if (mutationRules !== undefined && !Array.isArray(mutationRules))
|
|
978
|
+
throw metadataError(id, "localData.mutations must be an array.");
|
|
979
|
+
const collections = Array.isArray(collectionRules) ? collectionRules.map((entry, index) => {
|
|
980
|
+
const rule = requireObject(entry, id, `localData.collections[${index}] must be an object.`);
|
|
981
|
+
const allowedRuleKeys = new Set([
|
|
982
|
+
"evictionPriority",
|
|
983
|
+
"match",
|
|
984
|
+
"maxAgeMs",
|
|
985
|
+
"onProtectionUnavailable",
|
|
986
|
+
"persistence",
|
|
987
|
+
"protection",
|
|
988
|
+
"sensitivity"
|
|
989
|
+
]);
|
|
990
|
+
const unsupportedRuleKey = Object.keys(rule).find((key) => !allowedRuleKeys.has(key));
|
|
991
|
+
if (unsupportedRuleKey)
|
|
992
|
+
throw metadataError(id, `localData.collections[${index}].${unsupportedRuleKey} is not supported.`);
|
|
993
|
+
const match = nonEmpty(Reflect.get(rule, "match"), id, `localData.collections[${index}].match`);
|
|
994
|
+
const persistence = unknownField(rule, "persistence");
|
|
995
|
+
const sensitivity = unknownField(rule, "sensitivity");
|
|
996
|
+
const protection = unknownField(rule, "protection");
|
|
997
|
+
const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
|
|
998
|
+
const evictionPriority = unknownField(rule, "evictionPriority");
|
|
999
|
+
const maxAge = unknownField(rule, "maxAgeMs");
|
|
1000
|
+
if (persistence !== undefined && persistence !== "durable" && persistence !== "memory-only")
|
|
1001
|
+
throw metadataError(id, `localData.collections[${index}].persistence is invalid.`);
|
|
1002
|
+
if (sensitivity !== undefined && sensitivity !== "public" && sensitivity !== "private" && sensitivity !== "secret")
|
|
1003
|
+
throw metadataError(id, `localData.collections[${index}].sensitivity is invalid.`);
|
|
1004
|
+
if (protection !== undefined && protection !== "none" && protection !== "required")
|
|
1005
|
+
throw metadataError(id, `localData.collections[${index}].protection is invalid.`);
|
|
1006
|
+
if (onProtectionUnavailable !== undefined && onProtectionUnavailable !== "error" && onProtectionUnavailable !== "memory-only")
|
|
1007
|
+
throw metadataError(id, `localData.collections[${index}].onProtectionUnavailable is invalid.`);
|
|
1008
|
+
if (evictionPriority !== undefined && evictionPriority !== "critical" && evictionPriority !== "normal" && evictionPriority !== "disposable")
|
|
1009
|
+
throw metadataError(id, `localData.collections[${index}].evictionPriority is invalid.`);
|
|
1010
|
+
return {
|
|
1011
|
+
match,
|
|
1012
|
+
...sensitivity ? { sensitivity } : {},
|
|
1013
|
+
...persistence ? { persistence } : {},
|
|
1014
|
+
...protection ? { protection } : {},
|
|
1015
|
+
...onProtectionUnavailable ? {
|
|
1016
|
+
onProtectionUnavailable
|
|
1017
|
+
} : {},
|
|
1018
|
+
...evictionPriority ? { evictionPriority } : {},
|
|
1019
|
+
...maxAge === undefined ? {} : {
|
|
1020
|
+
maxAgeMs: positiveVersion2(maxAge, id, `localData.collections[${index}].maxAgeMs`)
|
|
1030
1021
|
}
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1022
|
+
};
|
|
1023
|
+
}) : undefined;
|
|
1024
|
+
const mutations = Array.isArray(mutationRules) ? mutationRules.map((entry, index) => {
|
|
1025
|
+
const rule = requireObject(entry, id, `localData.mutations[${index}] must be an object.`);
|
|
1026
|
+
const allowedRuleKeys = new Set([
|
|
1027
|
+
"conflict",
|
|
1028
|
+
"match",
|
|
1029
|
+
"onProtectionUnavailable",
|
|
1030
|
+
"persistence",
|
|
1031
|
+
"protection",
|
|
1032
|
+
"sensitivity"
|
|
1033
|
+
]);
|
|
1034
|
+
const unsupportedRuleKey = Object.keys(rule).find((key) => !allowedRuleKeys.has(key));
|
|
1035
|
+
if (unsupportedRuleKey)
|
|
1036
|
+
throw metadataError(id, `localData.mutations[${index}].${unsupportedRuleKey} is not supported.`);
|
|
1037
|
+
const protection = unknownField(rule, "protection");
|
|
1038
|
+
const sensitivity = unknownField(rule, "sensitivity");
|
|
1039
|
+
const persistence = unknownField(rule, "persistence");
|
|
1040
|
+
const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
|
|
1041
|
+
const declaredConflict = unknownField(rule, "conflict");
|
|
1042
|
+
let conflict;
|
|
1043
|
+
if (declaredConflict !== undefined) {
|
|
1044
|
+
const conflictRecord = requireObject(declaredConflict, id, `localData.mutations[${index}].conflict must be an object.`);
|
|
1045
|
+
const unsupportedConflictKey = Object.keys(conflictRecord).find((key) => key !== "maxAttempts" && key !== "strategy");
|
|
1046
|
+
if (unsupportedConflictKey)
|
|
1047
|
+
throw metadataError(id, `localData.mutations[${index}].conflict.${unsupportedConflictKey} is not supported.`);
|
|
1048
|
+
const strategy = unknownField(conflictRecord, "strategy");
|
|
1049
|
+
if (strategy !== "client-wins" && strategy !== "manual" && strategy !== "server-wins")
|
|
1050
|
+
throw metadataError(id, `localData.mutations[${index}].conflict.strategy is invalid.`);
|
|
1051
|
+
const maxAttempts = unknownField(conflictRecord, "maxAttempts");
|
|
1052
|
+
if (maxAttempts !== undefined && strategy !== "client-wins")
|
|
1053
|
+
throw metadataError(id, `localData.mutations[${index}].conflict.maxAttempts requires client-wins.`);
|
|
1054
|
+
conflict = {
|
|
1055
|
+
strategy,
|
|
1056
|
+
...maxAttempts === undefined ? {} : {
|
|
1057
|
+
maxAttempts: positiveVersion2(maxAttempts, id, `localData.mutations[${index}].conflict.maxAttempts`)
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1059
1060
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
const
|
|
1080
|
-
for (const path of SOURCE_GLOB.scanSync({ cwd: root })) {
|
|
1081
|
-
const portable = relative9(root, resolve11(root, path)).replaceAll("\\", "/");
|
|
1082
|
-
if (isIgnored(portable))
|
|
1083
|
-
continue;
|
|
1084
|
-
const source = readFileSync4(resolve11(root, portable), "utf8");
|
|
1085
|
-
for (const name of importedCapabilities(source, portable))
|
|
1086
|
-
if (known.has(name))
|
|
1087
|
-
capabilities.add(name);
|
|
1088
|
-
}
|
|
1089
|
-
return [...capabilities].sort();
|
|
1090
|
-
}, missingAbsoluteDeviceCapabilityPackages = (plan, directPackages) => plan.requiredPackages.filter((spec) => {
|
|
1091
|
-
const packageName = spec.slice(0, spec.lastIndexOf("@"));
|
|
1092
|
-
return !directPackages.has(packageName);
|
|
1093
|
-
}), projectImportsAbsoluteDeviceCapability = (projectRoot, capability) => {
|
|
1094
|
-
const root = resolve11(projectRoot);
|
|
1095
|
-
for (const path of SOURCE_GLOB.scanSync({ cwd: root })) {
|
|
1096
|
-
const portable = relative9(root, resolve11(root, path)).replaceAll("\\", "/");
|
|
1097
|
-
if (isIgnored(portable))
|
|
1098
|
-
continue;
|
|
1099
|
-
const source = readFileSync4(resolve11(root, portable), "utf8");
|
|
1100
|
-
if (importedCapabilities(source, portable).has(capability))
|
|
1101
|
-
return true;
|
|
1102
|
-
}
|
|
1103
|
-
return false;
|
|
1104
|
-
}, resolveAbsoluteDeviceCapabilityPlan = (projectRoot, provider = "capacitor") => {
|
|
1105
|
-
const allProviders = loadAbsoluteDeviceCapabilityProviders(projectRoot, provider);
|
|
1106
|
-
const capabilities = discoverAbsoluteDeviceCapabilities(projectRoot, allProviders);
|
|
1107
|
-
for (const capability of ABSOLUTE_MOBILE_SHELL_DEVICE_CAPABILITIES) {
|
|
1108
|
-
if (allProviders[capability] && !capabilities.includes(capability))
|
|
1109
|
-
capabilities.push(capability);
|
|
1110
|
-
}
|
|
1111
|
-
capabilities.sort();
|
|
1112
|
-
const providers = {};
|
|
1113
|
-
for (const name of capabilities) {
|
|
1114
|
-
const capabilityProvider = allProviders[name];
|
|
1115
|
-
if (capabilityProvider)
|
|
1116
|
-
providers[name] = capabilityProvider;
|
|
1117
|
-
}
|
|
1061
|
+
if (protection !== undefined && protection !== "none" && protection !== "required")
|
|
1062
|
+
throw metadataError(id, `localData.mutations[${index}].protection is invalid.`);
|
|
1063
|
+
if (sensitivity !== undefined && sensitivity !== "public" && sensitivity !== "private" && sensitivity !== "secret")
|
|
1064
|
+
throw metadataError(id, `localData.mutations[${index}].sensitivity is invalid.`);
|
|
1065
|
+
if (onProtectionUnavailable !== undefined && onProtectionUnavailable !== "error" && onProtectionUnavailable !== "memory-only")
|
|
1066
|
+
throw metadataError(id, `localData.mutations[${index}].onProtectionUnavailable is invalid.`);
|
|
1067
|
+
if (persistence !== undefined && persistence !== "durable" && persistence !== "memory-only")
|
|
1068
|
+
throw metadataError(id, `localData.mutations[${index}].persistence is invalid.`);
|
|
1069
|
+
return {
|
|
1070
|
+
match: nonEmpty(Reflect.get(rule, "match"), id, `localData.mutations[${index}].match`),
|
|
1071
|
+
...conflict ? { conflict } : {},
|
|
1072
|
+
...sensitivity ? { sensitivity } : {},
|
|
1073
|
+
...onProtectionUnavailable ? { onProtectionUnavailable } : {},
|
|
1074
|
+
...persistence ? {
|
|
1075
|
+
persistence
|
|
1076
|
+
} : {},
|
|
1077
|
+
...protection ? { protection } : {}
|
|
1078
|
+
};
|
|
1079
|
+
}) : undefined;
|
|
1080
|
+
const quota = Reflect.get(record, "maxBytesPerNamespace");
|
|
1118
1081
|
return {
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
};
|
|
1125
|
-
};
|
|
1126
|
-
var init_deviceCapabilities = __esm(() => {
|
|
1127
|
-
ABSOLUTE_MOBILE_SHELL_DEVICE_CAPABILITIES = [
|
|
1128
|
-
"keyboard",
|
|
1129
|
-
"systemBars"
|
|
1130
|
-
];
|
|
1131
|
-
ADAPTERS = {
|
|
1132
|
-
capacitor: "@absolutejs/devices-capacitor",
|
|
1133
|
-
expo: "@absolutejs/devices-expo"
|
|
1082
|
+
...collections ? { collections } : {},
|
|
1083
|
+
...mutations ? { mutations } : {},
|
|
1084
|
+
...quota === undefined ? {} : {
|
|
1085
|
+
maxBytesPerNamespace: positiveVersion2(quota, id, "localData.maxBytesPerNamespace")
|
|
1086
|
+
}
|
|
1134
1087
|
};
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
"
|
|
1139
|
-
"
|
|
1140
|
-
"
|
|
1141
|
-
"
|
|
1142
|
-
"dist",
|
|
1143
|
-
"node_modules",
|
|
1144
|
-
"test",
|
|
1145
|
-
"tests"
|
|
1146
|
-
]);
|
|
1147
|
-
IDENTIFIER_PATTERN = /^[A-Za-z_$][\w$]*$/u;
|
|
1148
|
-
ANDROID_PERMISSION_PATTERN = /^android\.permission\.[A-Z][A-Z0-9_]*$/u;
|
|
1149
|
-
IOS_USAGE_DESCRIPTIONS = new Set([
|
|
1150
|
-
"camera",
|
|
1151
|
-
"location-always",
|
|
1152
|
-
"location-when-in-use",
|
|
1153
|
-
"photo-library",
|
|
1154
|
-
"photo-library-add"
|
|
1088
|
+
}, component = (id, value) => {
|
|
1089
|
+
const record = requireObject(value, id, "localSchema must be an object.");
|
|
1090
|
+
const allowed = new Set([
|
|
1091
|
+
"localData",
|
|
1092
|
+
"migrations",
|
|
1093
|
+
"minimumCompatibleVersion",
|
|
1094
|
+
"version"
|
|
1155
1095
|
]);
|
|
1156
|
-
|
|
1157
|
-
|
|
1096
|
+
const unsupported = Object.keys(record).find((key) => !allowed.has(key));
|
|
1097
|
+
if (unsupported)
|
|
1098
|
+
throw metadataError(id, `${unsupported} is not supported.`);
|
|
1099
|
+
const version = positiveVersion2(Reflect.get(record, "version"), id, "version");
|
|
1100
|
+
const declaredMinimum = Reflect.get(record, "minimumCompatibleVersion");
|
|
1101
|
+
const minimumCompatibleVersion = declaredMinimum === undefined ? Math.max(1, version - 2) : positiveVersion2(declaredMinimum, id, "minimumCompatibleVersion");
|
|
1102
|
+
const declaredMigrations = Reflect.get(record, "migrations");
|
|
1103
|
+
const declaredLocalData = Reflect.get(record, "localData");
|
|
1104
|
+
if (declaredMigrations !== undefined && !Array.isArray(declaredMigrations))
|
|
1105
|
+
throw metadataError(id, "migrations must be an array.");
|
|
1106
|
+
const migrations = Array.isArray(declaredMigrations) ? declaredMigrations : undefined;
|
|
1107
|
+
return {
|
|
1108
|
+
id,
|
|
1109
|
+
...declaredLocalData === undefined ? {} : { localData: localDataPolicy(declaredLocalData, id) },
|
|
1110
|
+
minimumCompatibleVersion,
|
|
1111
|
+
...Array.isArray(migrations) ? {
|
|
1112
|
+
migrations: migrations.map((entry, index) => migration(entry, id, index))
|
|
1113
|
+
} : {},
|
|
1114
|
+
version
|
|
1158
1115
|
};
|
|
1159
|
-
|
|
1160
|
-
|
|
1116
|
+
}, dependencyNames = (manifest) => [
|
|
1117
|
+
Reflect.get(manifest, "dependencies"),
|
|
1118
|
+
Reflect.get(manifest, "optionalDependencies"),
|
|
1119
|
+
Reflect.get(manifest, "devDependencies"),
|
|
1120
|
+
Reflect.get(manifest, "peerDependencies")
|
|
1121
|
+
].flatMap((dependencies) => object2(dependencies) ? Object.keys(dependencies) : []).filter((name, index, names) => names.indexOf(name) === index).sort(), discoverAbsoluteSyncSchema = (projectRoot) => {
|
|
1122
|
+
const appManifestPath = join13(resolve11(projectRoot), "package.json");
|
|
1123
|
+
const appManifest = manifestAt(appManifestPath);
|
|
1124
|
+
if (!appManifest)
|
|
1125
|
+
return {
|
|
1126
|
+
components: [
|
|
1127
|
+
{
|
|
1128
|
+
id: "@absolutejs/app",
|
|
1129
|
+
minimumCompatibleVersion: 1,
|
|
1130
|
+
version: 1
|
|
1131
|
+
}
|
|
1132
|
+
],
|
|
1133
|
+
sources: []
|
|
1134
|
+
};
|
|
1135
|
+
const appMetadata = localSchemaMetadata(appManifest);
|
|
1136
|
+
const components = [
|
|
1137
|
+
appMetadata === undefined ? { id: "@absolutejs/app", minimumCompatibleVersion: 1, version: 1 } : component("@absolutejs/app", appMetadata)
|
|
1138
|
+
];
|
|
1139
|
+
const sources = [
|
|
1140
|
+
{ id: "@absolutejs/app", manifestPath: appManifestPath }
|
|
1161
1141
|
];
|
|
1142
|
+
for (const name of dependencyNames(appManifest)) {
|
|
1143
|
+
const manifestPath = packageManifestPath(projectRoot, name);
|
|
1144
|
+
if (!manifestPath)
|
|
1145
|
+
continue;
|
|
1146
|
+
const manifest = manifestAt(manifestPath);
|
|
1147
|
+
if (!manifest)
|
|
1148
|
+
continue;
|
|
1149
|
+
const metadata = localSchemaMetadata(manifest);
|
|
1150
|
+
if (metadata === undefined)
|
|
1151
|
+
continue;
|
|
1152
|
+
components.push(component(name, metadata));
|
|
1153
|
+
sources.push({ id: name, manifestPath });
|
|
1154
|
+
}
|
|
1155
|
+
components.sort((left, right) => left.id.localeCompare(right.id));
|
|
1156
|
+
sources.sort((left, right) => left.id.localeCompare(right.id));
|
|
1157
|
+
resolveSyncLocalSchemaComponents({}, { components });
|
|
1158
|
+
return { components, sources };
|
|
1159
|
+
};
|
|
1160
|
+
var init_syncSchema = __esm(() => {
|
|
1161
|
+
init_client();
|
|
1162
1162
|
});
|
|
1163
1163
|
|
|
1164
1164
|
// src/cli/scripts/telemetry.ts
|
|
1165
1165
|
import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync5, writeFileSync } from "fs";
|
|
1166
1166
|
import { homedir as homedir3 } from "os";
|
|
1167
|
-
import { join as
|
|
1167
|
+
import { join as join18 } from "path";
|
|
1168
1168
|
var configDir, configPath, getTelemetryConfig = () => {
|
|
1169
1169
|
try {
|
|
1170
1170
|
if (!existsSync5(configPath))
|
|
@@ -1177,14 +1177,14 @@ var configDir, configPath, getTelemetryConfig = () => {
|
|
|
1177
1177
|
}
|
|
1178
1178
|
};
|
|
1179
1179
|
var init_telemetry = __esm(() => {
|
|
1180
|
-
configDir =
|
|
1181
|
-
configPath =
|
|
1180
|
+
configDir = join18(homedir3(), ".absolutejs");
|
|
1181
|
+
configPath = join18(configDir, "telemetry.json");
|
|
1182
1182
|
});
|
|
1183
1183
|
|
|
1184
1184
|
// src/cli/telemetryEvent.ts
|
|
1185
1185
|
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
1186
|
-
import { arch, platform as
|
|
1187
|
-
import { dirname as
|
|
1186
|
+
import { arch, platform as platform3 } from "os";
|
|
1187
|
+
import { dirname as dirname13, join as join19, parse } from "path";
|
|
1188
1188
|
var checkCandidate = (candidate) => {
|
|
1189
1189
|
if (!existsSync6(candidate)) {
|
|
1190
1190
|
return null;
|
|
@@ -1204,12 +1204,12 @@ var checkCandidate = (candidate) => {
|
|
|
1204
1204
|
}, findPackageVersion = () => {
|
|
1205
1205
|
let { dir } = import.meta;
|
|
1206
1206
|
while (dir !== parse(dir).root) {
|
|
1207
|
-
const candidate =
|
|
1207
|
+
const candidate = join19(dir, "package.json");
|
|
1208
1208
|
const version = checkCandidate(candidate);
|
|
1209
1209
|
if (version) {
|
|
1210
1210
|
return version;
|
|
1211
1211
|
}
|
|
1212
|
-
dir =
|
|
1212
|
+
dir = dirname13(dir);
|
|
1213
1213
|
}
|
|
1214
1214
|
return "unknown";
|
|
1215
1215
|
}, sendTelemetryEvent = (event, payload) => {
|
|
@@ -1224,7 +1224,7 @@ var checkCandidate = (candidate) => {
|
|
|
1224
1224
|
arch: arch(),
|
|
1225
1225
|
bunVersion: Bun.version,
|
|
1226
1226
|
event,
|
|
1227
|
-
os:
|
|
1227
|
+
os: platform3(),
|
|
1228
1228
|
payload,
|
|
1229
1229
|
timestamp: new Date().toISOString(),
|
|
1230
1230
|
version: getVersion()
|
|
@@ -8024,28 +8024,194 @@ var readAbsoluteMobileMaterializedReleases = async (root) => {
|
|
|
8024
8024
|
}
|
|
8025
8025
|
};
|
|
8026
8026
|
|
|
8027
|
-
// src/mobile/buildPipeline.ts
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8027
|
+
// src/mobile/buildPipeline.ts
|
|
8028
|
+
init_deviceCapabilities();
|
|
8029
|
+
|
|
8030
|
+
// src/mobile/expoProject.ts
|
|
8031
|
+
init_nativeAuth();
|
|
8032
|
+
init_syncSchema();
|
|
8033
|
+
init_deviceCapabilities();
|
|
8034
|
+
import {
|
|
8035
|
+
access as access9,
|
|
8036
|
+
cp as cp2,
|
|
8037
|
+
mkdir as mkdir11,
|
|
8038
|
+
mkdtemp as mkdtemp7,
|
|
8039
|
+
readdir as readdir4,
|
|
8040
|
+
readFile as readFile14,
|
|
8041
|
+
rename as rename11,
|
|
8042
|
+
rm as rm10,
|
|
8043
|
+
writeFile as writeFile12
|
|
8044
|
+
} from "fs/promises";
|
|
8045
|
+
import { createHash as createHash11 } from "crypto";
|
|
8046
|
+
import { basename as basename4, dirname as dirname10, join as join14, relative as relative10, resolve as resolve12, sep as sep6 } from "path";
|
|
8047
|
+
|
|
8048
|
+
// src/mobile/updateRuntime.ts
|
|
8049
|
+
init_deviceCapabilities();
|
|
8050
|
+
init_nativeAuth();
|
|
8051
|
+
import { createHash as createHash10 } from "crypto";
|
|
8052
|
+
|
|
8053
|
+
// src/mobile/updateProtocol.ts
|
|
8054
|
+
var ABSOLUTE_MOBILE_UPDATE_FORMAT = 1;
|
|
8055
|
+
var ABSOLUTE_MOBILE_UPDATE_MAX_FILE_BYTES = 32 * 1024 * 1024;
|
|
8056
|
+
var ABSOLUTE_MOBILE_UPDATE_MAX_FILES = 1e4;
|
|
8057
|
+
var ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
8058
|
+
var ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM = "ecdsa-p256-sha256";
|
|
8059
|
+
var HASH_PATTERN = /^[a-f0-9]{64}$/u;
|
|
8060
|
+
var RELEASE_PATTERN = /^amu_[a-f0-9]{64}$/u;
|
|
8061
|
+
var KEY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
8062
|
+
var CHANNEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
8063
|
+
var isClassification = (value) => value === "bug-fix" || value === "content" || value === "security";
|
|
8064
|
+
var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8065
|
+
var canonicalValue = (value) => {
|
|
8066
|
+
if (Array.isArray(value))
|
|
8067
|
+
return value.map(canonicalValue);
|
|
8068
|
+
if (!object3(value))
|
|
8069
|
+
return value;
|
|
8070
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonicalValue(value[key])]));
|
|
8071
|
+
};
|
|
8072
|
+
var absoluteMobileUpdateSigningPayload = (manifest) => new TextEncoder().encode(canonicalizeAbsoluteMobileUpdate(manifest));
|
|
8073
|
+
var canonicalizeAbsoluteMobileUpdate = (value) => JSON.stringify(canonicalValue(value));
|
|
8074
|
+
var requireText2 = (value, field2) => {
|
|
8075
|
+
if (typeof value !== "string" || value.length === 0)
|
|
8076
|
+
throw new TypeError(`${field2} must be a non-empty string.`);
|
|
8077
|
+
return value;
|
|
8078
|
+
};
|
|
8079
|
+
var normalizeAbsoluteMobileUpdatePath = (value) => {
|
|
8080
|
+
const path = requireText2(value, "Update file path").replaceAll("\\", "/");
|
|
8081
|
+
if (path.startsWith("/") || path.includes("\x00") || path.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
|
|
8082
|
+
throw new TypeError("Update file paths must be normalized relative paths.");
|
|
8083
|
+
}
|
|
8084
|
+
return path;
|
|
8085
|
+
};
|
|
8086
|
+
var parseFile = (value) => {
|
|
8087
|
+
if (!object3(value))
|
|
8088
|
+
throw new TypeError("Update files must be objects.");
|
|
8089
|
+
const path = normalizeAbsoluteMobileUpdatePath(value.path);
|
|
8090
|
+
if (typeof value.bytes !== "number" || !Number.isSafeInteger(value.bytes) || value.bytes < 0 || value.bytes > ABSOLUTE_MOBILE_UPDATE_MAX_FILE_BYTES) {
|
|
8091
|
+
throw new TypeError(`Update file ${path} has an invalid byte length.`);
|
|
8092
|
+
}
|
|
8093
|
+
if (typeof value.sha256 !== "string" || !HASH_PATTERN.test(value.sha256))
|
|
8094
|
+
throw new TypeError(`Update file ${path} has an invalid SHA-256 digest.`);
|
|
8095
|
+
return { bytes: value.bytes, path, sha256: value.sha256 };
|
|
8096
|
+
};
|
|
8097
|
+
var parseAbsoluteMobileUnsignedUpdateManifest = (value) => {
|
|
8098
|
+
if (!object3(value) || value.format !== ABSOLUTE_MOBILE_UPDATE_FORMAT)
|
|
8099
|
+
throw new TypeError("Invalid AbsoluteJS mobile update manifest.");
|
|
8100
|
+
const appId = requireText2(value.appId, "Update appId");
|
|
8101
|
+
const channel = requireText2(value.channel, "Update channel");
|
|
8102
|
+
if (!CHANNEL_PATTERN.test(channel))
|
|
8103
|
+
throw new TypeError("Update channel contains unsupported characters.");
|
|
8104
|
+
if (!isClassification(value.classification))
|
|
8105
|
+
throw new TypeError("Update classification is invalid.");
|
|
8106
|
+
const createdAt = requireText2(value.createdAt, "Update createdAt");
|
|
8107
|
+
if (!Number.isFinite(Date.parse(createdAt)) || new Date(createdAt).toISOString() !== createdAt)
|
|
8108
|
+
throw new TypeError("Update createdAt must be a canonical ISO timestamp.");
|
|
8109
|
+
if (!Array.isArray(value.files) || value.files.length === 0)
|
|
8110
|
+
throw new TypeError("An update must contain at least one file.");
|
|
8111
|
+
if (value.files.length > ABSOLUTE_MOBILE_UPDATE_MAX_FILES)
|
|
8112
|
+
throw new TypeError("Update contains too many files.");
|
|
8113
|
+
const files = value.files.map(parseFile);
|
|
8114
|
+
const sorted = [...files].sort((left, right) => left.path.localeCompare(right.path));
|
|
8115
|
+
if (files.some((file, index) => file.path !== sorted[index]?.path))
|
|
8116
|
+
throw new TypeError("Update files must be sorted by path.");
|
|
8117
|
+
if (new Set(files.map(({ path }) => path)).size !== files.length)
|
|
8118
|
+
throw new TypeError("Update file paths must be unique.");
|
|
8119
|
+
if (files.reduce((total, file) => total + file.bytes, 0) > ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES)
|
|
8120
|
+
throw new TypeError("Update exceeds the maximum uncompressed size.");
|
|
8121
|
+
if (typeof value.releaseId !== "string" || !RELEASE_PATTERN.test(value.releaseId))
|
|
8122
|
+
throw new TypeError("Update releaseId is invalid.");
|
|
8123
|
+
if (typeof value.runtimeFingerprint !== "string" || !HASH_PATTERN.test(value.runtimeFingerprint))
|
|
8124
|
+
throw new TypeError("Update runtime fingerprint is invalid.");
|
|
8125
|
+
if (value.withinSubmittedPurpose !== true)
|
|
8126
|
+
throw new TypeError("OTA updates must attest that they remain within the submitted app purpose.");
|
|
8127
|
+
return {
|
|
8128
|
+
appId,
|
|
8129
|
+
channel,
|
|
8130
|
+
classification: value.classification,
|
|
8131
|
+
createdAt,
|
|
8132
|
+
files,
|
|
8133
|
+
format: ABSOLUTE_MOBILE_UPDATE_FORMAT,
|
|
8134
|
+
releaseId: value.releaseId,
|
|
8135
|
+
runtimeFingerprint: value.runtimeFingerprint,
|
|
8136
|
+
withinSubmittedPurpose: true
|
|
8137
|
+
};
|
|
8138
|
+
};
|
|
8139
|
+
var parseAbsoluteMobileUpdateManifest = (value) => {
|
|
8140
|
+
if (!object3(value))
|
|
8141
|
+
throw new TypeError("Invalid AbsoluteJS mobile update manifest.");
|
|
8142
|
+
const { signature: signatureValue, ...unsignedValue } = value;
|
|
8143
|
+
const unsigned = parseAbsoluteMobileUnsignedUpdateManifest(unsignedValue);
|
|
8144
|
+
if (!object3(signatureValue))
|
|
8145
|
+
throw new TypeError("Update signature is missing.");
|
|
8146
|
+
if (signatureValue.algorithm !== ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM)
|
|
8147
|
+
throw new TypeError("Update signature algorithm is unsupported.");
|
|
8148
|
+
const keyId = requireText2(signatureValue.keyId, "Update signature keyId");
|
|
8149
|
+
if (!KEY_ID_PATTERN.test(keyId))
|
|
8150
|
+
throw new TypeError("Update signature keyId is invalid.");
|
|
8151
|
+
const signature = requireText2(signatureValue.value, "Update signature value");
|
|
8152
|
+
let signatureBytes;
|
|
8153
|
+
try {
|
|
8154
|
+
signatureBytes = Uint8Array.from(atob(signature), (character) => character.charCodeAt(0));
|
|
8155
|
+
} catch {
|
|
8156
|
+
signatureBytes = new Uint8Array;
|
|
8157
|
+
}
|
|
8158
|
+
if (!/^[A-Za-z0-9+/]+={0,2}$/u.test(signature) || signatureBytes.byteLength !== 64 || btoa(String.fromCharCode(...signatureBytes)) !== signature)
|
|
8159
|
+
throw new TypeError("Update signature is not canonical base64.");
|
|
8160
|
+
return {
|
|
8161
|
+
...unsigned,
|
|
8162
|
+
signature: {
|
|
8163
|
+
algorithm: ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM,
|
|
8164
|
+
keyId,
|
|
8165
|
+
value: signature
|
|
8166
|
+
}
|
|
8167
|
+
};
|
|
8168
|
+
};
|
|
8169
|
+
var unsignedAbsoluteMobileUpdate = (manifest) => {
|
|
8170
|
+
const { signature: _signature, ...unsigned } = manifest;
|
|
8171
|
+
return unsigned;
|
|
8172
|
+
};
|
|
8173
|
+
|
|
8174
|
+
// src/mobile/updateRuntime.ts
|
|
8175
|
+
init_syncSchema();
|
|
8176
|
+
var ABSOLUTE_MOBILE_SHELL_ABI = 2;
|
|
8177
|
+
var ABSOLUTE_MOBILE_UPDATE_RUNTIME_FORMAT = 1;
|
|
8178
|
+
var createAbsoluteMobileUpdateRuntimeDescriptor = (options) => ({
|
|
8179
|
+
appId: options.config.appId,
|
|
8180
|
+
auth: options.auth ?? null,
|
|
8181
|
+
deepLinks: {
|
|
8182
|
+
hosts: options.config.deepLinkHosts,
|
|
8183
|
+
...options.config.deepLinkScheme ? { scheme: options.config.deepLinkScheme } : {}
|
|
8184
|
+
},
|
|
8185
|
+
deviceCapabilities: options.deviceCapabilities,
|
|
8186
|
+
engine: options.config.engine,
|
|
8187
|
+
format: ABSOLUTE_MOBILE_UPDATE_RUNTIME_FORMAT,
|
|
8188
|
+
shellAbi: ABSOLUTE_MOBILE_SHELL_ABI,
|
|
8189
|
+
syncSchema: options.syncSchema ?? null,
|
|
8190
|
+
updates: options.config.updates ?? null
|
|
8191
|
+
});
|
|
8192
|
+
var fingerprintAbsoluteMobileUpdateRuntime = (descriptor) => createHash10("sha256").update(canonicalizeAbsoluteMobileUpdate(descriptor)).digest("hex");
|
|
8193
|
+
var resolveAbsoluteMobileUpdateRuntime = (config, projectRoot) => {
|
|
8194
|
+
const auth = resolveAbsoluteMobileAuthManifest(projectRoot, config);
|
|
8195
|
+
const sync = auth !== undefined && projectUsesAbsoluteSync(projectRoot);
|
|
8196
|
+
const syncSchema = sync ? discoverAbsoluteSyncSchema(projectRoot) : undefined;
|
|
8197
|
+
const deviceCapabilities = resolveAbsoluteDeviceCapabilityPlan(projectRoot, config.engine);
|
|
8198
|
+
const descriptor = createAbsoluteMobileUpdateRuntimeDescriptor({
|
|
8199
|
+
...auth ? { auth } : {},
|
|
8200
|
+
config,
|
|
8201
|
+
deviceCapabilities,
|
|
8202
|
+
...syncSchema ? { syncSchema } : {}
|
|
8203
|
+
});
|
|
8204
|
+
return {
|
|
8205
|
+
auth,
|
|
8206
|
+
descriptor,
|
|
8207
|
+
deviceCapabilities,
|
|
8208
|
+
fingerprint: fingerprintAbsoluteMobileUpdateRuntime(descriptor),
|
|
8209
|
+
sync,
|
|
8210
|
+
syncSchema
|
|
8211
|
+
};
|
|
8212
|
+
};
|
|
8213
|
+
|
|
8032
8214
|
// src/mobile/expoProject.ts
|
|
8033
|
-
init_nativeAuth();
|
|
8034
|
-
init_syncSchema();
|
|
8035
|
-
init_deviceCapabilities();
|
|
8036
|
-
import {
|
|
8037
|
-
access as access9,
|
|
8038
|
-
cp as cp2,
|
|
8039
|
-
mkdir as mkdir11,
|
|
8040
|
-
mkdtemp as mkdtemp7,
|
|
8041
|
-
readdir as readdir4,
|
|
8042
|
-
readFile as readFile14,
|
|
8043
|
-
rename as rename11,
|
|
8044
|
-
rm as rm10,
|
|
8045
|
-
writeFile as writeFile12
|
|
8046
|
-
} from "fs/promises";
|
|
8047
|
-
import { createHash as createHash10 } from "crypto";
|
|
8048
|
-
import { basename as basename4, dirname as dirname10, join as join14, relative as relative10, resolve as resolve12, sep as sep6 } from "path";
|
|
8049
8215
|
var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
|
|
8050
8216
|
`;
|
|
8051
8217
|
var EXPO_ASSET_EXTENSION = ".absasset";
|
|
@@ -8122,7 +8288,7 @@ var packageDependencies = (plan) => Object.fromEntries(plan.requiredPackages.map
|
|
|
8122
8288
|
const separator = spec.lastIndexOf("@");
|
|
8123
8289
|
return [spec.slice(0, separator), spec.slice(separator + 1)];
|
|
8124
8290
|
}));
|
|
8125
|
-
var expoPackage = (auth, sync, devices) => ({
|
|
8291
|
+
var expoPackage = (auth, sync, devices, updates) => ({
|
|
8126
8292
|
dependencies: {
|
|
8127
8293
|
"@absolutejs/devices": "0.7.0",
|
|
8128
8294
|
"@absolutejs/devices-expo": "0.0.2",
|
|
@@ -8137,6 +8303,7 @@ var expoPackage = (auth, sync, devices) => ({
|
|
|
8137
8303
|
expo: "~57.0.9",
|
|
8138
8304
|
"expo-asset": "~57.0.15",
|
|
8139
8305
|
"expo-constants": "~57.0.16",
|
|
8306
|
+
...updates ? { "expo-crypto": "~57.0.1" } : {},
|
|
8140
8307
|
"expo-dev-client": "~57.0.16",
|
|
8141
8308
|
"expo-file-system": "~57.0.6",
|
|
8142
8309
|
"expo-haptics": "~57.0.2",
|
|
@@ -8145,13 +8312,13 @@ var expoPackage = (auth, sync, devices) => ({
|
|
|
8145
8312
|
"expo-background-task": "~57.0.14",
|
|
8146
8313
|
"expo-network": "~57.0.1",
|
|
8147
8314
|
"expo-sqlite": "~57.0.2",
|
|
8148
|
-
"expo-task-manager": "~57.0.14"
|
|
8149
|
-
"expo-updates": "~57.0.19"
|
|
8315
|
+
"expo-task-manager": "~57.0.14"
|
|
8150
8316
|
} : {},
|
|
8317
|
+
...updates || sync ? { "expo-updates": "~57.0.19" } : {},
|
|
8151
8318
|
"expo-router": "~57.0.17",
|
|
8152
|
-
...auth ? {
|
|
8319
|
+
...auth || updates ? {
|
|
8153
8320
|
"expo-secure-store": "~57.0.2",
|
|
8154
|
-
"expo-web-browser": "~57.0.2"
|
|
8321
|
+
...auth ? { "expo-web-browser": "~57.0.2" } : {}
|
|
8155
8322
|
} : {},
|
|
8156
8323
|
react: "19.2.3",
|
|
8157
8324
|
"react-native": "0.86.3",
|
|
@@ -8174,7 +8341,7 @@ var expoPackage = (auth, sync, devices) => ({
|
|
|
8174
8341
|
},
|
|
8175
8342
|
version: "0.0.0"
|
|
8176
8343
|
});
|
|
8177
|
-
var expoAppConfig = (config, auth, sync, devices) => {
|
|
8344
|
+
var expoAppConfig = (config, auth, sync, devices, runtimeFingerprint) => {
|
|
8178
8345
|
const requirements = absoluteDeviceNativeRequirements(devices);
|
|
8179
8346
|
const usageKey = (purpose) => {
|
|
8180
8347
|
if (purpose === "camera")
|
|
@@ -8260,7 +8427,7 @@ var expoAppConfig = (config, auth, sync, devices) => {
|
|
|
8260
8427
|
plugins: [
|
|
8261
8428
|
"expo-router",
|
|
8262
8429
|
["expo-dev-client", { launchMode: "most-recent" }],
|
|
8263
|
-
...auth ? ["expo-secure-store"] : [],
|
|
8430
|
+
...auth || config.updates ? ["expo-secure-store"] : [],
|
|
8264
8431
|
...sync ? [
|
|
8265
8432
|
"expo-sqlite",
|
|
8266
8433
|
"expo-background-task",
|
|
@@ -8268,9 +8435,20 @@ var expoAppConfig = (config, auth, sync, devices) => {
|
|
|
8268
8435
|
] : [],
|
|
8269
8436
|
...configuredPlugins
|
|
8270
8437
|
],
|
|
8271
|
-
runtimeVersion:
|
|
8438
|
+
runtimeVersion: runtimeFingerprint,
|
|
8272
8439
|
scheme: config.deepLinkScheme,
|
|
8273
8440
|
slug: config.appId.toLowerCase().replaceAll(".", "-"),
|
|
8441
|
+
...config.updates ? {
|
|
8442
|
+
updates: {
|
|
8443
|
+
checkAutomatically: "NEVER",
|
|
8444
|
+
fallbackToCacheTimeout: config.updates.bootTimeoutMs,
|
|
8445
|
+
requestHeaders: {
|
|
8446
|
+
"x-absolute-mobile-app": config.appId,
|
|
8447
|
+
"x-absolute-mobile-channel": config.updates.channel
|
|
8448
|
+
},
|
|
8449
|
+
url: config.updates.manifestUrl
|
|
8450
|
+
}
|
|
8451
|
+
} : {},
|
|
8274
8452
|
version: config.iosVersion ?? "0.1.0"
|
|
8275
8453
|
}
|
|
8276
8454
|
};
|
|
@@ -8341,13 +8519,16 @@ config.watchFolders = [appRoot];
|
|
|
8341
8519
|
|
|
8342
8520
|
module.exports = config;
|
|
8343
8521
|
`;
|
|
8344
|
-
var layoutSource = (auth, sync) => `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
|
|
8522
|
+
var layoutSource = (auth, sync, updates) => `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
|
|
8345
8523
|
import '../src/generated/AbsoluteDevices';
|
|
8346
8524
|
${auth ? `import { useEffect, useState } from 'react';
|
|
8347
8525
|
import { startAbsoluteExpoAuth } from '../src/generated/AbsoluteAuth';` : ""}
|
|
8348
8526
|
${sync ? "import { startAbsoluteExpoSync } from '../src/generated/AbsoluteSync';" : ""}
|
|
8527
|
+
${updates ? `import { useEffect as useUpdateEffect } from 'react';
|
|
8528
|
+
import { startAbsoluteExpoUpdates } from '../src/generated/AbsoluteUpdates';` : ""}
|
|
8349
8529
|
|
|
8350
8530
|
export default function AbsoluteLayout() {
|
|
8531
|
+
${updates ? "useUpdateEffect(() => { void startAbsoluteExpoUpdates(); }, []);" : ""}
|
|
8351
8532
|
${auth ? `const [ready, setReady] = useState(false);
|
|
8352
8533
|
useEffect(() => {
|
|
8353
8534
|
let active = true;
|
|
@@ -8358,6 +8539,40 @@ export default function AbsoluteLayout() {
|
|
|
8358
8539
|
return <Stack screenOptions={{ headerShown: false }} />;
|
|
8359
8540
|
}
|
|
8360
8541
|
`;
|
|
8542
|
+
var updatesRuntimeSource = (config) => `${EXPO_GENERATED_HEADER}import { randomUUID } from 'expo-crypto';
|
|
8543
|
+
import * as SecureStore from 'expo-secure-store';
|
|
8544
|
+
import * as Updates from 'expo-updates';
|
|
8545
|
+
|
|
8546
|
+
const INSTALLATION_KEY = 'absolutejs.mobile.update.installation.v1';
|
|
8547
|
+
const REQUEST_HEADERS = ${JSON.stringify({
|
|
8548
|
+
"x-absolute-mobile-app": config.appId,
|
|
8549
|
+
"x-absolute-mobile-channel": config.updates?.channel
|
|
8550
|
+
})};
|
|
8551
|
+
|
|
8552
|
+
let startPromise: Promise<void> | undefined;
|
|
8553
|
+
export const startAbsoluteExpoUpdates = () => {
|
|
8554
|
+
startPromise ??= (async () => {
|
|
8555
|
+
if (!Updates.isEnabled || __DEV__) return;
|
|
8556
|
+
let installationId = await SecureStore.getItemAsync(INSTALLATION_KEY);
|
|
8557
|
+
if (!installationId) {
|
|
8558
|
+
installationId = randomUUID();
|
|
8559
|
+
await SecureStore.setItemAsync(INSTALLATION_KEY, installationId);
|
|
8560
|
+
}
|
|
8561
|
+
await Updates.setUpdateRequestHeadersOverride({
|
|
8562
|
+
...REQUEST_HEADERS,
|
|
8563
|
+
'x-absolute-mobile-installation': installationId
|
|
8564
|
+
});
|
|
8565
|
+
const result = await Updates.checkForUpdateAsync();
|
|
8566
|
+
if (result.isAvailable || result.isRollBackToEmbedded) {
|
|
8567
|
+
await Updates.fetchUpdateAsync();
|
|
8568
|
+
}
|
|
8569
|
+
})().catch(error => {
|
|
8570
|
+
console.warn('[absolute:mobile-update] Expo update check failed', error instanceof Error ? error.message : 'unknown error');
|
|
8571
|
+
});
|
|
8572
|
+
|
|
8573
|
+
return startPromise;
|
|
8574
|
+
};
|
|
8575
|
+
`;
|
|
8361
8576
|
var nativeDiagnosticSource = `${EXPO_GENERATED_HEADER}import * as Haptics from 'expo-haptics';
|
|
8362
8577
|
import { Link } from 'expo-router';
|
|
8363
8578
|
import { Pressable, SafeAreaView, StyleSheet, Text, View } from 'react-native';
|
|
@@ -8460,7 +8675,7 @@ ${push ? `absoluteExpoAuth.onPrincipalChange(principal => {
|
|
|
8460
8675
|
`;
|
|
8461
8676
|
};
|
|
8462
8677
|
var authRuntimeSource = (auth, appId) => {
|
|
8463
|
-
const storageIdentity =
|
|
8678
|
+
const storageIdentity = createHash11("sha256").update(appId).digest("hex").slice(0, 24);
|
|
8464
8679
|
return `${EXPO_GENERATED_HEADER}import { createAbsoluteExpoAuthClient } from '@absolutejs/auth-expo';
|
|
8465
8680
|
import { createMobileAuthTransport, installAuthClientRuntimeTransport } from '@absolutejs/auth/client/mobile';
|
|
8466
8681
|
|
|
@@ -8492,7 +8707,7 @@ export const startAbsoluteExpoAuth = () => {
|
|
|
8492
8707
|
`;
|
|
8493
8708
|
};
|
|
8494
8709
|
var syncRuntimeSource = (config, schema) => {
|
|
8495
|
-
const storageIdentity =
|
|
8710
|
+
const storageIdentity = createHash11("sha256").update(config.appId).digest("hex").slice(0, 24);
|
|
8496
8711
|
const backgroundEndpoint = new URL("/__absolute/sync/background", config.productionOrigin).href;
|
|
8497
8712
|
const backgroundTask = `${config.appId}.absolutejs.sync`;
|
|
8498
8713
|
return `${EXPO_GENERATED_HEADER}import { runHeadlessSync } from '@absolutejs/sync/client/headless';
|
|
@@ -9130,6 +9345,7 @@ var writeAbsoluteExpoProject = async (config, options) => {
|
|
|
9130
9345
|
const devices = resolveAbsoluteDeviceCapabilityPlan(projectRoot, "expo");
|
|
9131
9346
|
const syncEnabled = Boolean(auth && projectUsesAbsoluteSync(projectRoot));
|
|
9132
9347
|
const syncSchema = syncEnabled ? { components: discoverAbsoluteSyncSchema(projectRoot).components } : undefined;
|
|
9348
|
+
const runtimeFingerprint = resolveAbsoluteMobileUpdateRuntime(config, projectRoot).fingerprint;
|
|
9133
9349
|
const routeModules = Object.entries(config.expoNativeRoutes);
|
|
9134
9350
|
const moduleChecks = await Promise.all(routeModules.map(async ([route, module]) => ({
|
|
9135
9351
|
exists: await exists3(module),
|
|
@@ -9162,12 +9378,12 @@ node_modules/
|
|
|
9162
9378
|
],
|
|
9163
9379
|
[
|
|
9164
9380
|
join14(project, "app.json"),
|
|
9165
|
-
jsonSource(expoAppConfig(config, authEnabled, syncEnabled, devices))
|
|
9381
|
+
jsonSource(expoAppConfig(config, authEnabled, syncEnabled, devices, runtimeFingerprint))
|
|
9166
9382
|
],
|
|
9167
9383
|
[join14(project, "app.config.js"), expoDynamicAppConfig],
|
|
9168
9384
|
[
|
|
9169
9385
|
join14(project, "package.json"),
|
|
9170
|
-
jsonSource(expoPackage(authEnabled, syncEnabled, devices))
|
|
9386
|
+
jsonSource(expoPackage(authEnabled, syncEnabled, devices, config.updates !== undefined))
|
|
9171
9387
|
],
|
|
9172
9388
|
[join14(project, "metro.config.js"), metroConfig(projectRoot)],
|
|
9173
9389
|
[
|
|
@@ -9180,7 +9396,7 @@ node_modules/
|
|
|
9180
9396
|
],
|
|
9181
9397
|
[
|
|
9182
9398
|
join14(project, "app", "_layout.tsx"),
|
|
9183
|
-
layoutSource(authEnabled, syncEnabled)
|
|
9399
|
+
layoutSource(authEnabled, syncEnabled, config.updates !== undefined)
|
|
9184
9400
|
],
|
|
9185
9401
|
[
|
|
9186
9402
|
join14(project, "app", "__absolute", "native", "index.tsx"),
|
|
@@ -9199,6 +9415,9 @@ node_modules/
|
|
|
9199
9415
|
webHostSource(config, auth, syncEnabled)
|
|
9200
9416
|
]
|
|
9201
9417
|
]);
|
|
9418
|
+
if (config.updates) {
|
|
9419
|
+
files.set(join14(project, "src", "generated", "AbsoluteUpdates.ts"), updatesRuntimeSource(config));
|
|
9420
|
+
}
|
|
9202
9421
|
if (auth) {
|
|
9203
9422
|
files.set(join14(project, "src", "generated", "AbsoluteAuth.ts"), authRuntimeSource(auth, config.appId));
|
|
9204
9423
|
}
|
|
@@ -9302,7 +9521,7 @@ var syncAbsoluteExpoWebAssets = async (config) => {
|
|
|
9302
9521
|
const nativeDataManifest = expoNativeDataManifest(manifest);
|
|
9303
9522
|
const { appBuild } = nativeDataManifest;
|
|
9304
9523
|
const files = await walkFiles(config.bundleDirectory);
|
|
9305
|
-
const bundleHash =
|
|
9524
|
+
const bundleHash = createHash11("sha256");
|
|
9306
9525
|
const filesWithContents = await Promise.all(files.map(async (file) => ({ contents: await readFile14(file), file })));
|
|
9307
9526
|
filesWithContents.forEach(({ contents, file }) => {
|
|
9308
9527
|
bundleHash.update(relative10(config.bundleDirectory, file).replaceAll("\\", "/"));
|
|
@@ -9334,149 +9553,6 @@ var syncAbsoluteExpoWebAssets = async (config) => {
|
|
|
9334
9553
|
return { appBuild, assets: assets.length, bundleId, path: destination };
|
|
9335
9554
|
};
|
|
9336
9555
|
|
|
9337
|
-
// src/mobile/updateRuntime.ts
|
|
9338
|
-
import { createHash as createHash11 } from "crypto";
|
|
9339
|
-
|
|
9340
|
-
// src/mobile/updateProtocol.ts
|
|
9341
|
-
var ABSOLUTE_MOBILE_UPDATE_FORMAT = 1;
|
|
9342
|
-
var ABSOLUTE_MOBILE_UPDATE_MAX_FILE_BYTES = 32 * 1024 * 1024;
|
|
9343
|
-
var ABSOLUTE_MOBILE_UPDATE_MAX_FILES = 1e4;
|
|
9344
|
-
var ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
9345
|
-
var ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM = "ecdsa-p256-sha256";
|
|
9346
|
-
var HASH_PATTERN = /^[a-f0-9]{64}$/u;
|
|
9347
|
-
var RELEASE_PATTERN = /^amu_[a-f0-9]{64}$/u;
|
|
9348
|
-
var KEY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
9349
|
-
var CHANNEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
9350
|
-
var isClassification = (value) => value === "bug-fix" || value === "content" || value === "security";
|
|
9351
|
-
var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9352
|
-
var canonicalValue = (value) => {
|
|
9353
|
-
if (Array.isArray(value))
|
|
9354
|
-
return value.map(canonicalValue);
|
|
9355
|
-
if (!object3(value))
|
|
9356
|
-
return value;
|
|
9357
|
-
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonicalValue(value[key])]));
|
|
9358
|
-
};
|
|
9359
|
-
var absoluteMobileUpdateSigningPayload = (manifest) => new TextEncoder().encode(canonicalizeAbsoluteMobileUpdate(manifest));
|
|
9360
|
-
var canonicalizeAbsoluteMobileUpdate = (value) => JSON.stringify(canonicalValue(value));
|
|
9361
|
-
var requireText2 = (value, field2) => {
|
|
9362
|
-
if (typeof value !== "string" || value.length === 0)
|
|
9363
|
-
throw new TypeError(`${field2} must be a non-empty string.`);
|
|
9364
|
-
return value;
|
|
9365
|
-
};
|
|
9366
|
-
var normalizeAbsoluteMobileUpdatePath = (value) => {
|
|
9367
|
-
const path = requireText2(value, "Update file path").replaceAll("\\", "/");
|
|
9368
|
-
if (path.startsWith("/") || path.includes("\x00") || path.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
|
|
9369
|
-
throw new TypeError("Update file paths must be normalized relative paths.");
|
|
9370
|
-
}
|
|
9371
|
-
return path;
|
|
9372
|
-
};
|
|
9373
|
-
var parseFile = (value) => {
|
|
9374
|
-
if (!object3(value))
|
|
9375
|
-
throw new TypeError("Update files must be objects.");
|
|
9376
|
-
const path = normalizeAbsoluteMobileUpdatePath(value.path);
|
|
9377
|
-
if (typeof value.bytes !== "number" || !Number.isSafeInteger(value.bytes) || value.bytes < 0 || value.bytes > ABSOLUTE_MOBILE_UPDATE_MAX_FILE_BYTES) {
|
|
9378
|
-
throw new TypeError(`Update file ${path} has an invalid byte length.`);
|
|
9379
|
-
}
|
|
9380
|
-
if (typeof value.sha256 !== "string" || !HASH_PATTERN.test(value.sha256))
|
|
9381
|
-
throw new TypeError(`Update file ${path} has an invalid SHA-256 digest.`);
|
|
9382
|
-
return { bytes: value.bytes, path, sha256: value.sha256 };
|
|
9383
|
-
};
|
|
9384
|
-
var parseAbsoluteMobileUnsignedUpdateManifest = (value) => {
|
|
9385
|
-
if (!object3(value) || value.format !== ABSOLUTE_MOBILE_UPDATE_FORMAT)
|
|
9386
|
-
throw new TypeError("Invalid AbsoluteJS mobile update manifest.");
|
|
9387
|
-
const appId = requireText2(value.appId, "Update appId");
|
|
9388
|
-
const channel = requireText2(value.channel, "Update channel");
|
|
9389
|
-
if (!CHANNEL_PATTERN.test(channel))
|
|
9390
|
-
throw new TypeError("Update channel contains unsupported characters.");
|
|
9391
|
-
if (!isClassification(value.classification))
|
|
9392
|
-
throw new TypeError("Update classification is invalid.");
|
|
9393
|
-
const createdAt = requireText2(value.createdAt, "Update createdAt");
|
|
9394
|
-
if (!Number.isFinite(Date.parse(createdAt)) || new Date(createdAt).toISOString() !== createdAt)
|
|
9395
|
-
throw new TypeError("Update createdAt must be a canonical ISO timestamp.");
|
|
9396
|
-
if (!Array.isArray(value.files) || value.files.length === 0)
|
|
9397
|
-
throw new TypeError("An update must contain at least one file.");
|
|
9398
|
-
if (value.files.length > ABSOLUTE_MOBILE_UPDATE_MAX_FILES)
|
|
9399
|
-
throw new TypeError("Update contains too many files.");
|
|
9400
|
-
const files = value.files.map(parseFile);
|
|
9401
|
-
const sorted = [...files].sort((left, right) => left.path.localeCompare(right.path));
|
|
9402
|
-
if (files.some((file, index) => file.path !== sorted[index]?.path))
|
|
9403
|
-
throw new TypeError("Update files must be sorted by path.");
|
|
9404
|
-
if (new Set(files.map(({ path }) => path)).size !== files.length)
|
|
9405
|
-
throw new TypeError("Update file paths must be unique.");
|
|
9406
|
-
if (files.reduce((total, file) => total + file.bytes, 0) > ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES)
|
|
9407
|
-
throw new TypeError("Update exceeds the maximum uncompressed size.");
|
|
9408
|
-
if (typeof value.releaseId !== "string" || !RELEASE_PATTERN.test(value.releaseId))
|
|
9409
|
-
throw new TypeError("Update releaseId is invalid.");
|
|
9410
|
-
if (typeof value.runtimeFingerprint !== "string" || !HASH_PATTERN.test(value.runtimeFingerprint))
|
|
9411
|
-
throw new TypeError("Update runtime fingerprint is invalid.");
|
|
9412
|
-
if (value.withinSubmittedPurpose !== true)
|
|
9413
|
-
throw new TypeError("OTA updates must attest that they remain within the submitted app purpose.");
|
|
9414
|
-
return {
|
|
9415
|
-
appId,
|
|
9416
|
-
channel,
|
|
9417
|
-
classification: value.classification,
|
|
9418
|
-
createdAt,
|
|
9419
|
-
files,
|
|
9420
|
-
format: ABSOLUTE_MOBILE_UPDATE_FORMAT,
|
|
9421
|
-
releaseId: value.releaseId,
|
|
9422
|
-
runtimeFingerprint: value.runtimeFingerprint,
|
|
9423
|
-
withinSubmittedPurpose: true
|
|
9424
|
-
};
|
|
9425
|
-
};
|
|
9426
|
-
var parseAbsoluteMobileUpdateManifest = (value) => {
|
|
9427
|
-
if (!object3(value))
|
|
9428
|
-
throw new TypeError("Invalid AbsoluteJS mobile update manifest.");
|
|
9429
|
-
const { signature: signatureValue, ...unsignedValue } = value;
|
|
9430
|
-
const unsigned = parseAbsoluteMobileUnsignedUpdateManifest(unsignedValue);
|
|
9431
|
-
if (!object3(signatureValue))
|
|
9432
|
-
throw new TypeError("Update signature is missing.");
|
|
9433
|
-
if (signatureValue.algorithm !== ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM)
|
|
9434
|
-
throw new TypeError("Update signature algorithm is unsupported.");
|
|
9435
|
-
const keyId = requireText2(signatureValue.keyId, "Update signature keyId");
|
|
9436
|
-
if (!KEY_ID_PATTERN.test(keyId))
|
|
9437
|
-
throw new TypeError("Update signature keyId is invalid.");
|
|
9438
|
-
const signature = requireText2(signatureValue.value, "Update signature value");
|
|
9439
|
-
let signatureBytes;
|
|
9440
|
-
try {
|
|
9441
|
-
signatureBytes = Uint8Array.from(atob(signature), (character) => character.charCodeAt(0));
|
|
9442
|
-
} catch {
|
|
9443
|
-
signatureBytes = new Uint8Array;
|
|
9444
|
-
}
|
|
9445
|
-
if (!/^[A-Za-z0-9+/]+={0,2}$/u.test(signature) || signatureBytes.byteLength !== 64 || btoa(String.fromCharCode(...signatureBytes)) !== signature)
|
|
9446
|
-
throw new TypeError("Update signature is not canonical base64.");
|
|
9447
|
-
return {
|
|
9448
|
-
...unsigned,
|
|
9449
|
-
signature: {
|
|
9450
|
-
algorithm: ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM,
|
|
9451
|
-
keyId,
|
|
9452
|
-
value: signature
|
|
9453
|
-
}
|
|
9454
|
-
};
|
|
9455
|
-
};
|
|
9456
|
-
var unsignedAbsoluteMobileUpdate = (manifest) => {
|
|
9457
|
-
const { signature: _signature, ...unsigned } = manifest;
|
|
9458
|
-
return unsigned;
|
|
9459
|
-
};
|
|
9460
|
-
|
|
9461
|
-
// src/mobile/updateRuntime.ts
|
|
9462
|
-
var ABSOLUTE_MOBILE_SHELL_ABI = 2;
|
|
9463
|
-
var ABSOLUTE_MOBILE_UPDATE_RUNTIME_FORMAT = 1;
|
|
9464
|
-
var createAbsoluteMobileUpdateRuntimeDescriptor = (options) => ({
|
|
9465
|
-
appId: options.config.appId,
|
|
9466
|
-
auth: options.auth ?? null,
|
|
9467
|
-
deepLinks: {
|
|
9468
|
-
hosts: options.config.deepLinkHosts,
|
|
9469
|
-
...options.config.deepLinkScheme ? { scheme: options.config.deepLinkScheme } : {}
|
|
9470
|
-
},
|
|
9471
|
-
deviceCapabilities: options.deviceCapabilities,
|
|
9472
|
-
engine: options.config.engine,
|
|
9473
|
-
format: ABSOLUTE_MOBILE_UPDATE_RUNTIME_FORMAT,
|
|
9474
|
-
shellAbi: ABSOLUTE_MOBILE_SHELL_ABI,
|
|
9475
|
-
syncSchema: options.syncSchema ?? null,
|
|
9476
|
-
updates: options.config.updates ?? null
|
|
9477
|
-
});
|
|
9478
|
-
var fingerprintAbsoluteMobileUpdateRuntime = (descriptor) => createHash11("sha256").update(canonicalizeAbsoluteMobileUpdate(descriptor)).digest("hex");
|
|
9479
|
-
|
|
9480
9556
|
// src/mobile/buildPipeline.ts
|
|
9481
9557
|
var isElysiaApp = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "compile") === "function" && Array.isArray(Reflect.get(value, "routes"));
|
|
9482
9558
|
var isStringRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((entry) => typeof entry === "string");
|
|
@@ -9548,16 +9624,13 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
9548
9624
|
producerPath: resolve13(options.producerPath),
|
|
9549
9625
|
runtime: String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION)
|
|
9550
9626
|
});
|
|
9551
|
-
const
|
|
9552
|
-
|
|
9553
|
-
const syncSchema = sync ? discoverAbsoluteSyncSchema(options.projectRoot) : undefined;
|
|
9554
|
-
const deviceCapabilities = resolveAbsoluteDeviceCapabilityPlan(options.projectRoot, mobile.engine);
|
|
9555
|
-
const runtimeFingerprint = fingerprintAbsoluteMobileUpdateRuntime(createAbsoluteMobileUpdateRuntimeDescriptor({
|
|
9556
|
-
...auth ? { auth } : {},
|
|
9557
|
-
config: mobile,
|
|
9627
|
+
const {
|
|
9628
|
+
auth,
|
|
9558
9629
|
deviceCapabilities,
|
|
9559
|
-
|
|
9560
|
-
|
|
9630
|
+
fingerprint: runtimeFingerprint,
|
|
9631
|
+
sync,
|
|
9632
|
+
syncSchema
|
|
9633
|
+
} = resolveAbsoluteMobileUpdateRuntime(mobile, options.projectRoot);
|
|
9561
9634
|
const usesPush = deviceCapabilities.capabilities.includes("pushNotifications");
|
|
9562
9635
|
if (usesPush && !auth)
|
|
9563
9636
|
throw new TypeError("Portable push notifications require @absolutejs/auth so provider tokens can be registered without exposing identity controls to page code.");
|
|
@@ -10284,14 +10357,102 @@ var startAbsoluteExpoDevSession = async (options) => {
|
|
|
10284
10357
|
throw error;
|
|
10285
10358
|
}
|
|
10286
10359
|
};
|
|
10360
|
+
// src/mobile/expoUpdate.ts
|
|
10361
|
+
import { access as access11, mkdir as mkdir12, readFile as readFile16, writeFile as writeFile13 } from "fs/promises";
|
|
10362
|
+
import { dirname as dirname11, extname as extname4, join as join17 } from "path";
|
|
10363
|
+
var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
|
|
10364
|
+
var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
|
|
10365
|
+
var object4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10366
|
+
var safePath = (value, field2) => {
|
|
10367
|
+
if (typeof value !== "string" || value.length === 0)
|
|
10368
|
+
throw new TypeError(`Expo update ${field2} is invalid.`);
|
|
10369
|
+
const path = value.replaceAll("\\", "/");
|
|
10370
|
+
if (path.startsWith("/") || path.split("/").some((segment) => !segment || segment === "." || segment === ".."))
|
|
10371
|
+
throw new TypeError(`Expo update ${field2} must be a relative path.`);
|
|
10372
|
+
return path;
|
|
10373
|
+
};
|
|
10374
|
+
var extension = (value, path) => {
|
|
10375
|
+
if (value === null || value === undefined) {
|
|
10376
|
+
const inferred = extname4(path);
|
|
10377
|
+
return inferred ? inferred.slice(1) : undefined;
|
|
10378
|
+
}
|
|
10379
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9]+$/u.test(value))
|
|
10380
|
+
throw new TypeError("Expo update asset extension is invalid.");
|
|
10381
|
+
return value;
|
|
10382
|
+
};
|
|
10383
|
+
var asset = (value) => {
|
|
10384
|
+
if (!object4(value))
|
|
10385
|
+
throw new TypeError("Expo update asset is invalid.");
|
|
10386
|
+
const path = safePath(value.path, "asset path");
|
|
10387
|
+
const normalizedExtension = extension(value.extension ?? value.ext, path);
|
|
10388
|
+
return {
|
|
10389
|
+
...normalizedExtension ? { extension: normalizedExtension } : {},
|
|
10390
|
+
path
|
|
10391
|
+
};
|
|
10392
|
+
};
|
|
10393
|
+
var platform2 = (value) => {
|
|
10394
|
+
if (!object4(value) || !Array.isArray(value.assets))
|
|
10395
|
+
throw new TypeError("Expo update platform metadata is invalid.");
|
|
10396
|
+
const launchValue = object4(value.launchAsset) ? value.launchAsset : { path: value.bundle };
|
|
10397
|
+
const launchPath = safePath(launchValue.path, "launch bundle");
|
|
10398
|
+
const launchExtension = extension(launchValue.extension ?? null, launchPath);
|
|
10399
|
+
const assets = value.assets.map(asset);
|
|
10400
|
+
return {
|
|
10401
|
+
assets,
|
|
10402
|
+
launchAsset: {
|
|
10403
|
+
...launchExtension ? { extension: launchExtension } : {},
|
|
10404
|
+
path: launchPath
|
|
10405
|
+
}
|
|
10406
|
+
};
|
|
10407
|
+
};
|
|
10408
|
+
var parseAbsoluteExpoUpdateDescriptor = (value) => {
|
|
10409
|
+
if (!object4(value) || value.engine !== "expo" || value.format !== ABSOLUTE_EXPO_UPDATE_FORMAT || !object4(value.expoConfig) || !object4(value.platforms) || typeof value.runtimeVersion !== "string" || !/^[a-f0-9]{64}$/u.test(value.runtimeVersion))
|
|
10410
|
+
throw new TypeError("AbsoluteJS Expo update descriptor is invalid.");
|
|
10411
|
+
const platforms = {};
|
|
10412
|
+
for (const name of ["android", "ios"]) {
|
|
10413
|
+
const candidate = value.platforms[name];
|
|
10414
|
+
if (candidate !== undefined)
|
|
10415
|
+
platforms[name] = platform2(candidate);
|
|
10416
|
+
}
|
|
10417
|
+
if (Object.keys(platforms).length === 0)
|
|
10418
|
+
throw new TypeError("Expo update descriptor has no native platforms.");
|
|
10419
|
+
return {
|
|
10420
|
+
engine: "expo",
|
|
10421
|
+
expoConfig: value.expoConfig,
|
|
10422
|
+
format: ABSOLUTE_EXPO_UPDATE_FORMAT,
|
|
10423
|
+
platforms,
|
|
10424
|
+
runtimeVersion: value.runtimeVersion
|
|
10425
|
+
};
|
|
10426
|
+
};
|
|
10427
|
+
var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
10428
|
+
const metadata = JSON.parse(await readFile16(join17(options.exportDirectory, "metadata.json"), "utf8"));
|
|
10429
|
+
if (!object4(metadata) || !object4(metadata.fileMetadata))
|
|
10430
|
+
throw new TypeError("Expo export metadata.json is invalid.");
|
|
10431
|
+
const descriptor = parseAbsoluteExpoUpdateDescriptor({
|
|
10432
|
+
engine: "expo",
|
|
10433
|
+
expoConfig: options.expoConfig,
|
|
10434
|
+
format: ABSOLUTE_EXPO_UPDATE_FORMAT,
|
|
10435
|
+
platforms: metadata.fileMetadata,
|
|
10436
|
+
runtimeVersion: options.runtimeVersion
|
|
10437
|
+
});
|
|
10438
|
+
const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
|
|
10439
|
+
await Promise.all(referenced.map(({ path }) => access11(join17(options.exportDirectory, path)).catch(() => {
|
|
10440
|
+
throw new TypeError(`Expo export metadata references missing asset ${path}.`);
|
|
10441
|
+
})));
|
|
10442
|
+
const destination = join17(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
|
|
10443
|
+
await mkdir12(dirname11(destination), { recursive: true });
|
|
10444
|
+
await writeFile13(destination, `${JSON.stringify(descriptor, null, "\t")}
|
|
10445
|
+
`);
|
|
10446
|
+
return { descriptor, path: destination };
|
|
10447
|
+
};
|
|
10287
10448
|
|
|
10288
10449
|
// src/mobile/index.ts
|
|
10289
10450
|
init_config();
|
|
10290
10451
|
|
|
10291
10452
|
// src/mobile/ciWorkflow.ts
|
|
10292
10453
|
import { existsSync as existsSync4 } from "fs";
|
|
10293
|
-
import { access as
|
|
10294
|
-
import { dirname as
|
|
10454
|
+
import { access as access12, mkdir as mkdir13, readFile as readFile17, writeFile as writeFile14 } from "fs/promises";
|
|
10455
|
+
import { dirname as dirname12, extname as extname5, relative as relative11, resolve as resolve14, sep as sep7 } from "path";
|
|
10295
10456
|
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
|
|
10296
10457
|
var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
10297
10458
|
var CI_ENV_INDENTATION = 6;
|
|
@@ -10312,7 +10473,7 @@ var RESERVED_SECRET_NAMES = new Set([
|
|
|
10312
10473
|
]);
|
|
10313
10474
|
var exists4 = async (path) => {
|
|
10314
10475
|
try {
|
|
10315
|
-
await
|
|
10476
|
+
await access12(path);
|
|
10316
10477
|
return true;
|
|
10317
10478
|
} catch {
|
|
10318
10479
|
return false;
|
|
@@ -10337,7 +10498,7 @@ var workflowOutputPath = (projectRoot, value) => {
|
|
|
10337
10498
|
const workflows = resolve14(root, ".github/workflows");
|
|
10338
10499
|
const path = resolve14(root, value ?? ".github/workflows/absolute-mobile.yml");
|
|
10339
10500
|
const portable = relative11(workflows, path);
|
|
10340
|
-
if (portable === ".." || portable.startsWith(`..${sep7}`) ||
|
|
10501
|
+
if (portable === ".." || portable.startsWith(`..${sep7}`) || extname5(path) !== ".yml" && extname5(path) !== ".yaml") {
|
|
10341
10502
|
throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
|
|
10342
10503
|
}
|
|
10343
10504
|
return path;
|
|
@@ -10394,13 +10555,13 @@ var bundleAuditSteps = ` - name: Prepare production mobile bundle
|
|
|
10394
10555
|
- name: Enforce mobile bundle audit
|
|
10395
10556
|
if: steps.mobile-bundle-audit.outcome != 'success'
|
|
10396
10557
|
run: exit 1`;
|
|
10397
|
-
var releaseAuditSteps = (
|
|
10558
|
+
var releaseAuditSteps = (platform3) => ` - name: Run redacted mobile release audit
|
|
10398
10559
|
id: mobile-release-audit
|
|
10399
10560
|
continue-on-error: true
|
|
10400
10561
|
shell: bash
|
|
10401
10562
|
run: |
|
|
10402
10563
|
mkdir -p .absolutejs/mobile-ci
|
|
10403
|
-
args=(bunx absolute mobile doctor release ${
|
|
10564
|
+
args=(bunx absolute mobile doctor release ${platform3} --json)
|
|
10404
10565
|
${appendConfigArgument}
|
|
10405
10566
|
"\${args[@]}" > .absolutejs/mobile-ci/compliance.json
|
|
10406
10567
|
- name: Upload mobile compliance report
|
|
@@ -10467,7 +10628,7 @@ var publishingInputs = (platforms, includePublishing) => {
|
|
|
10467
10628
|
${fields.join(`
|
|
10468
10629
|
`)}`;
|
|
10469
10630
|
};
|
|
10470
|
-
var jobCondition = (
|
|
10631
|
+
var jobCondition = (platform3) => `github.event_name == 'workflow_dispatch' && (inputs.platform == 'all' || inputs.platform == '${platform3}')`;
|
|
10471
10632
|
var androidJob = (options) => {
|
|
10472
10633
|
const custom = customSecretEnvironment(options.customSecrets);
|
|
10473
10634
|
const publishEnvironment = options.includePublishing ? `
|
|
@@ -10766,18 +10927,18 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
|
|
|
10766
10927
|
var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
10767
10928
|
const path = workflowOutputPath(options.projectRoot, options.outputPath);
|
|
10768
10929
|
const generated = createAbsoluteMobileGithubWorkflow(options);
|
|
10769
|
-
const previous = await exists4(path) ? await
|
|
10930
|
+
const previous = await exists4(path) ? await readFile17(path, "utf8") : undefined;
|
|
10770
10931
|
if (previous !== undefined && previous !== generated.workflow && !options.force)
|
|
10771
10932
|
throw new TypeError(`${relative11(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
|
|
10772
10933
|
if (previous !== generated.workflow) {
|
|
10773
|
-
await
|
|
10774
|
-
await
|
|
10934
|
+
await mkdir13(dirname12(path), { recursive: true });
|
|
10935
|
+
await writeFile14(path, generated.workflow);
|
|
10775
10936
|
}
|
|
10776
10937
|
return {
|
|
10777
10938
|
changed: previous !== generated.workflow,
|
|
10778
10939
|
format: ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT,
|
|
10779
10940
|
path,
|
|
10780
|
-
platforms: options.config.engine === "expo" ? options.config.platforms.filter((
|
|
10941
|
+
platforms: options.config.engine === "expo" ? options.config.platforms.filter((platform3) => platform3 === "android") : [...options.config.platforms].sort(),
|
|
10781
10942
|
publishing: options.includePublishing === true,
|
|
10782
10943
|
requiredSecrets: generated.requiredSecrets
|
|
10783
10944
|
};
|
|
@@ -10984,13 +11145,13 @@ var createAbsoluteMobilePreviewPlugin = (mobile) => {
|
|
|
10984
11145
|
})).post("/__absolute/mobile-preview-telemetry", ({ body, status }) => {
|
|
10985
11146
|
const value = isRecord10(body) ? body : undefined;
|
|
10986
11147
|
const durationMs = value?.durationMs;
|
|
10987
|
-
const
|
|
10988
|
-
if (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0 || durationMs > 300000 ||
|
|
11148
|
+
const platform4 = value?.platform;
|
|
11149
|
+
if (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0 || durationMs > 300000 || platform4 !== "android" && platform4 !== "ios") {
|
|
10989
11150
|
return status(400, { error: "invalid-preview-telemetry" });
|
|
10990
11151
|
}
|
|
10991
11152
|
sendTelemetryEvent("mobile:preview-ready", {
|
|
10992
11153
|
durationMs: Math.round(durationMs),
|
|
10993
|
-
platform:
|
|
11154
|
+
platform: platform4,
|
|
10994
11155
|
provider: "capacitor",
|
|
10995
11156
|
target: "mobile-preview"
|
|
10996
11157
|
});
|
|
@@ -10998,19 +11159,19 @@ var createAbsoluteMobilePreviewPlugin = (mobile) => {
|
|
|
10998
11159
|
});
|
|
10999
11160
|
};
|
|
11000
11161
|
// src/mobile/nativeDeepLinks.ts
|
|
11001
|
-
import { readFile as
|
|
11002
|
-
import { join as
|
|
11162
|
+
import { readFile as readFile18, rename as rename12, writeFile as writeFile15 } from "fs/promises";
|
|
11163
|
+
import { join as join20 } from "path";
|
|
11003
11164
|
var START_MARKER = "<!-- absolutejs:deep-links:start -->";
|
|
11004
11165
|
var END_MARKER = "<!-- absolutejs:deep-links:end -->";
|
|
11005
11166
|
var IOS_ENTITLEMENTS = "App/AbsoluteJS.entitlements";
|
|
11006
11167
|
var NOT_FOUND = -1;
|
|
11007
11168
|
var escapeXml = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
11008
11169
|
var writeChangedFile = async (path, source) => {
|
|
11009
|
-
const current = await
|
|
11170
|
+
const current = await readFile18(path, "utf8");
|
|
11010
11171
|
if (current === source)
|
|
11011
11172
|
return false;
|
|
11012
11173
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
11013
|
-
await
|
|
11174
|
+
await writeFile15(temporary, source, { flag: "wx" });
|
|
11014
11175
|
await rename12(temporary, path);
|
|
11015
11176
|
return true;
|
|
11016
11177
|
};
|
|
@@ -11056,8 +11217,8 @@ ${hosts}
|
|
|
11056
11217
|
`;
|
|
11057
11218
|
};
|
|
11058
11219
|
var configureAndroid = async (config) => {
|
|
11059
|
-
const path =
|
|
11060
|
-
const source = await
|
|
11220
|
+
const path = join20(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
11221
|
+
const source = await readFile18(path, "utf8");
|
|
11061
11222
|
const mainActivity = source.indexOf('android:name=".MainActivity"');
|
|
11062
11223
|
if (mainActivity === NOT_FOUND) {
|
|
11063
11224
|
throw new TypeError("Android MainActivity was not found.");
|
|
@@ -11082,8 +11243,8 @@ var iosSchemeRegion = (scheme) => ` ${START_MARKER}
|
|
|
11082
11243
|
${END_MARKER}
|
|
11083
11244
|
`;
|
|
11084
11245
|
var configureIosInfo = async (config) => {
|
|
11085
|
-
const path =
|
|
11086
|
-
const source = await
|
|
11246
|
+
const path = join20(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
11247
|
+
const source = await readFile18(path, "utf8");
|
|
11087
11248
|
const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
|
|
11088
11249
|
${END_MARKER}
|
|
11089
11250
|
`;
|
|
@@ -11106,10 +11267,10 @@ ${domains}
|
|
|
11106
11267
|
`;
|
|
11107
11268
|
};
|
|
11108
11269
|
var configureIosEntitlements = async (config) => {
|
|
11109
|
-
const path =
|
|
11270
|
+
const path = join20(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
11110
11271
|
let current = "";
|
|
11111
11272
|
try {
|
|
11112
|
-
current = await
|
|
11273
|
+
current = await readFile18(path, "utf8");
|
|
11113
11274
|
} catch (error) {
|
|
11114
11275
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
11115
11276
|
throw error;
|
|
@@ -11119,13 +11280,13 @@ var configureIosEntitlements = async (config) => {
|
|
|
11119
11280
|
if (current === source)
|
|
11120
11281
|
return false;
|
|
11121
11282
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
11122
|
-
await
|
|
11283
|
+
await writeFile15(temporary, source, { flag: "wx" });
|
|
11123
11284
|
await rename12(temporary, path);
|
|
11124
11285
|
return true;
|
|
11125
11286
|
};
|
|
11126
11287
|
var configureIosProject = async (config) => {
|
|
11127
|
-
const path =
|
|
11128
|
-
const source = await
|
|
11288
|
+
const path = join20(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
11289
|
+
const source = await readFile18(path, "utf8");
|
|
11129
11290
|
const declarations = [
|
|
11130
11291
|
...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
|
|
11131
11292
|
].map((match) => match[1]);
|
|
@@ -11154,18 +11315,18 @@ var configureIos = async (config) => {
|
|
|
11154
11315
|
return changed.some(Boolean);
|
|
11155
11316
|
};
|
|
11156
11317
|
var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms) => {
|
|
11157
|
-
const results = await Promise.all(platforms.map(async (
|
|
11158
|
-
const didChange =
|
|
11159
|
-
return { didChange, platform:
|
|
11318
|
+
const results = await Promise.all(platforms.map(async (platform4) => {
|
|
11319
|
+
const didChange = platform4 === "android" ? await configureAndroid(config) : await configureIos(config);
|
|
11320
|
+
return { didChange, platform: platform4 };
|
|
11160
11321
|
}));
|
|
11161
11322
|
return {
|
|
11162
|
-
changed: results.filter(({ didChange }) => didChange).map(({ platform:
|
|
11323
|
+
changed: results.filter(({ didChange }) => didChange).map(({ platform: platform4 }) => platform4)
|
|
11163
11324
|
};
|
|
11164
11325
|
};
|
|
11165
11326
|
// src/mobile/nativeDeviceCapabilities.ts
|
|
11166
11327
|
init_deviceCapabilities();
|
|
11167
|
-
import { readFile as
|
|
11168
|
-
import { join as
|
|
11328
|
+
import { readFile as readFile19, rename as rename13, writeFile as writeFile16 } from "fs/promises";
|
|
11329
|
+
import { join as join21 } from "path";
|
|
11169
11330
|
var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->";
|
|
11170
11331
|
var END_MARKER2 = "<!-- absolutejs:device-capabilities:end -->";
|
|
11171
11332
|
var NOT_FOUND2 = -1;
|
|
@@ -11175,11 +11336,11 @@ var PUSH_START_MARKER = "absolutejs:push-notifications:start";
|
|
|
11175
11336
|
var PUSH_END_MARKER = "absolutejs:push-notifications:end";
|
|
11176
11337
|
var escapeXml2 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
11177
11338
|
var writeChangedFile2 = async (path, source) => {
|
|
11178
|
-
const current = await
|
|
11339
|
+
const current = await readFile19(path, "utf8");
|
|
11179
11340
|
if (current === source)
|
|
11180
11341
|
return false;
|
|
11181
11342
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
11182
|
-
await
|
|
11343
|
+
await writeFile16(temporary, source, { flag: "wx" });
|
|
11183
11344
|
await rename13(temporary, path);
|
|
11184
11345
|
return true;
|
|
11185
11346
|
};
|
|
@@ -11188,14 +11349,14 @@ var writeOptionalChangedFile = async (path, source) => {
|
|
|
11188
11349
|
if (current === source)
|
|
11189
11350
|
return false;
|
|
11190
11351
|
if (current === null) {
|
|
11191
|
-
await
|
|
11352
|
+
await writeFile16(path, source, { flag: "wx" });
|
|
11192
11353
|
return true;
|
|
11193
11354
|
}
|
|
11194
11355
|
return writeChangedFile2(path, source);
|
|
11195
11356
|
};
|
|
11196
11357
|
var optionalSource = async (path) => {
|
|
11197
11358
|
try {
|
|
11198
|
-
return await
|
|
11359
|
+
return await readFile19(path, "utf8");
|
|
11199
11360
|
} catch (error) {
|
|
11200
11361
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
|
|
11201
11362
|
return null;
|
|
@@ -11309,14 +11470,14 @@ var writeIosPrivacyManifest = async (path, current, source) => {
|
|
|
11309
11470
|
return false;
|
|
11310
11471
|
if (current !== null)
|
|
11311
11472
|
return writeChangedFile2(path, source);
|
|
11312
|
-
await
|
|
11473
|
+
await writeFile16(path, source, { flag: "wx" });
|
|
11313
11474
|
return true;
|
|
11314
11475
|
};
|
|
11315
11476
|
var configureIosPrivacyProject = async (config, requirements) => {
|
|
11316
11477
|
if (requirements.iosPrivacyAccessedApis.length === 0)
|
|
11317
11478
|
return false;
|
|
11318
|
-
const projectPath2 =
|
|
11319
|
-
const project = await
|
|
11479
|
+
const projectPath2 = join21(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
11480
|
+
const project = await readFile19(projectPath2, "utf8");
|
|
11320
11481
|
return writeChangedFile2(projectPath2, addIosPrivacyProjectReference(project));
|
|
11321
11482
|
};
|
|
11322
11483
|
var addIosPrivacyProjectReference = (source) => {
|
|
@@ -11368,8 +11529,8 @@ ${next.slice(index)}`;
|
|
|
11368
11529
|
return next;
|
|
11369
11530
|
};
|
|
11370
11531
|
var configureIos2 = async (config, plan) => {
|
|
11371
|
-
const path =
|
|
11372
|
-
const source = await
|
|
11532
|
+
const path = join21(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
11533
|
+
const source = await readFile19(path, "utf8");
|
|
11373
11534
|
const requirements = absoluteDeviceNativeRequirements(plan);
|
|
11374
11535
|
const existingSystemBars = source.match(/<key>UIViewControllerBasedStatusBarAppearance<\/key>\s*<(true|false)\s*\/>/u);
|
|
11375
11536
|
const ownedStart = source.indexOf(START_MARKER2);
|
|
@@ -11389,7 +11550,7 @@ ${content}
|
|
|
11389
11550
|
${END_MARKER2}
|
|
11390
11551
|
` : "";
|
|
11391
11552
|
const infoChanged = await writeChangedFile2(path, managed(source, region, source.lastIndexOf("</dict>")));
|
|
11392
|
-
const privacyPath =
|
|
11553
|
+
const privacyPath = join21(config.nativeProjectDirectory, "ios/App/App/PrivacyInfo.xcprivacy");
|
|
11393
11554
|
const privacyCurrent = await optionalSource(privacyPath);
|
|
11394
11555
|
const privacySource = privacyManifestSource(privacyCurrent, requirements);
|
|
11395
11556
|
const [privacyChanged, projectChanged, pushChanged] = await Promise.all([
|
|
@@ -11400,7 +11561,7 @@ ${content}
|
|
|
11400
11561
|
return infoChanged || privacyChanged || projectChanged || pushChanged;
|
|
11401
11562
|
};
|
|
11402
11563
|
var configureIosPushNotifications = async (config, enabled) => {
|
|
11403
|
-
const entitlementsPath =
|
|
11564
|
+
const entitlementsPath = join21(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
11404
11565
|
const entitlements = await optionalSource(entitlementsPath);
|
|
11405
11566
|
if (entitlements === null && !enabled)
|
|
11406
11567
|
return false;
|
|
@@ -11412,7 +11573,7 @@ var configureIosPushNotifications = async (config, enabled) => {
|
|
|
11412
11573
|
<!-- ${PUSH_END_MARKER} -->
|
|
11413
11574
|
` : "";
|
|
11414
11575
|
const nextEntitlements = replacePushRegion(entitlements, entitlementRegion, entitlements.lastIndexOf("</dict>"));
|
|
11415
|
-
const delegatePath =
|
|
11576
|
+
const delegatePath = join21(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
|
|
11416
11577
|
const delegate = await optionalSource(delegatePath);
|
|
11417
11578
|
if (delegate === null && !enabled)
|
|
11418
11579
|
return false;
|
|
@@ -11461,8 +11622,8 @@ var replacePushRegion = (source, region, insertion) => {
|
|
|
11461
11622
|
return `${source.slice(0, insertion)}${region}${source.slice(insertion)}`;
|
|
11462
11623
|
};
|
|
11463
11624
|
var configureAndroid2 = async (config, plan) => {
|
|
11464
|
-
const path =
|
|
11465
|
-
const source = await
|
|
11625
|
+
const path = join21(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
11626
|
+
const source = await readFile19(path, "utf8");
|
|
11466
11627
|
const permissions = absoluteDeviceNativeRequirements(plan).androidPermissions;
|
|
11467
11628
|
const content = permissions.map((permission) => ` <uses-permission android:name="${escapeXml2(permission)}" />`).join(`
|
|
11468
11629
|
`);
|
|
@@ -11497,17 +11658,17 @@ ${content}
|
|
|
11497
11658
|
throw new TypeError(`Android google-services.json does not contain package ${config.appId}.`);
|
|
11498
11659
|
const [manifestChanged, firebaseChanged] = await Promise.all([
|
|
11499
11660
|
writeChangedFile2(path, nextManifest),
|
|
11500
|
-
writeOptionalChangedFile(
|
|
11661
|
+
writeOptionalChangedFile(join21(config.nativeProjectDirectory, "android/app/google-services.json"), firebaseSource)
|
|
11501
11662
|
]);
|
|
11502
11663
|
return manifestChanged || firebaseChanged;
|
|
11503
11664
|
};
|
|
11504
11665
|
var applyAbsoluteNativeDeviceCapabilities = async (projectRoot, config, platforms = config.platforms, plan = resolveAbsoluteDeviceCapabilityPlan(projectRoot)) => {
|
|
11505
|
-
const results = await Promise.all(platforms.map(async (
|
|
11506
|
-
didChange:
|
|
11507
|
-
platform:
|
|
11666
|
+
const results = await Promise.all(platforms.map(async (platform4) => ({
|
|
11667
|
+
didChange: platform4 === "ios" ? await configureIos2(config, plan) : await configureAndroid2(config, plan),
|
|
11668
|
+
platform: platform4
|
|
11508
11669
|
})));
|
|
11509
11670
|
return {
|
|
11510
|
-
changed: results.filter(({ didChange }) => didChange).map(({ platform:
|
|
11671
|
+
changed: results.filter(({ didChange }) => didChange).map(({ platform: platform4 }) => platform4)
|
|
11511
11672
|
};
|
|
11512
11673
|
};
|
|
11513
11674
|
|
|
@@ -11516,14 +11677,14 @@ init_nativeAuth();
|
|
|
11516
11677
|
|
|
11517
11678
|
// src/mobile/nativeUpdates.ts
|
|
11518
11679
|
import {
|
|
11519
|
-
mkdir as
|
|
11520
|
-
readFile as
|
|
11680
|
+
mkdir as mkdir14,
|
|
11681
|
+
readFile as readFile20,
|
|
11521
11682
|
readdir as readdir5,
|
|
11522
11683
|
rename as rename14,
|
|
11523
11684
|
rm as rm11,
|
|
11524
|
-
writeFile as
|
|
11685
|
+
writeFile as writeFile17
|
|
11525
11686
|
} from "fs/promises";
|
|
11526
|
-
import { dirname as
|
|
11687
|
+
import { dirname as dirname14, join as join22 } from "path";
|
|
11527
11688
|
var START = "// absolutejs:mobile-updates:start";
|
|
11528
11689
|
var END = "// absolutejs:mobile-updates:end";
|
|
11529
11690
|
var PLUGIN_START = "// absolutejs:mobile-update-plugin:start";
|
|
@@ -11531,7 +11692,7 @@ var PLUGIN_END = "// absolutejs:mobile-update-plugin:end";
|
|
|
11531
11692
|
var ANDROID_PLUGIN = "AbsoluteMobileUpdateWatchdogPlugin.java";
|
|
11532
11693
|
var optionalSource2 = async (path) => {
|
|
11533
11694
|
try {
|
|
11534
|
-
return await
|
|
11695
|
+
return await readFile20(path, "utf8");
|
|
11535
11696
|
} catch (error) {
|
|
11536
11697
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
|
|
11537
11698
|
return null;
|
|
@@ -11543,13 +11704,13 @@ var writeChanged = async (path, source) => {
|
|
|
11543
11704
|
const current = await optionalSource2(path);
|
|
11544
11705
|
if (current === source)
|
|
11545
11706
|
return false;
|
|
11546
|
-
await
|
|
11707
|
+
await mkdir14(dirname14(path), { recursive: true });
|
|
11547
11708
|
if (current === null) {
|
|
11548
|
-
await
|
|
11709
|
+
await writeFile17(path, source, { flag: "wx" });
|
|
11549
11710
|
return true;
|
|
11550
11711
|
}
|
|
11551
11712
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
11552
|
-
await
|
|
11713
|
+
await writeFile17(temporary, source, { flag: "wx" });
|
|
11553
11714
|
await rename14(temporary, path);
|
|
11554
11715
|
return true;
|
|
11555
11716
|
};
|
|
@@ -11690,9 +11851,9 @@ public final class AbsoluteMobileUpdateWatchdogPlugin: CAPPlugin, CAPBridgedPlug
|
|
|
11690
11851
|
${PLUGIN_END}
|
|
11691
11852
|
`;
|
|
11692
11853
|
var configureIos3 = async (config, enabled) => {
|
|
11693
|
-
const path =
|
|
11694
|
-
const capacitorConfigPath =
|
|
11695
|
-
let source = await
|
|
11854
|
+
const path = join22(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
|
|
11855
|
+
const capacitorConfigPath = join22(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
|
|
11856
|
+
let source = await readFile20(path, "utf8");
|
|
11696
11857
|
const launch = source.indexOf("didFinishLaunchingWithOptions");
|
|
11697
11858
|
const brace = launch < 0 ? -1 : source.indexOf("{", launch);
|
|
11698
11859
|
const launchRegion = enabled ? ` ${START}
|
|
@@ -11703,7 +11864,7 @@ var configureIos3 = async (config, enabled) => {
|
|
|
11703
11864
|
`, brace) + 1, "Could not find a safe iOS location for mobile update recovery.");
|
|
11704
11865
|
const plugin = enabled ? iosPluginRegion(config.updates?.bootTimeoutMs ?? 20000) : "";
|
|
11705
11866
|
source = replaceMarkedRegion(source, PLUGIN_START, PLUGIN_END, plugin, source.length, "Could not find a safe iOS location for the mobile update watchdog.");
|
|
11706
|
-
const capacitorConfigSource2 = await
|
|
11867
|
+
const capacitorConfigSource2 = await readFile20(capacitorConfigPath, "utf8");
|
|
11707
11868
|
let capacitorConfig;
|
|
11708
11869
|
try {
|
|
11709
11870
|
const value = JSON.parse(capacitorConfigSource2);
|
|
@@ -11881,12 +12042,12 @@ var androidActivityInRoot = async (root) => {
|
|
|
11881
12042
|
throw error;
|
|
11882
12043
|
}
|
|
11883
12044
|
const candidate = entries.find((entry) => entry.endsWith("/MainActivity.java") || entry.endsWith("/MainActivity.kt") || entry === "MainActivity.java" || entry === "MainActivity.kt");
|
|
11884
|
-
return candidate ?
|
|
12045
|
+
return candidate ? join22(root, candidate) : undefined;
|
|
11885
12046
|
};
|
|
11886
12047
|
var androidActivity = async (config) => {
|
|
11887
12048
|
const roots = [
|
|
11888
|
-
|
|
11889
|
-
|
|
12049
|
+
join22(config.nativeProjectDirectory, "android/app/src/main/java"),
|
|
12050
|
+
join22(config.nativeProjectDirectory, "android/app/src/main/kotlin")
|
|
11890
12051
|
];
|
|
11891
12052
|
const candidates = await Promise.all(roots.map(androidActivityInRoot));
|
|
11892
12053
|
const candidate = candidates.find((value) => value !== undefined);
|
|
@@ -11985,12 +12146,12 @@ var injectAndroidActivity = (source, enabled) => {
|
|
|
11985
12146
|
};
|
|
11986
12147
|
var configureAndroid3 = async (config, enabled) => {
|
|
11987
12148
|
const activityPath = await androidActivity(config);
|
|
11988
|
-
const activity = await
|
|
12149
|
+
const activity = await readFile20(activityPath, "utf8");
|
|
11989
12150
|
const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
|
|
11990
12151
|
if (!packageName)
|
|
11991
12152
|
throw new TypeError("Android MainActivity package declaration was not found.");
|
|
11992
12153
|
const packagePath = packageName.replaceAll(".", "/");
|
|
11993
|
-
const pluginPath =
|
|
12154
|
+
const pluginPath = join22(config.nativeProjectDirectory, "android/app/src/main/java", packagePath, ANDROID_PLUGIN);
|
|
11994
12155
|
const changed = await Promise.all([
|
|
11995
12156
|
writeChanged(activityPath, injectAndroidActivity(activity, enabled)),
|
|
11996
12157
|
enabled ? writeChanged(pluginPath, androidPluginSource(packageName, config.updates?.bootTimeoutMs ?? 20000)) : removeOwnedFile(pluginPath)
|
|
@@ -12009,7 +12170,7 @@ var applyAbsoluteNativeUpdates = async (config, platforms = config.platforms) =>
|
|
|
12009
12170
|
return { changed: changed.some(Boolean) };
|
|
12010
12171
|
};
|
|
12011
12172
|
// src/mobile/releasePublisher.ts
|
|
12012
|
-
import { access as
|
|
12173
|
+
import { access as access13 } from "fs/promises";
|
|
12013
12174
|
import { isAbsolute as isAbsolute6, relative as relative12, resolve as resolve15, sep as sep8 } from "path";
|
|
12014
12175
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
12015
12176
|
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
@@ -12046,7 +12207,7 @@ var publisherModulePath = (projectRoot, requested) => {
|
|
|
12046
12207
|
};
|
|
12047
12208
|
var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
|
|
12048
12209
|
const modulePath = publisherModulePath(projectRoot, requestedModulePath);
|
|
12049
|
-
await
|
|
12210
|
+
await access13(modulePath).catch(() => {
|
|
12050
12211
|
throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
|
|
12051
12212
|
});
|
|
12052
12213
|
const loaded = await import(pathToFileURL3(modulePath).href);
|
|
@@ -12108,7 +12269,7 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
12108
12269
|
};
|
|
12109
12270
|
// src/mobile/routeMetadataTransform.ts
|
|
12110
12271
|
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
12111
|
-
import { dirname as
|
|
12272
|
+
import { dirname as dirname15, extname as extname6, relative as relative13, resolve as resolve16 } from "path";
|
|
12112
12273
|
import ts2 from "typescript";
|
|
12113
12274
|
var ROUTE_METHODS = new Set(["get", "head"]);
|
|
12114
12275
|
var SOURCE_FILTER = /\.[cm]?[jt]sx?$/;
|
|
@@ -12159,7 +12320,7 @@ var PAGE_HANDLERS = new Map([
|
|
|
12159
12320
|
]
|
|
12160
12321
|
]);
|
|
12161
12322
|
var posixPath = (value) => value.replace(/\\/g, "/");
|
|
12162
|
-
var findTsconfig = (entry, projectRoot) => ts2.findConfigFile(
|
|
12323
|
+
var findTsconfig = (entry, projectRoot) => ts2.findConfigFile(dirname15(entry), existsSync7, "tsconfig.json") ?? ts2.findConfigFile(projectRoot, existsSync7, "tsconfig.json");
|
|
12163
12324
|
var createProgram = (entry, projectRoot) => {
|
|
12164
12325
|
const configPath2 = findTsconfig(entry, projectRoot);
|
|
12165
12326
|
if (!configPath2) {
|
|
@@ -12171,7 +12332,7 @@ var createProgram = (entry, projectRoot) => {
|
|
|
12171
12332
|
target: ts2.ScriptTarget.ESNext
|
|
12172
12333
|
});
|
|
12173
12334
|
}
|
|
12174
|
-
const parsed = ts2.parseJsonConfigFileContent(ts2.readConfigFile(configPath2, (path) => readFileSync7(path, "utf8")).config, ts2.sys,
|
|
12335
|
+
const parsed = ts2.parseJsonConfigFileContent(ts2.readConfigFile(configPath2, (path) => readFileSync7(path, "utf8")).config, ts2.sys, dirname15(configPath2));
|
|
12175
12336
|
if (!parsed.fileNames.includes(entry))
|
|
12176
12337
|
parsed.fileNames.push(entry);
|
|
12177
12338
|
return ts2.createProgram(parsed.fileNames, parsed.options);
|
|
@@ -12185,8 +12346,8 @@ var propertyName = (property) => {
|
|
|
12185
12346
|
return property.name.text;
|
|
12186
12347
|
return;
|
|
12187
12348
|
};
|
|
12188
|
-
var objectPropertyExpression = (
|
|
12189
|
-
const property =
|
|
12349
|
+
var objectPropertyExpression = (object5, name) => {
|
|
12350
|
+
const property = object5.properties.find((candidate) => propertyName(candidate) === name);
|
|
12190
12351
|
if (property && ts2.isPropertyAssignment(property)) {
|
|
12191
12352
|
return property.initializer;
|
|
12192
12353
|
}
|
|
@@ -12382,8 +12543,8 @@ var spreadObject = (expression, checker, bindings) => {
|
|
|
12382
12543
|
return;
|
|
12383
12544
|
return callableObject(expression, checker);
|
|
12384
12545
|
};
|
|
12385
|
-
var objectAssetKey = (
|
|
12386
|
-
for (const property of [...
|
|
12546
|
+
var objectAssetKey = (object5, name, checker, bindings = new Map) => {
|
|
12547
|
+
for (const property of [...object5.properties].reverse()) {
|
|
12387
12548
|
if (propertyName(property) === name && ts2.isShorthandPropertyAssignment(property)) {
|
|
12388
12549
|
return assetKeyWithBindings(property.name, checker, bindings);
|
|
12389
12550
|
}
|
|
@@ -12622,7 +12783,7 @@ var createAbsoluteMobileRouteMetadataPlugin = (options) => {
|
|
|
12622
12783
|
const source = await Bun.file(path).text();
|
|
12623
12784
|
return {
|
|
12624
12785
|
contents: transformFile(source, path, analysis),
|
|
12625
|
-
loader:
|
|
12786
|
+
loader: extname6(path).endsWith("x") ? "tsx" : "ts"
|
|
12626
12787
|
};
|
|
12627
12788
|
});
|
|
12628
12789
|
}
|
|
@@ -13170,15 +13331,15 @@ var createAbsoluteMobileUpdateClient = (options) => {
|
|
|
13170
13331
|
const file = manifest.files[index];
|
|
13171
13332
|
if (!file)
|
|
13172
13333
|
return received;
|
|
13173
|
-
const
|
|
13334
|
+
const asset2 = await request(fileUrl(manifestUrl, manifest.releaseId, file.path), {
|
|
13174
13335
|
cache: "no-store",
|
|
13175
13336
|
credentials: "omit",
|
|
13176
13337
|
redirect: "error",
|
|
13177
13338
|
signal: AbortSignal.timeout(30000)
|
|
13178
13339
|
});
|
|
13179
|
-
if (!
|
|
13180
|
-
throw new TypeError(`Mobile update asset ${file.path} failed with HTTP ${
|
|
13181
|
-
const contents = await readBounded(
|
|
13340
|
+
if (!asset2.ok)
|
|
13341
|
+
throw new TypeError(`Mobile update asset ${file.path} failed with HTTP ${asset2.status}.`);
|
|
13342
|
+
const contents = await readBounded(asset2, file.bytes);
|
|
13182
13343
|
const total = received + contents.byteLength;
|
|
13183
13344
|
if (contents.byteLength !== file.bytes || total > ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES)
|
|
13184
13345
|
throw new TypeError(`Mobile update asset ${file.path} has an invalid size.`);
|
|
@@ -13233,7 +13394,7 @@ var createAbsoluteMobileUpdateClient = (options) => {
|
|
|
13233
13394
|
};
|
|
13234
13395
|
};
|
|
13235
13396
|
// src/mobile/updatePublisher.ts
|
|
13236
|
-
import { access as
|
|
13397
|
+
import { access as access14 } from "fs/promises";
|
|
13237
13398
|
import { isAbsolute as isAbsolute7, relative as relative15, resolve as resolve18, sep as sep9 } from "path";
|
|
13238
13399
|
import { pathToFileURL as pathToFileURL4 } from "url";
|
|
13239
13400
|
|
|
@@ -13241,23 +13402,23 @@ import { pathToFileURL as pathToFileURL4 } from "url";
|
|
|
13241
13402
|
import { createHash as createHash12, sign, verify } from "crypto";
|
|
13242
13403
|
import {
|
|
13243
13404
|
cp as cp3,
|
|
13244
|
-
mkdir as
|
|
13405
|
+
mkdir as mkdir15,
|
|
13245
13406
|
mkdtemp as mkdtemp8,
|
|
13246
13407
|
readdir as readdir6,
|
|
13247
|
-
readFile as
|
|
13408
|
+
readFile as readFile21,
|
|
13248
13409
|
rename as rename15,
|
|
13249
13410
|
rm as rm12,
|
|
13250
13411
|
stat as stat4,
|
|
13251
|
-
writeFile as
|
|
13412
|
+
writeFile as writeFile18
|
|
13252
13413
|
} from "fs/promises";
|
|
13253
|
-
import { dirname as
|
|
13414
|
+
import { dirname as dirname16, join as join23, relative as relative14, resolve as resolve17 } from "path";
|
|
13254
13415
|
var UPDATE_MANIFEST_FILE = "update.json";
|
|
13255
13416
|
var UPDATE_FILES_DIRECTORY = "files";
|
|
13256
13417
|
var sha2562 = (value) => createHash12("sha256").update(value).digest("hex");
|
|
13257
13418
|
var listFiles = async (root, directory = root) => {
|
|
13258
13419
|
const entries = await readdir6(directory, { withFileTypes: true });
|
|
13259
13420
|
const paths = await Promise.all(entries.map(async (entry) => {
|
|
13260
|
-
const path =
|
|
13421
|
+
const path = join23(directory, entry.name);
|
|
13261
13422
|
if (entry.isDirectory())
|
|
13262
13423
|
return listFiles(root, path);
|
|
13263
13424
|
if (!entry.isFile())
|
|
@@ -13267,10 +13428,10 @@ var listFiles = async (root, directory = root) => {
|
|
|
13267
13428
|
return paths.flat().sort((left, right) => left.localeCompare(right));
|
|
13268
13429
|
};
|
|
13269
13430
|
var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
|
|
13270
|
-
const source =
|
|
13431
|
+
const source = join23(root, path);
|
|
13271
13432
|
const [metadata, contents] = await Promise.all([
|
|
13272
13433
|
stat4(source),
|
|
13273
|
-
|
|
13434
|
+
readFile21(source)
|
|
13274
13435
|
]);
|
|
13275
13436
|
return { bytes: metadata.size, path, sha256: sha2562(contents) };
|
|
13276
13437
|
}));
|
|
@@ -13305,15 +13466,15 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
13305
13466
|
value: signature.toString("base64")
|
|
13306
13467
|
}
|
|
13307
13468
|
});
|
|
13308
|
-
await
|
|
13309
|
-
const outputDirectory =
|
|
13310
|
-
const staging = await mkdtemp8(
|
|
13469
|
+
await mkdir15(outputRoot, { recursive: true });
|
|
13470
|
+
const outputDirectory = join23(outputRoot, manifest.releaseId);
|
|
13471
|
+
const staging = await mkdtemp8(join23(outputRoot, ".stage-"));
|
|
13311
13472
|
try {
|
|
13312
|
-
await cp3(bundleDirectory,
|
|
13473
|
+
await cp3(bundleDirectory, join23(staging, UPDATE_FILES_DIRECTORY), {
|
|
13313
13474
|
force: true,
|
|
13314
13475
|
recursive: true
|
|
13315
13476
|
});
|
|
13316
|
-
await
|
|
13477
|
+
await writeFile18(join23(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
13317
13478
|
`);
|
|
13318
13479
|
await rename15(staging, outputDirectory);
|
|
13319
13480
|
} catch (error) {
|
|
@@ -13324,7 +13485,7 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
13324
13485
|
}
|
|
13325
13486
|
return {
|
|
13326
13487
|
manifest,
|
|
13327
|
-
manifestPath:
|
|
13488
|
+
manifestPath: join23(outputDirectory, UPDATE_MANIFEST_FILE),
|
|
13328
13489
|
outputDirectory
|
|
13329
13490
|
};
|
|
13330
13491
|
};
|
|
@@ -13333,10 +13494,10 @@ var copyAbsoluteMobileUpdateFile = async (updateDirectory, path, destination) =>
|
|
|
13333
13494
|
const root = resolve17(updateDirectory, UPDATE_FILES_DIRECTORY);
|
|
13334
13495
|
if (!source.startsWith(`${root}/`))
|
|
13335
13496
|
throw new TypeError("Update file escaped its release.");
|
|
13336
|
-
await
|
|
13497
|
+
await mkdir15(dirname16(destination), { recursive: true });
|
|
13337
13498
|
await cp3(source, destination, { force: true });
|
|
13338
13499
|
};
|
|
13339
|
-
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await
|
|
13500
|
+
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile21(join23(resolve17(directory), UPDATE_MANIFEST_FILE), "utf8")));
|
|
13340
13501
|
var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
13341
13502
|
const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
|
|
13342
13503
|
const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
|
|
@@ -13346,8 +13507,8 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
|
13346
13507
|
};
|
|
13347
13508
|
|
|
13348
13509
|
// src/mobile/updatePublisher.ts
|
|
13349
|
-
var
|
|
13350
|
-
var isPublisher2 = (value) =>
|
|
13510
|
+
var object5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13511
|
+
var isPublisher2 = (value) => object5(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
|
|
13351
13512
|
var projectPath2 = (projectRoot, requested, label) => {
|
|
13352
13513
|
const root = resolve18(projectRoot);
|
|
13353
13514
|
const path = resolve18(root, requested);
|
|
@@ -13358,11 +13519,11 @@ var projectPath2 = (projectRoot, requested, label) => {
|
|
|
13358
13519
|
};
|
|
13359
13520
|
var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
|
|
13360
13521
|
const modulePath = projectPath2(projectRoot, requestedModulePath, "mobile update registry");
|
|
13361
|
-
await
|
|
13522
|
+
await access14(modulePath).catch(() => {
|
|
13362
13523
|
throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
|
|
13363
13524
|
});
|
|
13364
13525
|
const loaded = await import(pathToFileURL4(modulePath).href);
|
|
13365
|
-
const publisher =
|
|
13526
|
+
const publisher = object5(loaded) ? loaded.default ?? loaded.registry : undefined;
|
|
13366
13527
|
if (!isPublisher2(publisher))
|
|
13367
13528
|
throw new TypeError("Mobile update registry must implement publishUpdate, promoteUpdate, and rollbackUpdate.");
|
|
13368
13529
|
return publisher;
|
|
@@ -13449,6 +13610,8 @@ export {
|
|
|
13449
13610
|
ABSOLUTE_EXPO_SYNC_CORE_VERSION,
|
|
13450
13611
|
ABSOLUTE_EXPO_SYNC_PACKAGE,
|
|
13451
13612
|
ABSOLUTE_EXPO_SYNC_VERSION,
|
|
13613
|
+
ABSOLUTE_EXPO_UPDATE_DESCRIPTOR,
|
|
13614
|
+
ABSOLUTE_EXPO_UPDATE_FORMAT,
|
|
13452
13615
|
ABSOLUTE_IOS_RELEASE_FORMAT,
|
|
13453
13616
|
ABSOLUTE_IOS_SIMULATOR_NAME,
|
|
13454
13617
|
ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT,
|
|
@@ -13533,6 +13696,7 @@ export {
|
|
|
13533
13696
|
discoverAbsoluteSyncSchema,
|
|
13534
13697
|
disposeAbsoluteMobilePage,
|
|
13535
13698
|
fetchAbsoluteMobilePage,
|
|
13699
|
+
finalizeAbsoluteExpoUpdateExport,
|
|
13536
13700
|
finalizeAbsoluteMobileCompatibilityBuild,
|
|
13537
13701
|
finalizeAbsoluteMobilePage,
|
|
13538
13702
|
fingerprintAbsoluteIosDevProject,
|
|
@@ -13576,6 +13740,7 @@ export {
|
|
|
13576
13740
|
pairAbsoluteRemoteMac,
|
|
13577
13741
|
parseAbsoluteAndroidInstalledApp,
|
|
13578
13742
|
parseAbsoluteExpoBridgeMessage,
|
|
13743
|
+
parseAbsoluteExpoUpdateDescriptor,
|
|
13579
13744
|
parseAbsoluteIosHmrLog,
|
|
13580
13745
|
parseAbsoluteIosLogLine,
|
|
13581
13746
|
parseAbsoluteMobileBuildPageMetadata,
|
|
@@ -13614,6 +13779,7 @@ export {
|
|
|
13614
13779
|
resolveAbsoluteMobileDeepLink,
|
|
13615
13780
|
resolveAbsoluteMobileNavigation,
|
|
13616
13781
|
resolveAbsoluteMobileRoute,
|
|
13782
|
+
resolveAbsoluteMobileUpdateRuntime,
|
|
13617
13783
|
retainAbsoluteMobileCompatibilityArtifacts,
|
|
13618
13784
|
rollbackAbsoluteMobileUpdate,
|
|
13619
13785
|
runAbsoluteAndroidUpgradeConformance,
|
|
@@ -13641,5 +13807,5 @@ export {
|
|
|
13641
13807
|
writeAbsoluteMobileGithubWorkflow
|
|
13642
13808
|
};
|
|
13643
13809
|
|
|
13644
|
-
//# debugId=
|
|
13810
|
+
//# debugId=CBACAEA8DBA2A4F164756E2164756E21
|
|
13645
13811
|
//# sourceMappingURL=index.js.map
|