@absolutejs/absolute 0.20.0-beta.85 → 0.20.0-beta.87

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.
@@ -200,6 +200,30 @@ var requestHeaders = (config) => ({
200
200
  "x-absolute-mobile-release": config.currentReleaseId,
201
201
  "x-absolute-mobile-runtime": config.runtimeFingerprint
202
202
  });
203
+ var healthUrl = (manifestUrl) => new URL("./health", manifestUrl);
204
+ var reportAbsoluteMobileUpdateHealth = async (config, input, request = globalThis.fetch) => {
205
+ const manifestUrl = exactManifestUrl(config.manifestUrl);
206
+ const headers = new Headers(requestHeaders(config));
207
+ headers.set("content-type", "application/json");
208
+ headers.set("x-absolute-mobile-health-token", input.healthToken);
209
+ const response = await request(healthUrl(manifestUrl), {
210
+ body: JSON.stringify({
211
+ kind: input.kind,
212
+ ...input.reason ? { reason: input.reason } : {},
213
+ releaseId: input.releaseId,
214
+ ...input.transfer ? { transfer: input.transfer } : {}
215
+ }),
216
+ cache: "no-store",
217
+ credentials: "omit",
218
+ headers,
219
+ keepalive: true,
220
+ method: "POST",
221
+ redirect: "error",
222
+ signal: AbortSignal.timeout(15000)
223
+ });
224
+ if (response.status !== 202)
225
+ throw new TypeError(`Mobile update health report failed with HTTP ${response.status}.`);
226
+ };
203
227
  var requireCompatible = (manifest, config) => {
204
228
  if (manifest.appId !== config.appId)
205
229
  throw new TypeError("Mobile update belongs to another app.");
@@ -395,15 +419,24 @@ var createAbsoluteMobileUpdateClient = (options) => {
395
419
  throw new TypeError("Mobile update manifest is not valid JSON.");
396
420
  }
397
421
  const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
422
+ const healthToken = response.headers.get("x-absolute-mobile-health-token");
398
423
  requireCompatible(manifest, options.config);
399
424
  if (!await options.verifier.verify(manifest))
400
425
  throw new TypeError("Mobile update signature verification failed.");
401
426
  if (manifest.releaseId === options.config.currentReleaseId)
402
427
  return { kind: "current" };
403
428
  if (options.config.blockedReleaseIds?.includes(manifest.releaseId))
404
- return { kind: "quarantined", releaseId: manifest.releaseId };
429
+ return {
430
+ ...healthToken ? { healthToken } : {},
431
+ kind: "quarantined",
432
+ releaseId: manifest.releaseId
433
+ };
405
434
  if (!download)
406
- return { kind: "update-available", manifest };
435
+ return {
436
+ ...healthToken ? { healthToken } : {},
437
+ kind: "update-available",
438
+ manifest
439
+ };
407
440
  await options.store.begin(manifest);
408
441
  let transfer;
409
442
  try {
@@ -414,14 +447,28 @@ var createAbsoluteMobileUpdateClient = (options) => {
414
447
  await options.store.suspend(manifest.releaseId);
415
448
  else
416
449
  await options.store.abort(manifest.releaseId);
450
+ if (healthToken)
451
+ reportAbsoluteMobileUpdateHealth(options.config, {
452
+ healthToken,
453
+ kind: "download-failed",
454
+ releaseId: manifest.releaseId
455
+ }, request).catch(() => {
456
+ return;
457
+ });
417
458
  throw error;
418
459
  }
419
- return { kind: "downloaded", manifest, transfer };
460
+ return {
461
+ ...healthToken ? { healthToken } : {},
462
+ kind: "downloaded",
463
+ manifest,
464
+ transfer
465
+ };
420
466
  };
421
467
  return {
422
468
  check,
423
469
  activate: (releaseId) => options.store.activate(releaseId),
424
- download: () => check(true)
470
+ download: () => check(true),
471
+ report: (input) => reportAbsoluteMobileUpdateHealth(options.config, input, request)
425
472
  };
426
473
  };
427
474
 
@@ -478,12 +525,15 @@ var readState = async () => {
478
525
  releaseId: recovery.releaseId
479
526
  };
480
527
  return {
528
+ ...text("activeHealthToken") ? { activeHealthToken: text("activeHealthToken") } : {},
481
529
  ...text("activeRelease") ? { activeRelease: text("activeRelease") } : {},
482
530
  ...text("pendingRelease") ? { pendingRelease: text("pendingRelease") } : {},
531
+ ...text("pendingHealthToken") ? { pendingHealthToken: text("pendingHealthToken") } : {},
483
532
  ...number("pendingStartedAt") === undefined ? {} : { pendingStartedAt: number("pendingStartedAt") },
484
533
  ...text("previousPath") ? { previousPath: text("previousPath") } : {},
485
534
  ...quarantinedReleases.length > 0 ? { quarantinedReleases } : {},
486
535
  ...text("readyRelease") ? { readyRelease: text("readyRelease") } : {},
536
+ ...text("readyHealthToken") ? { readyHealthToken: text("readyHealthToken") } : {},
487
537
  ...validRecovery ? { recovery: validRecovery } : {}
488
538
  };
489
539
  } catch {
@@ -569,6 +619,7 @@ var createStore = () => {
569
619
  const state = await readState();
570
620
  if (state.readyRelease === releaseId || state.pendingRelease === releaseId)
571
621
  await writeState({
622
+ activeHealthToken: state.activeHealthToken,
572
623
  activeRelease: state.activeRelease,
573
624
  quarantinedReleases: state.quarantinedReleases,
574
625
  recovery: state.recovery
@@ -581,7 +632,9 @@ var createStore = () => {
581
632
  const path = await releaseNativePath(releaseId);
582
633
  const previousPath = await currentServerBasePath();
583
634
  await writeState({
635
+ activeHealthToken: state.activeHealthToken,
584
636
  activeRelease: state.activeRelease,
637
+ pendingHealthToken: state.readyHealthToken,
585
638
  pendingRelease: releaseId,
586
639
  pendingStartedAt: Date.now(),
587
640
  previousPath,
@@ -596,6 +649,7 @@ var createStore = () => {
596
649
  });
597
650
  await removeRelease(releaseId);
598
651
  await writeState({
652
+ activeHealthToken: state.activeHealthToken,
599
653
  activeRelease: state.activeRelease,
600
654
  quarantinedReleases: state.quarantinedReleases
601
655
  });
@@ -653,14 +707,15 @@ var createStore = () => {
653
707
  commit: async (manifest) => {
654
708
  if (staging?.releaseId !== manifest.releaseId)
655
709
  throw new TypeError("Mobile update staging transaction changed identity.");
656
- await removeStaging(manifest.releaseId);
657
- await Preferences.remove({ key: STAGING_KEY });
658
710
  const state = await readState();
659
711
  await writeState({
712
+ activeHealthToken: state.activeHealthToken,
660
713
  activeRelease: state.activeRelease,
661
714
  quarantinedReleases: state.quarantinedReleases,
662
715
  readyRelease: manifest.releaseId
663
716
  });
717
+ await removeStaging(manifest.releaseId);
718
+ await Preferences.remove({ key: STAGING_KEY });
664
719
  staging = undefined;
665
720
  },
666
721
  readPartial: async (file) => {
@@ -738,29 +793,56 @@ var removePriorRelease = async (prior, active) => {
738
793
  if (prior && prior !== active)
739
794
  await removeRelease(prior);
740
795
  };
741
- var reconcilePendingRelease = async (store, state) => {
796
+ var reconcilePendingRelease = async (store, state, report) => {
742
797
  if (!state.pendingRelease)
743
798
  return state;
744
799
  const pendingPath = await releaseNativePath(state.pendingRelease);
745
800
  const currentPath = await currentServerBasePath();
746
801
  if (currentPath !== pendingPath) {
747
802
  const failed = state.pendingRelease;
803
+ if (state.pendingHealthToken)
804
+ report({
805
+ healthToken: state.pendingHealthToken,
806
+ kind: "rolled-back",
807
+ releaseId: failed
808
+ });
748
809
  await store.abort(failed);
749
810
  emitUpdateResult({ kind: "rolled-back", releaseId: failed });
750
811
  return readState();
751
812
  }
752
813
  webView().persistServerBasePath();
753
- const next = { activeRelease: state.pendingRelease };
814
+ const next = {
815
+ activeHealthToken: state.pendingHealthToken,
816
+ activeRelease: state.pendingRelease
817
+ };
754
818
  await writeState(next);
755
819
  await watchdog.confirm({ releaseId: state.pendingRelease });
820
+ if (state.pendingHealthToken)
821
+ report({
822
+ healthToken: state.pendingHealthToken,
823
+ kind: "activated",
824
+ releaseId: state.pendingRelease
825
+ });
756
826
  await removePriorRelease(state.activeRelease, next.activeRelease);
757
827
  emitUpdateResult({ kind: "activated", releaseId: next.activeRelease });
758
828
  return next;
759
829
  };
760
- var consumeNativeRecovery = async (state) => {
830
+ var consumeNativeRecovery = async (state, report) => {
761
831
  if (!state.recovery)
762
832
  return state;
763
- const { recovery, ...next } = state;
833
+ const {
834
+ pendingHealthToken,
835
+ readyHealthToken: _readyHealthToken,
836
+ recovery,
837
+ ...next
838
+ } = state;
839
+ if (pendingHealthToken)
840
+ report({
841
+ healthToken: pendingHealthToken,
842
+ kind: "rolled-back",
843
+ reason: recovery.reason,
844
+ releaseId: recovery.releaseId
845
+ });
764
846
  emitUpdateResult({
765
847
  durationMs: Math.round(recovery.durationMs),
766
848
  kind: "rolled-back",
@@ -771,28 +853,53 @@ var consumeNativeRecovery = async (state) => {
771
853
  return next;
772
854
  };
773
855
  var installAbsoluteMobileShellUpdates = async (manifest) => {
774
- if (!manifest.updates)
856
+ const { updates } = manifest;
857
+ if (!updates)
775
858
  return;
776
859
  const store = createStore();
777
- const recovered = await consumeNativeRecovery(await readState());
778
- const state = await reconcilePendingRelease(store, recovered);
860
+ const identity = await installationId();
861
+ const clientConfig = (state2) => ({
862
+ appId: manifest.appId,
863
+ blockedReleaseIds: state2.quarantinedReleases ?? [],
864
+ channel: updates.channel,
865
+ currentReleaseId: state2.activeRelease ?? `embedded:${manifest.appBuild}`,
866
+ installationId: identity,
867
+ manifestUrl: updates.manifestUrl,
868
+ runtimeFingerprint: manifest.nativeRuntime
869
+ });
870
+ const reportFor = (state2) => async (evidence) => {
871
+ await reportAbsoluteMobileUpdateHealth(clientConfig(state2), evidence).catch(() => {
872
+ return;
873
+ });
874
+ };
875
+ const initial = await readState();
876
+ const recovered = await consumeNativeRecovery(initial, reportFor(initial));
877
+ const state = await reconcilePendingRelease(store, recovered, reportFor(recovered));
779
878
  const client = createAbsoluteMobileUpdateClient({
780
- config: {
781
- appId: manifest.appId,
782
- blockedReleaseIds: state.quarantinedReleases ?? [],
783
- channel: manifest.updates.channel,
784
- currentReleaseId: state.activeRelease ?? `embedded:${manifest.appBuild}`,
785
- installationId: await installationId(),
786
- manifestUrl: manifest.updates.manifestUrl,
787
- runtimeFingerprint: manifest.nativeRuntime
788
- },
879
+ config: clientConfig(state),
789
880
  store,
790
- verifier: createVerifier(manifest.updates.publicKeys),
881
+ verifier: createVerifier(updates.publicKeys),
791
882
  onProgress: (progress) => emitUpdateResult(progress)
792
883
  });
793
884
  const activateDownloaded = async (result) => {
794
885
  if (result.kind !== "downloaded")
795
886
  return;
887
+ if (result.healthToken) {
888
+ const ready = await readState();
889
+ if (ready.readyRelease === result.manifest.releaseId)
890
+ await writeState({
891
+ ...ready,
892
+ readyHealthToken: result.healthToken
893
+ });
894
+ client.report({
895
+ healthToken: result.healthToken,
896
+ kind: "downloaded",
897
+ releaseId: result.manifest.releaseId,
898
+ transfer: result.transfer
899
+ }).catch(() => {
900
+ return;
901
+ });
902
+ }
796
903
  emitUpdateResult({
797
904
  avoidedBytes: result.transfer.avoidedBytes,
798
905
  completedFiles: result.transfer.completedFiles,
@@ -813,6 +920,14 @@ var installAbsoluteMobileShellUpdates = async (manifest) => {
813
920
  };
814
921
  client.download().then((result) => {
815
922
  if (result.kind === "quarantined") {
923
+ if (result.healthToken)
924
+ client.report({
925
+ healthToken: result.healthToken,
926
+ kind: "quarantined",
927
+ releaseId: result.releaseId
928
+ }).catch(() => {
929
+ return;
930
+ });
816
931
  emitUpdateResult({
817
932
  kind: "quarantined",
818
933
  releaseId: result.releaseId
@@ -36,6 +36,20 @@ export type NormalizedAbsoluteMobileConfig = {
36
36
  };
37
37
  updateServer?: {
38
38
  autoMount: boolean;
39
+ health?: {
40
+ failureRate: number;
41
+ minimumReports: number;
42
+ secretEnv: string;
43
+ };
44
+ rollout?: {
45
+ automatic: boolean;
46
+ stages: {
47
+ maximumFailureRate: number;
48
+ minimumReports: number;
49
+ observationMs: number;
50
+ rollout: number;
51
+ }[];
52
+ };
39
53
  expoCodeSigningKeys: Record<string, {
40
54
  certificatePem: string;
41
55
  privateKeyEnv: string;
@@ -41,16 +41,27 @@ export type AbsoluteMobileUpdateClientOptions = {
41
41
  export type AbsoluteMobileUpdateCheckResult = {
42
42
  kind: 'current';
43
43
  } | {
44
+ healthToken?: string;
44
45
  kind: 'downloaded';
45
46
  manifest: AbsoluteMobileUpdateManifest;
46
47
  transfer: AbsoluteMobileUpdateTransfer;
47
48
  } | {
49
+ healthToken?: string;
48
50
  kind: 'quarantined';
49
51
  releaseId: string;
50
52
  } | {
53
+ healthToken?: string;
51
54
  kind: 'update-available';
52
55
  manifest: AbsoluteMobileUpdateManifest;
53
56
  };
57
+ export type AbsoluteMobileUpdateHealthKind = 'activated' | 'downloaded' | 'download-failed' | 'quarantined' | 'rolled-back';
58
+ export type AbsoluteMobileUpdateHealthEvidence = {
59
+ healthToken: string;
60
+ kind: AbsoluteMobileUpdateHealthKind;
61
+ reason?: 'boot-interrupted' | 'boot-timeout';
62
+ releaseId: string;
63
+ transfer?: Pick<AbsoluteMobileUpdateTransfer, 'avoidedBytes' | 'downloadedBytes' | 'durationMs' | 'resumedBytes' | 'reusedBytes' | 'throughputBytesPerSecond'>;
64
+ };
54
65
  export type AbsoluteMobileUpdateTransfer = {
55
66
  avoidedBytes: number;
56
67
  completedFiles: number;
@@ -68,8 +79,10 @@ export type AbsoluteMobileUpdateTransfer = {
68
79
  export type AbsoluteMobileUpdateProgress = AbsoluteMobileUpdateTransfer & {
69
80
  kind: 'download-progress';
70
81
  };
82
+ export declare const reportAbsoluteMobileUpdateHealth: (config: AbsoluteMobileUpdateClientConfig, input: AbsoluteMobileUpdateHealthEvidence, request?: typeof globalThis.fetch) => Promise<void>;
71
83
  export declare const createAbsoluteMobileUpdateClient: (options: AbsoluteMobileUpdateClientOptions) => {
72
84
  check: (download?: boolean) => Promise<AbsoluteMobileUpdateCheckResult>;
73
85
  activate: (releaseId: string) => Promise<void>;
74
86
  download: () => Promise<AbsoluteMobileUpdateCheckResult>;
87
+ report: (input: AbsoluteMobileUpdateHealthEvidence) => Promise<void>;
75
88
  };
@@ -1,4 +1,4 @@
1
- import type { MobileUpdatePruneOptions, MobileUpdatePruneResult, MobileUpdateRetentionOptions, MobileUpdateStorageReport } from '@absolutejs/deploy/mobile-update';
1
+ import type { MobileUpdateHealthReport, MobileUpdatePruneOptions, MobileUpdatePruneResult, MobileUpdateRetentionOptions, MobileUpdateRolloutReport, MobileUpdateStorageReport } from '@absolutejs/deploy/mobile-update';
2
2
  import { readAbsoluteMobileUpdate } from './updateSigning';
3
3
  export type AbsoluteMobileUpdatePublication = {
4
4
  appId: string;
@@ -26,6 +26,31 @@ export type AbsoluteMobileUpdateRollback = {
26
26
  stage: 'rolled-back';
27
27
  };
28
28
  export type AbsoluteMobileUpdatePublisher = {
29
+ advanceUpdateRollout?: (options: {
30
+ appId: string;
31
+ channel: string;
32
+ rollout?: number;
33
+ signal?: AbortSignal;
34
+ }) => Promise<MobileUpdateRolloutReport>;
35
+ cancelUpdateRollout?: (options: {
36
+ appId: string;
37
+ channel: string;
38
+ signal?: AbortSignal;
39
+ }) => Promise<MobileUpdateRolloutReport>;
40
+ inspectUpdateHealth?: (options: {
41
+ appId: string;
42
+ channel: string;
43
+ releaseId?: string;
44
+ }) => Promise<MobileUpdateHealthReport | null>;
45
+ inspectUpdateRollout?: (options: {
46
+ appId: string;
47
+ channel: string;
48
+ }) => Promise<MobileUpdateRolloutReport | null>;
49
+ pauseUpdateRollout?: (options: {
50
+ appId: string;
51
+ channel: string;
52
+ signal?: AbortSignal;
53
+ }) => Promise<MobileUpdateRolloutReport>;
29
54
  inspectUpdateStorage?: (options: MobileUpdateRetentionOptions) => Promise<MobileUpdateStorageReport>;
30
55
  pruneUpdates?: (options: MobileUpdatePruneOptions) => Promise<MobileUpdatePruneResult>;
31
56
  publishUpdate(options: {
@@ -47,7 +72,59 @@ export type AbsoluteMobileUpdatePublisher = {
47
72
  releaseId?: string;
48
73
  signal?: AbortSignal;
49
74
  }): Promise<AbsoluteMobileUpdateRollback>;
75
+ reconcileUpdateRollout?: (options: {
76
+ appId: string;
77
+ channel: string;
78
+ signal?: AbortSignal;
79
+ }) => Promise<MobileUpdateRolloutReport | null>;
80
+ resumeUpdateRollout?: (options: {
81
+ appId: string;
82
+ channel: string;
83
+ signal?: AbortSignal;
84
+ }) => Promise<MobileUpdateRolloutReport>;
50
85
  };
86
+ export declare const inspectAbsoluteMobileUpdateHealth: (options: {
87
+ appId: string;
88
+ channel: string;
89
+ publisher: AbsoluteMobileUpdatePublisher;
90
+ releaseId?: string;
91
+ }) => Promise<MobileUpdateHealthReport | null>;
92
+ export declare const inspectAbsoluteMobileUpdateRollout: (options: {
93
+ appId: string;
94
+ channel: string;
95
+ publisher: AbsoluteMobileUpdatePublisher;
96
+ }) => Promise<MobileUpdateRolloutReport | null>;
97
+ export declare const advanceAbsoluteMobileUpdateRollout: (options: {
98
+ appId: string;
99
+ channel: string;
100
+ publisher: AbsoluteMobileUpdatePublisher;
101
+ rollout?: number;
102
+ signal?: AbortSignal;
103
+ }) => Promise<MobileUpdateRolloutReport>;
104
+ export declare const cancelAbsoluteMobileUpdateRollout: (options: {
105
+ appId: string;
106
+ channel: string;
107
+ publisher: AbsoluteMobileUpdatePublisher;
108
+ signal?: AbortSignal;
109
+ }) => Promise<MobileUpdateRolloutReport>;
110
+ export declare const pauseAbsoluteMobileUpdateRollout: (options: {
111
+ appId: string;
112
+ channel: string;
113
+ publisher: AbsoluteMobileUpdatePublisher;
114
+ signal?: AbortSignal;
115
+ }) => Promise<MobileUpdateRolloutReport>;
116
+ export declare const reconcileAbsoluteMobileUpdateRollout: (options: {
117
+ appId: string;
118
+ channel: string;
119
+ publisher: AbsoluteMobileUpdatePublisher;
120
+ signal?: AbortSignal;
121
+ }) => Promise<MobileUpdateRolloutReport | null>;
122
+ export declare const resumeAbsoluteMobileUpdateRollout: (options: {
123
+ appId: string;
124
+ channel: string;
125
+ publisher: AbsoluteMobileUpdatePublisher;
126
+ signal?: AbortSignal;
127
+ }) => Promise<MobileUpdateRolloutReport>;
51
128
  export declare const inspectAbsoluteMobileUpdateStorage: (options: {
52
129
  appId: string;
53
130
  minAgeMs?: number;
@@ -22,11 +22,39 @@ export declare const createAbsoluteMobileUpdateServerPlugin: (config: Normalized
22
22
  }, import("elysia/types").DefaultMetadata, {}, import("elysia/types").DefaultEphemeral, import("elysia/types").DefaultEphemeral>>;
23
23
  export declare const inspectAbsoluteMobileUpdateServer: (config: NormalizedAbsoluteMobileConfig, projectRoot: string) => Promise<AbsoluteMobileUpdateServerMetadata | undefined>;
24
24
  export declare const renderAbsoluteMobileUpdateRegistry: (options: {
25
+ health?: {
26
+ failureRate: number;
27
+ minimumReports: number;
28
+ secretEnv: string;
29
+ };
30
+ rollout?: {
31
+ automatic: boolean;
32
+ stages: {
33
+ maximumFailureRate: number;
34
+ minimumReports: number;
35
+ observationMs: number;
36
+ rollout: number;
37
+ }[];
38
+ };
25
39
  publicKeys: Readonly<Record<string, string>>;
26
40
  storage: "local" | "s3";
27
41
  }) => string;
28
42
  export declare const writeAbsoluteMobileUpdateRegistry: (options: {
29
43
  force?: boolean;
44
+ health?: {
45
+ failureRate: number;
46
+ minimumReports: number;
47
+ secretEnv: string;
48
+ };
49
+ rollout?: {
50
+ automatic: boolean;
51
+ stages: {
52
+ maximumFailureRate: number;
53
+ minimumReports: number;
54
+ observationMs: number;
55
+ rollout: number;
56
+ }[];
57
+ };
30
58
  modulePath?: string;
31
59
  projectRoot: string;
32
60
  publicKeys: Readonly<Record<string, string>>;
@@ -111,6 +111,31 @@ type MobileSharedConfig = {
111
111
  server?: {
112
112
  /** Mount the update endpoint in the AbsoluteJS runtime. Defaults to true. */
113
113
  autoMount?: boolean;
114
+ /** Anonymous fleet-health receipts and automatic rollout protection. Enabled by default. */
115
+ health?: false | {
116
+ /** Failure fraction that pauses a promotion. Defaults to 0.2. */
117
+ failureRate?: number;
118
+ /** Terminal activation/rollback sample required before pausing. Defaults to 20. */
119
+ minimumReports?: number;
120
+ /** Trusted-server secret environment variable. */
121
+ secretEnv?: string;
122
+ };
123
+ /** Health-gated staged rollout orchestration. Disabled unless configured. */
124
+ rollout?: false | {
125
+ /** Advance qualifying stages from health reports. Defaults to false. */
126
+ automatic?: boolean;
127
+ /** Defaults to recommended 5%, 25%, and 100% stages. */
128
+ stages?: readonly {
129
+ /** Failure ceiling for advancement. Defaults to 0.05. */
130
+ maximumFailureRate?: number;
131
+ /** Cumulative terminal reports required. Defaults to 20. */
132
+ minimumReports?: number;
133
+ /** Time at this stage before advancement. Defaults to 60 minutes. */
134
+ observationMinutes?: number;
135
+ /** Installation cohort fraction, greater than 0 and at most 1. */
136
+ rollout: number;
137
+ }[];
138
+ };
114
139
  /** Project-relative registry module. Defaults to `mobile.update.ts`. */
115
140
  registry?: string;
116
141
  /** Environment variable containing the Expo RSA private key PEM. */
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "dependencies": {
10
10
  "@absolutejs/auth": ">=0.75.0 <0.77.0",
11
11
  "@absolutejs/beacon": ">=0.7.0-beta.4 <0.8.0",
12
- "@absolutejs/deploy": "0.25.9",
12
+ "@absolutejs/deploy": "0.25.13",
13
13
  "@absolutejs/devices": "0.7.0",
14
14
  "@absolutejs/devices-capacitor": "0.8.0",
15
15
  "@absolutejs/devices-expo": "0.0.2",
@@ -523,7 +523,7 @@
523
523
  ]
524
524
  }
525
525
  },
526
- "version": "0.20.0-beta.85",
526
+ "version": "0.20.0-beta.87",
527
527
  "workspaces": [
528
528
  "tests/fixtures/*",
529
529
  "tests/fixtures/_packages/*"