Merge pull request #461 from trails-cool/federation/outbox
feat(journal): federation outbox + push delivery to remote followers
This commit is contained in:
commit
1d4f364870
16 changed files with 905 additions and 27 deletions
119
apps/journal/app/jobs/deliver-activity.ts
Normal file
119
apps/journal/app/jobs/deliver-activity.ts
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
import type { JobDefinition } from "@trails-cool/jobs";
|
||||||
|
import { and, eq } from "drizzle-orm";
|
||||||
|
import { activities } from "@trails-cool/db/schema/journal";
|
||||||
|
import { getDb } from "../lib/db.ts";
|
||||||
|
import { getOrigin } from "../lib/config.server.ts";
|
||||||
|
import { getFederation } from "../lib/federation.server.ts";
|
||||||
|
import {
|
||||||
|
activityToCreate,
|
||||||
|
activityToDelete,
|
||||||
|
type FederatableActivity,
|
||||||
|
} from "../lib/federation-objects.server.ts";
|
||||||
|
import {
|
||||||
|
getCachedRemoteActor,
|
||||||
|
upsertRemoteActor,
|
||||||
|
type DeliveryPayload,
|
||||||
|
} from "../lib/federation-delivery.server.ts";
|
||||||
|
import { logger } from "../lib/logger.server.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outbound pacing (spec 5.5): never exceed 1 request/second per remote
|
||||||
|
* host. In-process map is sufficient — pg-boss works this queue
|
||||||
|
* sequentially in the single journal process.
|
||||||
|
*/
|
||||||
|
const lastSendPerHost = new Map<string, number>();
|
||||||
|
const MIN_INTERVAL_MS = 1000;
|
||||||
|
|
||||||
|
async function paceHost(host: string): Promise<void> {
|
||||||
|
const last = lastSendPerHost.get(host) ?? 0;
|
||||||
|
const wait = last + MIN_INTERVAL_MS - Date.now();
|
||||||
|
if (wait > 0) await new Promise((r) => setTimeout(r, wait));
|
||||||
|
lastSendPerHost.set(host, Date.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deliver one activity to one remote follower's inbox (spec 5.3/5.4).
|
||||||
|
* One job per (activity, recipient) so each delivery retries with
|
||||||
|
* exponential backoff independently (configured at enqueue time —
|
||||||
|
* see enqueueActivityDeliveries). A thrown error marks the attempt
|
||||||
|
* failed and pg-boss retries; exhausting the budget is the permanent
|
||||||
|
* failure, logged by the final catch.
|
||||||
|
*/
|
||||||
|
export const deliverActivityJob: JobDefinition = {
|
||||||
|
name: "deliver-activity",
|
||||||
|
expireInSeconds: 60,
|
||||||
|
async handler(jobs) {
|
||||||
|
for (const job of jobs) {
|
||||||
|
const p = job.data as DeliveryPayload;
|
||||||
|
try {
|
||||||
|
await deliverOne(p);
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(
|
||||||
|
{ err, action: p.action, objectIri: p.objectIri, recipient: p.recipientActorIri },
|
||||||
|
"deliver-activity attempt failed (pg-boss will retry until budget exhausted)",
|
||||||
|
);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async function deliverOne(p: DeliveryPayload): Promise<void> {
|
||||||
|
const federation = getFederation();
|
||||||
|
const ctx = federation.createContext(new URL(getOrigin()), undefined);
|
||||||
|
|
||||||
|
// Build the activity to send. For `create`, re-read the row at
|
||||||
|
// delivery time: if it was deleted or un-publicized since enqueue,
|
||||||
|
// skip rather than leak.
|
||||||
|
let activity;
|
||||||
|
if (p.action === "create") {
|
||||||
|
const db = getDb();
|
||||||
|
const [row] = await db
|
||||||
|
.select()
|
||||||
|
.from(activities)
|
||||||
|
.where(and(eq(activities.id, p.activityId!), eq(activities.visibility, "public")))
|
||||||
|
.limit(1);
|
||||||
|
if (!row) {
|
||||||
|
logger.info({ objectIri: p.objectIri }, "deliver-activity: activity gone or non-public; skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activity = activityToCreate(row as FederatableActivity, p.ownerUsername);
|
||||||
|
} else {
|
||||||
|
activity = activityToDelete(p.objectIri, p.ownerUsername);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the recipient's inbox: cached remote_actors row first,
|
||||||
|
// actor-document fetch as fallback (which also primes the cache).
|
||||||
|
const recipientIri = new URL(p.recipientActorIri);
|
||||||
|
let inboxUrl: URL;
|
||||||
|
const cached = await getCachedRemoteActor(p.recipientActorIri);
|
||||||
|
if (cached?.inboxUrl) {
|
||||||
|
inboxUrl = new URL(cached.inboxUrl);
|
||||||
|
} else {
|
||||||
|
await paceHost(recipientIri.host);
|
||||||
|
const actor = await ctx.lookupObject(recipientIri);
|
||||||
|
const fetchedInbox =
|
||||||
|
actor != null && "inboxId" in actor ? (actor.inboxId as URL | null) : null;
|
||||||
|
if (!fetchedInbox) {
|
||||||
|
throw new Error(`deliver-activity: cannot resolve inbox for ${p.recipientActorIri}`);
|
||||||
|
}
|
||||||
|
inboxUrl = fetchedInbox;
|
||||||
|
await upsertRemoteActor({
|
||||||
|
actorIri: p.recipientActorIri,
|
||||||
|
inboxUrl: inboxUrl.href,
|
||||||
|
domain: recipientIri.host,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await paceHost(inboxUrl.host);
|
||||||
|
await ctx.sendActivity(
|
||||||
|
// Identifier and username are the same thing in our actor model.
|
||||||
|
{ identifier: p.ownerUsername },
|
||||||
|
{ id: recipientIri, inboxId: inboxUrl },
|
||||||
|
activity,
|
||||||
|
);
|
||||||
|
logger.info(
|
||||||
|
{ action: p.action, objectIri: p.objectIri, recipient: p.recipientActorIri },
|
||||||
|
"deliver-activity: delivered",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import type { Visibility } from "@trails-cool/db/schema/journal";
|
||||||
import { validateGpx, writeGeom } from "./gpx-save.server.ts";
|
import { validateGpx, writeGeom } from "./gpx-save.server.ts";
|
||||||
import type { GpxData } from "./gpx-save.server.ts";
|
import type { GpxData } from "./gpx-save.server.ts";
|
||||||
import { enqueueOptional } from "./boss.server.ts";
|
import { enqueueOptional } from "./boss.server.ts";
|
||||||
|
import { enqueueActivityDeliveries } from "./federation-delivery.server.ts";
|
||||||
|
|
||||||
export interface ActivityInput {
|
export interface ActivityInput {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -38,6 +39,16 @@ export async function updateActivityVisibility(
|
||||||
// followers (only the first transition per activity emits).
|
// followers (only the first transition per activity emits).
|
||||||
if (visibility === "public") {
|
if (visibility === "public") {
|
||||||
await enqueueOptional("notifications-fanout", { activityId: id }, { source: "updateActivityVisibility" });
|
await enqueueOptional("notifications-fanout", { activityId: id }, { source: "updateActivityVisibility" });
|
||||||
|
// Federation: newly-public activities push to remote followers as
|
||||||
|
// Create(Note) (spec 5.3/5.6 — publish-on-flip is the "update"
|
||||||
|
// remotes care about; the delivery job re-checks visibility).
|
||||||
|
await enqueueActivityDeliveries(ownerId, id, "create");
|
||||||
|
} else {
|
||||||
|
// Was the activity possibly public before? Retract from remotes —
|
||||||
|
// a Delete for an object a remote never saw is acknowledged and
|
||||||
|
// dropped, so over-sending here is harmless and avoids tracking
|
||||||
|
// previous visibility (spec 5.6, basic update/delete fan-out).
|
||||||
|
await enqueueActivityDeliveries(ownerId, id, "delete");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -92,6 +103,8 @@ export async function createActivity(ownerId: string, input: ActivityInput) {
|
||||||
// up-front rather than flipped later).
|
// up-front rather than flipped later).
|
||||||
if (input.visibility === "public") {
|
if (input.visibility === "public") {
|
||||||
await enqueueOptional("notifications-fanout", { activityId: id }, { source: "createActivity" });
|
await enqueueOptional("notifications-fanout", { activityId: id }, { source: "createActivity" });
|
||||||
|
// Federation push delivery to accepted remote followers (spec 5.3).
|
||||||
|
await enqueueActivityDeliveries(ownerId, id, "create");
|
||||||
}
|
}
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
|
|
@ -108,9 +121,15 @@ export async function getActivity(id: string) {
|
||||||
|
|
||||||
export async function deleteActivity(id: string, ownerId: string): Promise<boolean> {
|
export async function deleteActivity(id: string, ownerId: string): Promise<boolean> {
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
const [activity] = await db.select({ id: activities.id }).from(activities)
|
const [activity] = await db.select({ id: activities.id, visibility: activities.visibility }).from(activities)
|
||||||
.where(and(eq(activities.id, id), eq(activities.ownerId, ownerId)));
|
.where(and(eq(activities.id, id), eq(activities.ownerId, ownerId)));
|
||||||
if (!activity) return false;
|
if (!activity) return false;
|
||||||
|
// Enqueue the federation retraction *before* the row disappears —
|
||||||
|
// the Delete payload carries everything it needs (object IRI +
|
||||||
|
// owner), so it survives the deletion (spec 5.6).
|
||||||
|
if (activity.visibility === "public") {
|
||||||
|
await enqueueActivityDeliveries(ownerId, id, "delete");
|
||||||
|
}
|
||||||
await db.delete(activities).where(eq(activities.id, id));
|
await db.delete(activities).where(eq(activities.id, id));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,14 @@ import { logger } from "./logger.server.ts";
|
||||||
|
|
||||||
// Structurally typed (we only need `send`) so we don't have to pull
|
// Structurally typed (we only need `send`) so we don't have to pull
|
||||||
// pg-boss into the journal app's dep graph just for the typedef.
|
// pg-boss into the journal app's dep graph just for the typedef.
|
||||||
|
export interface BossSendOptions {
|
||||||
|
retryLimit?: number;
|
||||||
|
retryBackoff?: boolean;
|
||||||
|
retryDelay?: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface BossLike {
|
interface BossLike {
|
||||||
send(queueName: string, data: unknown): Promise<string | null>;
|
send(queueName: string, data: unknown, options?: BossSendOptions): Promise<string | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let _boss: BossLike | null = null;
|
let _boss: BossLike | null = null;
|
||||||
|
|
@ -40,10 +46,11 @@ export async function enqueueOptional(
|
||||||
queue: string,
|
queue: string,
|
||||||
data: unknown,
|
data: unknown,
|
||||||
ctx: Record<string, unknown> = {},
|
ctx: Record<string, unknown> = {},
|
||||||
|
options?: BossSendOptions,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const boss = getBoss();
|
const boss = getBoss();
|
||||||
await boss.send(queue, data);
|
await boss.send(queue, data, options);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn({ err, queue, ...ctx }, "boss.send failed; continuing");
|
logger.warn({ err, queue, ...ctx }, "boss.send failed; continuing");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
115
apps/journal/app/lib/federation-delivery.server.ts
Normal file
115
apps/journal/app/lib/federation-delivery.server.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Push delivery of local public activities to accepted remote
|
||||||
|
// followers (spec: social-federation, "Push delivery on local activity
|
||||||
|
// create"). Enqueue side lives here; the actual signed POST happens in
|
||||||
|
// jobs/deliver-activity.ts.
|
||||||
|
|
||||||
|
import { and, eq, isNotNull } from "drizzle-orm";
|
||||||
|
import { follows, remoteActors, users } from "@trails-cool/db/schema/journal";
|
||||||
|
import { getDb } from "./db.ts";
|
||||||
|
import { federationEnabled } from "./federation.server.ts";
|
||||||
|
import { enqueueOptional } from "./boss.server.ts";
|
||||||
|
import { activityObjectIri } from "./federation-objects.server.ts";
|
||||||
|
|
||||||
|
export type DeliveryAction = "create" | "delete";
|
||||||
|
|
||||||
|
export interface DeliveryPayload {
|
||||||
|
action: DeliveryAction;
|
||||||
|
/** Present for `create` — the job re-reads the row at delivery time. */
|
||||||
|
activityId?: string;
|
||||||
|
/** Object IRI; for `delete` this is all that's left of the activity. */
|
||||||
|
objectIri: string;
|
||||||
|
ownerUsername: string;
|
||||||
|
recipientActorIri: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Accepted remote followers of a local user (the delivery audience). */
|
||||||
|
export async function listAcceptedRemoteFollowers(ownerId: string): Promise<string[]> {
|
||||||
|
const db = getDb();
|
||||||
|
const rows = await db
|
||||||
|
.select({ actorIri: follows.followerActorIri })
|
||||||
|
.from(follows)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(follows.followedUserId, ownerId),
|
||||||
|
isNotNull(follows.followerActorIri),
|
||||||
|
isNotNull(follows.acceptedAt),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return rows.map((r) => r.actorIri).filter((iri): iri is string => iri !== null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fan out one delivery job per accepted remote follower (spec 5.3: a
|
||||||
|
* job per follower, each with its own retry/backoff lifecycle). No-op
|
||||||
|
* when federation is off or the user has no remote followers — the
|
||||||
|
* common case stays a single SELECT.
|
||||||
|
*/
|
||||||
|
export async function enqueueActivityDeliveries(
|
||||||
|
ownerId: string,
|
||||||
|
activityId: string,
|
||||||
|
action: DeliveryAction,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!federationEnabled()) return;
|
||||||
|
const recipients = await listAcceptedRemoteFollowers(ownerId);
|
||||||
|
if (recipients.length === 0) return;
|
||||||
|
|
||||||
|
const db = getDb();
|
||||||
|
const [owner] = await db
|
||||||
|
.select({ username: users.username, profileVisibility: users.profileVisibility })
|
||||||
|
.from(users)
|
||||||
|
.where(eq(users.id, ownerId))
|
||||||
|
.limit(1);
|
||||||
|
// Private profiles don't federate — suppress push delivery entirely
|
||||||
|
// (spec 9.3: flipping to private stops federation).
|
||||||
|
if (!owner || owner.profileVisibility !== "public") return;
|
||||||
|
|
||||||
|
for (const recipientActorIri of recipients) {
|
||||||
|
const payload: DeliveryPayload = {
|
||||||
|
action,
|
||||||
|
activityId: action === "create" ? activityId : undefined,
|
||||||
|
objectIri: activityObjectIri(activityId),
|
||||||
|
ownerUsername: owner.username,
|
||||||
|
recipientActorIri,
|
||||||
|
};
|
||||||
|
await enqueueOptional(
|
||||||
|
"deliver-activity",
|
||||||
|
payload,
|
||||||
|
{ source: "enqueueActivityDeliveries", action },
|
||||||
|
// Spec 5.4: exponential backoff on failure, bounded retry budget.
|
||||||
|
// 8 retries with backoff spans roughly a day before permanent fail.
|
||||||
|
{ retryLimit: 8, retryBackoff: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CachedRemoteActor {
|
||||||
|
actorIri: string;
|
||||||
|
inboxUrl: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read the remote actor cache; null when we've never seen the actor. */
|
||||||
|
export async function getCachedRemoteActor(actorIri: string): Promise<CachedRemoteActor | null> {
|
||||||
|
const db = getDb();
|
||||||
|
const [row] = await db
|
||||||
|
.select({ actorIri: remoteActors.actorIri, inboxUrl: remoteActors.inboxUrl })
|
||||||
|
.from(remoteActors)
|
||||||
|
.where(eq(remoteActors.actorIri, actorIri))
|
||||||
|
.limit(1);
|
||||||
|
return row ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Upsert the fields delivery learns about a remote actor. */
|
||||||
|
export async function upsertRemoteActor(fields: {
|
||||||
|
actorIri: string;
|
||||||
|
inboxUrl?: string | null;
|
||||||
|
displayName?: string | null;
|
||||||
|
username?: string | null;
|
||||||
|
domain?: string | null;
|
||||||
|
}): Promise<void> {
|
||||||
|
const db = getDb();
|
||||||
|
const { actorIri, ...rest } = fields;
|
||||||
|
await db
|
||||||
|
.insert(remoteActors)
|
||||||
|
.values({ actorIri, ...rest })
|
||||||
|
.onConflictDoUpdate({ target: remoteActors.actorIri, set: rest });
|
||||||
|
}
|
||||||
86
apps/journal/app/lib/federation-objects.server.test.ts
Normal file
86
apps/journal/app/lib/federation-objects.server.test.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import {
|
||||||
|
activityToNote,
|
||||||
|
activityToCreate,
|
||||||
|
activityToDelete,
|
||||||
|
activityObjectIri,
|
||||||
|
type FederatableActivity,
|
||||||
|
} from "./federation-objects.server.ts";
|
||||||
|
|
||||||
|
const ACTIVITY: FederatableActivity = {
|
||||||
|
id: "act-1",
|
||||||
|
name: "Morning ride <3",
|
||||||
|
description: 'Through the "forest" & hills',
|
||||||
|
distance: 42_195,
|
||||||
|
elevationGain: 512.4,
|
||||||
|
duration: 2 * 3600 + 30 * 60,
|
||||||
|
startedAt: new Date("2026-06-01T08:00:00Z"),
|
||||||
|
createdAt: new Date("2026-06-01T12:00:00Z"),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("activityToNote", () => {
|
||||||
|
it("builds a public Note addressed at the activity page", async () => {
|
||||||
|
const note = activityToNote(ACTIVITY, "bruno");
|
||||||
|
expect(note.id?.href).toBe("http://localhost:3000/activities/act-1");
|
||||||
|
expect(note.attributionId?.href).toBe("http://localhost:3000/users/bruno");
|
||||||
|
expect(note.toIds.map((u) => u.href)).toContain(
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
);
|
||||||
|
expect(note.published?.toString()).toBe("2026-06-01T08:00:00Z");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("escapes HTML in user content and includes stats + link", () => {
|
||||||
|
const note = activityToNote(ACTIVITY, "bruno");
|
||||||
|
const content = String(note.content);
|
||||||
|
expect(content).toContain("Morning ride <3");
|
||||||
|
expect(content).toContain(""forest" & hills");
|
||||||
|
expect(content).toContain("42.2 km");
|
||||||
|
expect(content).toContain("↗ 512 m");
|
||||||
|
expect(content).toContain("2h 30m");
|
||||||
|
expect(content).toContain('href="http://localhost:3000/activities/act-1"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("carries structured metadata as PropertyValue attachments", async () => {
|
||||||
|
const note = activityToNote(ACTIVITY, "bruno");
|
||||||
|
const attachments = [];
|
||||||
|
for await (const a of note.getAttachments()) attachments.push(a);
|
||||||
|
const byName = new Map(
|
||||||
|
attachments.map((a) => [String((a as { name: unknown }).name), String((a as { value: unknown }).value)]),
|
||||||
|
);
|
||||||
|
expect(byName.get("distance-m")).toBe("42195");
|
||||||
|
expect(byName.get("elevation-gain-m")).toBe("512");
|
||||||
|
expect(byName.get("duration-s")).toBe("9000");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("omits stats it doesn't have", () => {
|
||||||
|
const bare = activityToNote(
|
||||||
|
{ ...ACTIVITY, distance: null, elevationGain: null, duration: null, description: null },
|
||||||
|
"bruno",
|
||||||
|
);
|
||||||
|
const content = String(bare.content);
|
||||||
|
expect(content).not.toContain("km");
|
||||||
|
expect(content).not.toContain("↗");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to createdAt when startedAt is missing", () => {
|
||||||
|
const note = activityToNote({ ...ACTIVITY, startedAt: null }, "bruno");
|
||||||
|
expect(note.published?.toString()).toBe("2026-06-01T12:00:00Z");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("activityToCreate", () => {
|
||||||
|
it("derives a stable id from the object and attributes the actor", () => {
|
||||||
|
const create = activityToCreate(ACTIVITY, "bruno");
|
||||||
|
expect(create.id?.href).toBe("http://localhost:3000/activities/act-1#create");
|
||||||
|
expect(create.actorId?.href).toBe("http://localhost:3000/users/bruno");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("activityToDelete", () => {
|
||||||
|
it("wraps a Tombstone for the deleted object", async () => {
|
||||||
|
const del = activityToDelete(activityObjectIri("act-1"), "bruno");
|
||||||
|
expect(del.actorId?.href).toBe("http://localhost:3000/users/bruno");
|
||||||
|
const tombstone = await del.getObject();
|
||||||
|
expect(tombstone?.id?.href).toBe("http://localhost:3000/activities/act-1");
|
||||||
|
});
|
||||||
|
});
|
||||||
121
apps/journal/app/lib/federation-objects.server.ts
Normal file
121
apps/journal/app/lib/federation-objects.server.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
// Mapping from journal activities to ActivityStreams objects (spec:
|
||||||
|
// social-federation, "Outbox publishes user's public activities" +
|
||||||
|
// push delivery). One mapping, used by both the outbox dispatcher and
|
||||||
|
// the deliver-activity job so remotes see identical shapes either way.
|
||||||
|
//
|
||||||
|
// Shape decision (design.md open question, resolved toward Mastodon
|
||||||
|
// compat): plain `Create(Note)`. The Note's HTML content carries the
|
||||||
|
// human-readable text + a link back to the activity page; structured
|
||||||
|
// trails metadata (distance, elevation, duration) rides along as
|
||||||
|
// PropertyValue attachments, which Mastodon ignores gracefully and
|
||||||
|
// trails consumers can read without parsing the HTML.
|
||||||
|
|
||||||
|
import { Temporal as TemporalPolyfill } from "@js-temporal/polyfill";
|
||||||
|
import { Create, Delete, Note, PropertyValue, Tombstone, PUBLIC_COLLECTION } from "@fedify/fedify/vocab";
|
||||||
|
import { getOrigin } from "./config.server.ts";
|
||||||
|
import { localActorIri } from "./actor-iri.ts";
|
||||||
|
|
||||||
|
export interface FederatableActivity {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
distance: number | null;
|
||||||
|
elevationGain: number | null;
|
||||||
|
duration: number | null;
|
||||||
|
startedAt: Date | null;
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Public IRI of an activity — its journal detail page. */
|
||||||
|
export function activityObjectIri(activityId: string): string {
|
||||||
|
return `${getOrigin()}/activities/${activityId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(s: string): string {
|
||||||
|
return s
|
||||||
|
.replaceAll("&", "&")
|
||||||
|
.replaceAll("<", "<")
|
||||||
|
.replaceAll(">", ">")
|
||||||
|
.replaceAll('"', """);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatStats(a: FederatableActivity): string {
|
||||||
|
const parts: string[] = [];
|
||||||
|
if (a.distance != null) parts.push(`${(a.distance / 1000).toFixed(1)} km`);
|
||||||
|
if (a.elevationGain != null) parts.push(`↗ ${Math.round(a.elevationGain)} m`);
|
||||||
|
if (a.duration != null) {
|
||||||
|
const h = Math.floor(a.duration / 3600);
|
||||||
|
const m = Math.round((a.duration % 3600) / 60);
|
||||||
|
parts.push(h > 0 ? `${h}h ${m}m` : `${m}m`);
|
||||||
|
}
|
||||||
|
return parts.join(" · ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fedify's types reference the *global* Temporal namespace
|
||||||
|
// (esnext.temporal lib); Node 22 doesn't ship Temporal at runtime, so
|
||||||
|
// we construct via the polyfill and bridge the nominally-distinct (but
|
||||||
|
// structurally identical) types with a cast.
|
||||||
|
function toInstant(d: Date): Temporal.Instant {
|
||||||
|
return TemporalPolyfill.Instant.fromEpochMilliseconds(
|
||||||
|
d.getTime(),
|
||||||
|
) as unknown as Temporal.Instant;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function activityToNote(a: FederatableActivity, ownerUsername: string): Note {
|
||||||
|
const objectIri = activityObjectIri(a.id);
|
||||||
|
const stats = formatStats(a);
|
||||||
|
const paragraphs = [
|
||||||
|
`<p>${escapeHtml(a.name)}</p>`,
|
||||||
|
a.description ? `<p>${escapeHtml(a.description)}</p>` : "",
|
||||||
|
stats ? `<p>${escapeHtml(stats)}</p>` : "",
|
||||||
|
`<p><a href="${objectIri}">${objectIri}</a></p>`,
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
const attachments: PropertyValue[] = [];
|
||||||
|
if (a.distance != null) {
|
||||||
|
attachments.push(new PropertyValue({ name: "distance-m", value: String(Math.round(a.distance)) }));
|
||||||
|
}
|
||||||
|
if (a.elevationGain != null) {
|
||||||
|
attachments.push(new PropertyValue({ name: "elevation-gain-m", value: String(Math.round(a.elevationGain)) }));
|
||||||
|
}
|
||||||
|
if (a.duration != null) {
|
||||||
|
attachments.push(new PropertyValue({ name: "duration-s", value: String(a.duration) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Note({
|
||||||
|
id: new URL(objectIri),
|
||||||
|
attribution: new URL(localActorIri(ownerUsername)),
|
||||||
|
content: paragraphs.join("\n"),
|
||||||
|
mediaType: "text/html",
|
||||||
|
url: new URL(objectIri),
|
||||||
|
published: toInstant(a.startedAt ?? a.createdAt),
|
||||||
|
to: PUBLIC_COLLECTION,
|
||||||
|
attachments,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function activityToCreate(a: FederatableActivity, ownerUsername: string): Create {
|
||||||
|
return new Create({
|
||||||
|
// Stable id derived from the object so replays/dedupe work across
|
||||||
|
// outbox pages and push deliveries alike.
|
||||||
|
id: new URL(`${activityObjectIri(a.id)}#create`),
|
||||||
|
actor: new URL(localActorIri(ownerUsername)),
|
||||||
|
object: activityToNote(a, ownerUsername),
|
||||||
|
published: toInstant(a.startedAt ?? a.createdAt),
|
||||||
|
to: PUBLIC_COLLECTION,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retraction for a deleted (or un-publicized) activity. Carries a
|
||||||
|
* Tombstone so remotes drop their copy (Mastodon honors Delete +
|
||||||
|
* Tombstone).
|
||||||
|
*/
|
||||||
|
export function activityToDelete(objectIri: string, ownerUsername: string): Delete {
|
||||||
|
return new Delete({
|
||||||
|
id: new URL(`${objectIri}#delete-${Date.now()}`),
|
||||||
|
actor: new URL(localActorIri(ownerUsername)),
|
||||||
|
object: new Tombstone({ id: new URL(objectIri) }),
|
||||||
|
to: PUBLIC_COLLECTION,
|
||||||
|
});
|
||||||
|
}
|
||||||
124
apps/journal/app/lib/federation-outbox.integration.test.ts
Normal file
124
apps/journal/app/lib/federation-outbox.integration.test.ts
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { randomUUID } from "node:crypto";
|
||||||
|
import { getDb } from "./db.ts";
|
||||||
|
import { users, activities, follows } from "@trails-cool/db/schema/journal";
|
||||||
|
|
||||||
|
// Opt-in: talks to real Postgres (and exercises the real Fedify
|
||||||
|
// dispatcher stack via handleFederationRequest). Same convention as the
|
||||||
|
// other *.integration.test.ts files.
|
||||||
|
const runIntegration = process.env.FEDERATION_INTEGRATION === "1";
|
||||||
|
|
||||||
|
const USERNAME = `outbox-user-${Date.now()}`;
|
||||||
|
let userId: string;
|
||||||
|
|
||||||
|
async function seed() {
|
||||||
|
const db = getDb();
|
||||||
|
userId = randomUUID();
|
||||||
|
await db.insert(users).values({
|
||||||
|
id: userId,
|
||||||
|
email: `${USERNAME}@example.test`,
|
||||||
|
username: USERNAME,
|
||||||
|
domain: "test.local",
|
||||||
|
profileVisibility: "public",
|
||||||
|
});
|
||||||
|
// 3 public, 1 unlisted, 1 private — only the 3 public ones federate.
|
||||||
|
const mk = (n: number, visibility: "public" | "unlisted" | "private") => ({
|
||||||
|
id: randomUUID(),
|
||||||
|
ownerId: userId,
|
||||||
|
name: `Activity ${n}`,
|
||||||
|
visibility,
|
||||||
|
distance: 1000 * n,
|
||||||
|
createdAt: new Date(Date.now() - n * 60_000),
|
||||||
|
});
|
||||||
|
await db
|
||||||
|
.insert(activities)
|
||||||
|
.values([mk(1, "public"), mk(2, "public"), mk(3, "public"), mk(4, "unlisted"), mk(5, "private")]);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe.runIf(runIntegration)("federation outbox (integration)", () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
process.env.FEDERATION_ENABLED = "true";
|
||||||
|
process.env.ORIGIN ??= "http://localhost:3000";
|
||||||
|
await seed();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = getDb();
|
||||||
|
await db.delete(activities).where(eq(activities.ownerId, userId));
|
||||||
|
await db.delete(follows).where(eq(follows.followedUserId, userId));
|
||||||
|
await db.delete(users).where(eq(users.id, userId));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mirrors routes/users.$username.outbox.ts: route-level visibility
|
||||||
|
// gate first (Fedify's collection-level response doesn't consult the
|
||||||
|
// page dispatcher), then delegate to Fedify.
|
||||||
|
async function fetchOutbox(path: string): Promise<Response> {
|
||||||
|
const { handleFederationRequest, isFederatableUser } = await import("./federation.server.ts");
|
||||||
|
if (!(await isFederatableUser(USERNAME))) {
|
||||||
|
return new Response("Not Found", { status: 404 });
|
||||||
|
}
|
||||||
|
return handleFederationRequest(
|
||||||
|
new Request(`http://localhost:3000${path}`, {
|
||||||
|
headers: { accept: "application/activity+json" },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
it("serves an OrderedCollection counting only public activities", async () => {
|
||||||
|
const res = await fetchOutbox(`/users/${USERNAME}/outbox`);
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
const body = await res.json();
|
||||||
|
expect(body.type).toBe("OrderedCollection");
|
||||||
|
expect(body.totalItems).toBe(3);
|
||||||
|
expect(body.first).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("pages contain Create(Note) items, public only, newest first", async () => {
|
||||||
|
const res = await fetchOutbox(`/users/${USERNAME}/outbox?cursor=0`);
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
const body = await res.json();
|
||||||
|
const items = Array.isArray(body.orderedItems) ? body.orderedItems : [body.orderedItems];
|
||||||
|
expect(items).toHaveLength(3);
|
||||||
|
for (const item of items) {
|
||||||
|
expect(item.type).toBe("Create");
|
||||||
|
expect(item.object.type).toBe("Note");
|
||||||
|
expect(item.object.attributedTo).toContain(`/users/${USERNAME}`);
|
||||||
|
}
|
||||||
|
const names = items.map((i: { object: { content: string } }) => i.object.content);
|
||||||
|
expect(names[0]).toContain("Activity 1"); // newest first
|
||||||
|
expect(names.join()).not.toContain("Activity 4"); // unlisted excluded
|
||||||
|
expect(names.join()).not.toContain("Activity 5"); // private excluded
|
||||||
|
});
|
||||||
|
|
||||||
|
it("404s the outbox of a private profile", async () => {
|
||||||
|
const db = getDb();
|
||||||
|
await db.update(users).set({ profileVisibility: "private" }).where(eq(users.id, userId));
|
||||||
|
const res = await fetchOutbox(`/users/${USERNAME}/outbox`);
|
||||||
|
expect(res.status).toBe(404);
|
||||||
|
await db.update(users).set({ profileVisibility: "public" }).where(eq(users.id, userId));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("lists accepted remote followers as the delivery audience", async () => {
|
||||||
|
const db = getDb();
|
||||||
|
const { listAcceptedRemoteFollowers } = await import("./federation-delivery.server.ts");
|
||||||
|
await db.insert(follows).values([
|
||||||
|
{
|
||||||
|
id: randomUUID(),
|
||||||
|
followerActorIri: "https://other.example/users/accepted",
|
||||||
|
followedActorIri: `http://localhost:3000/users/${USERNAME}`,
|
||||||
|
followedUserId: userId,
|
||||||
|
acceptedAt: new Date(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: randomUUID(),
|
||||||
|
followerActorIri: "https://other.example/users/pending",
|
||||||
|
followedActorIri: `http://localhost:3000/users/${USERNAME}`,
|
||||||
|
followedUserId: userId,
|
||||||
|
acceptedAt: null,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const audience = await listAcceptedRemoteFollowers(userId);
|
||||||
|
expect(audience).toEqual(["https://other.example/users/accepted"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
44
apps/journal/app/lib/federation-outbox.server.ts
Normal file
44
apps/journal/app/lib/federation-outbox.server.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Outbox listing queries (spec 5.1). Offset-paged, public-only —
|
||||||
|
// `unlisted` and `private` never federate. Separate from
|
||||||
|
// activities.server.ts because the outbox needs raw rows (no geojson
|
||||||
|
// batching) in a stable reverse-chronological order keyed on createdAt.
|
||||||
|
|
||||||
|
import { and, count, desc, eq } from "drizzle-orm";
|
||||||
|
import { activities } from "@trails-cool/db/schema/journal";
|
||||||
|
import { getDb } from "./db.ts";
|
||||||
|
import type { FederatableActivity } from "./federation-objects.server.ts";
|
||||||
|
|
||||||
|
export const OUTBOX_PAGE_SIZE = 20;
|
||||||
|
|
||||||
|
export async function listPublicActivitiesPage(
|
||||||
|
ownerId: string,
|
||||||
|
offset: number,
|
||||||
|
limit: number,
|
||||||
|
): Promise<FederatableActivity[]> {
|
||||||
|
const db = getDb();
|
||||||
|
return db
|
||||||
|
.select({
|
||||||
|
id: activities.id,
|
||||||
|
name: activities.name,
|
||||||
|
description: activities.description,
|
||||||
|
distance: activities.distance,
|
||||||
|
elevationGain: activities.elevationGain,
|
||||||
|
duration: activities.duration,
|
||||||
|
startedAt: activities.startedAt,
|
||||||
|
createdAt: activities.createdAt,
|
||||||
|
})
|
||||||
|
.from(activities)
|
||||||
|
.where(and(eq(activities.ownerId, ownerId), eq(activities.visibility, "public")))
|
||||||
|
.orderBy(desc(activities.createdAt))
|
||||||
|
.offset(offset)
|
||||||
|
.limit(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function countPublicActivities(ownerId: string): Promise<number> {
|
||||||
|
const db = getDb();
|
||||||
|
const [row] = await db
|
||||||
|
.select({ n: count() })
|
||||||
|
.from(activities)
|
||||||
|
.where(and(eq(activities.ownerId, ownerId), eq(activities.visibility, "public")));
|
||||||
|
return row?.n ?? 0;
|
||||||
|
}
|
||||||
|
|
@ -34,6 +34,12 @@ import { getOrigin } from "./config.server.ts";
|
||||||
import { localActorIri } from "./actor-iri.ts";
|
import { localActorIri } from "./actor-iri.ts";
|
||||||
import { PostgresKvStore } from "./federation-kv.server.ts";
|
import { PostgresKvStore } from "./federation-kv.server.ts";
|
||||||
import { ensureUserKeypair, loadUserKeypair } from "./federation-keys.server.ts";
|
import { ensureUserKeypair, loadUserKeypair } from "./federation-keys.server.ts";
|
||||||
|
import { activityToCreate } from "./federation-objects.server.ts";
|
||||||
|
import {
|
||||||
|
OUTBOX_PAGE_SIZE,
|
||||||
|
countPublicActivities,
|
||||||
|
listPublicActivitiesPage,
|
||||||
|
} from "./federation-outbox.server.ts";
|
||||||
import {
|
import {
|
||||||
recordRemoteFollow,
|
recordRemoteFollow,
|
||||||
removeRemoteFollow,
|
removeRemoteFollow,
|
||||||
|
|
@ -117,6 +123,7 @@ function buildFederation(): Federation<void> {
|
||||||
// declared explicitly so AP clients link browsers to HTML.
|
// declared explicitly so AP clients link browsers to HTML.
|
||||||
url: new URL(localActorIri(identifier)),
|
url: new URL(localActorIri(identifier)),
|
||||||
inbox: ctx.getInboxUri(identifier),
|
inbox: ctx.getInboxUri(identifier),
|
||||||
|
outbox: ctx.getOutboxUri(identifier),
|
||||||
// Public keys for inbound HTTP-Signature verification by
|
// Public keys for inbound HTTP-Signature verification by
|
||||||
// remotes (Mastodon reads publicKey; newer stacks read the
|
// remotes (Mastodon reads publicKey; newer stacks read the
|
||||||
// multikey assertionMethods).
|
// multikey assertionMethods).
|
||||||
|
|
@ -138,6 +145,30 @@ function buildFederation(): Federation<void> {
|
||||||
return pair ? [pair] : [];
|
return pair ? [pair] : [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Outbox (spec 5.1/5.2): paginated OrderedCollection of the user's
|
||||||
|
// public activities as Create(Note). Unsigned and signed fetches see
|
||||||
|
// the same content — the outbox only ever contains public items, so
|
||||||
|
// Authorized Fetch adds nothing until locked accounts exist (the
|
||||||
|
// spec's two scenarios deliberately collapse to one shape).
|
||||||
|
federation
|
||||||
|
.setOutboxDispatcher("/users/{identifier}/outbox", async (_ctx, identifier, cursor) => {
|
||||||
|
const user = await findUserByUsername(identifier);
|
||||||
|
if (!user || user.profileVisibility !== "public") return null;
|
||||||
|
const offset = cursor === null ? 0 : Number.parseInt(cursor, 10);
|
||||||
|
if (Number.isNaN(offset) || offset < 0) return null;
|
||||||
|
const page = await listPublicActivitiesPage(user.id, offset, OUTBOX_PAGE_SIZE);
|
||||||
|
return {
|
||||||
|
items: page.map((a) => activityToCreate(a, identifier)),
|
||||||
|
nextCursor: page.length === OUTBOX_PAGE_SIZE ? String(offset + OUTBOX_PAGE_SIZE) : null,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.setCounter(async (_ctx, identifier) => {
|
||||||
|
const user = await findUserByUsername(identifier);
|
||||||
|
if (!user || user.profileVisibility !== "public") return null;
|
||||||
|
return countPublicActivities(user.id);
|
||||||
|
})
|
||||||
|
.setFirstCursor(() => "0");
|
||||||
|
|
||||||
federation
|
federation
|
||||||
.setInboxListeners("/users/{identifier}/inbox")
|
.setInboxListeners("/users/{identifier}/inbox")
|
||||||
.on(Follow, async (ctx, follow) => {
|
.on(Follow, async (ctx, follow) => {
|
||||||
|
|
@ -224,6 +255,18 @@ export function getFederation(): Federation<void> {
|
||||||
return _federation;
|
return _federation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this username a local user that federates (exists + public)?
|
||||||
|
* Route-level gate for federation surfaces whose collection-level
|
||||||
|
* responses Fedify builds without consulting the page dispatcher
|
||||||
|
* (e.g. the outbox OrderedCollection) — private users must 404
|
||||||
|
* everywhere, mirroring the actor object.
|
||||||
|
*/
|
||||||
|
export async function isFederatableUser(username: string): Promise<boolean> {
|
||||||
|
const user = await findUserByUsername(username);
|
||||||
|
return user !== null && user.profileVisibility === "public";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate a request to Fedify's URL dispatcher. 404s when the feature
|
* Delegate a request to Fedify's URL dispatcher. 404s when the feature
|
||||||
* flag is off so disabled instances are indistinguishable from instances
|
* flag is off so disabled instances are indistinguishable from instances
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ export default [
|
||||||
route(".well-known/nodeinfo", "routes/api.well-known.nodeinfo.ts"),
|
route(".well-known/nodeinfo", "routes/api.well-known.nodeinfo.ts"),
|
||||||
route("nodeinfo/2.1", "routes/api.nodeinfo.ts"),
|
route("nodeinfo/2.1", "routes/api.nodeinfo.ts"),
|
||||||
route("users/:username/inbox", "routes/users.$username.inbox.ts"),
|
route("users/:username/inbox", "routes/users.$username.inbox.ts"),
|
||||||
|
route("users/:username/outbox", "routes/users.$username.outbox.ts"),
|
||||||
route("oauth/authorize", "routes/oauth.authorize.tsx"),
|
route("oauth/authorize", "routes/oauth.authorize.tsx"),
|
||||||
route("oauth/token", "routes/oauth.token.ts"),
|
route("oauth/token", "routes/oauth.token.ts"),
|
||||||
route("auth/register", "routes/auth.register.tsx"),
|
route("auth/register", "routes/auth.register.tsx"),
|
||||||
|
|
|
||||||
22
apps/journal/app/routes/users.$username.outbox.ts
Normal file
22
apps/journal/app/routes/users.$username.outbox.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import type { Route } from "./+types/users.$username.outbox";
|
||||||
|
import {
|
||||||
|
federationEnabled,
|
||||||
|
handleFederationRequest,
|
||||||
|
isFederatableUser,
|
||||||
|
} from "~/lib/federation.server";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /users/:username/outbox — paginated OrderedCollection of the
|
||||||
|
* user's public activities as Create(Note) (spec: social-federation,
|
||||||
|
* "Outbox publishes user's public activities"). Served by Fedify's
|
||||||
|
* collection dispatcher; 404s for private users (checked here because
|
||||||
|
* Fedify builds the collection-level response from counter/cursors
|
||||||
|
* without consulting the page dispatcher) and while FEDERATION_ENABLED
|
||||||
|
* is off.
|
||||||
|
*/
|
||||||
|
export async function loader({ request, params }: Route.LoaderArgs) {
|
||||||
|
if (!federationEnabled() || !(await isFederatableUser(params.username))) {
|
||||||
|
return new Response("Not Found", { status: 404 });
|
||||||
|
}
|
||||||
|
return handleFederationRequest(request);
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fedify/fedify": "2.1.16",
|
"@fedify/fedify": "2.1.16",
|
||||||
|
"@js-temporal/polyfill": "^0.5.1",
|
||||||
"@react-router/node": "catalog:",
|
"@react-router/node": "catalog:",
|
||||||
"@react-router/serve": "catalog:",
|
"@react-router/serve": "catalog:",
|
||||||
"@sentry/node": "catalog:",
|
"@sentry/node": "catalog:",
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,8 @@ server.listen(port, async () => {
|
||||||
if (process.env.FEDERATION_ENABLED === "true") {
|
if (process.env.FEDERATION_ENABLED === "true") {
|
||||||
const { backfillUserKeypairsJob } = await import("./app/jobs/backfill-user-keypairs.ts");
|
const { backfillUserKeypairsJob } = await import("./app/jobs/backfill-user-keypairs.ts");
|
||||||
const { federationKvSweepJob } = await import("./app/jobs/federation-kv-sweep.ts");
|
const { federationKvSweepJob } = await import("./app/jobs/federation-kv-sweep.ts");
|
||||||
jobs.push(backfillUserKeypairsJob, federationKvSweepJob);
|
const { deliverActivityJob } = await import("./app/jobs/deliver-activity.ts");
|
||||||
|
jobs.push(backfillUserKeypairsJob, federationKvSweepJob, deliverActivityJob);
|
||||||
}
|
}
|
||||||
|
|
||||||
const boss = createBoss(getDatabaseUrl());
|
const boss = createBoss(getDatabaseUrl());
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,17 @@ Inbound signature verification uses the actor's public key from their actor obje
|
||||||
with `/.well-known/trails-cool` as the secondary signal.
|
with `/.well-known/trails-cool` as the secondary signal.
|
||||||
- **Fedify's KvStore is Postgres-backed** (`journal.federation_kv`) so inbox
|
- **Fedify's KvStore is Postgres-backed** (`journal.federation_kv`) so inbox
|
||||||
replay protection and document caches survive restarts; swept daily.
|
replay protection and document caches survive restarts; swept daily.
|
||||||
|
- **Outgoing activity shape: `Create(Note)` with PropertyValue attachments**
|
||||||
|
(task 5.1; resolves the open question below toward Mastodon compat). The
|
||||||
|
Note's HTML content carries name/description/stats plus a link to the
|
||||||
|
activity page; distance/elevation/duration ride along as `PropertyValue`
|
||||||
|
attachments that Mastodon ignores gracefully and trails consumers can read
|
||||||
|
without HTML parsing. GPX download links can join once activities have a
|
||||||
|
public GPX endpoint.
|
||||||
|
- **Private-user 404 for collection endpoints is enforced at the route layer**
|
||||||
|
(task 5.1): Fedify builds collection-level responses (outbox
|
||||||
|
OrderedCollection) from counter/cursors without consulting the page
|
||||||
|
dispatcher, so the dispatcher's `null` → 404 contract doesn't cover them.
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,14 @@
|
||||||
|
|
||||||
## 5. Outbox + push delivery
|
## 5. Outbox + push delivery
|
||||||
|
|
||||||
- [ ] 5.1 Outbox endpoint at `/users/:username/outbox`. Returns paginated `OrderedCollection` of the user's `public` activities as `Create(Note)` (with structured trails-specific metadata in `attachment` so Mastodon shows text + GPX link, and trails consumers can read distance/elevation)
|
- [x] 5.1 Outbox endpoint at `/users/:username/outbox`. Returns paginated `OrderedCollection` of the user's `public` activities as `Create(Note)` (with structured trails-specific metadata in `attachment` so Mastodon shows text + GPX link, and trails consumers can read distance/elevation)
|
||||||
- [ ] 5.2 Honor signed-fetch (Authorized Fetch) on outbox GETs. Unsigned requests get a public-only subset; signed requests get the same (locked accounts is later-change territory)
|
- [x] 5.2 Honor signed-fetch (Authorized Fetch) on outbox GETs. Unsigned requests get a public-only subset; signed requests get the same (locked accounts is later-change territory)
|
||||||
- [ ] 5.3 On local activity create with `visibility = 'public'`, enqueue a `deliver-activity` pg-boss job per accepted remote follower
|
> The two scenarios collapse to one shape — the outbox only ever contains public items, so no signature check is needed until locked accounts exist. Documented in code.
|
||||||
- [ ] 5.4 `deliver-activity` job: HTTP-sign + POST `Create(Note)` to follower inbox; retry with exponential backoff on 5xx; permanent-fail after retry budget; log final outcome
|
- [x] 5.3 On local activity create with `visibility = 'public'`, enqueue a `deliver-activity` pg-boss job per accepted remote follower
|
||||||
- [ ] 5.5 Per-remote-host rate limit on outbound: 1 req/sec per host
|
- [x] 5.4 `deliver-activity` job: HTTP-sign + POST `Create(Note)` to follower inbox; retry with exponential backoff on 5xx; permanent-fail after retry budget; log final outcome
|
||||||
- [ ] 5.6 On local activity update or delete, enqueue corresponding `Update`/`Delete` activities to followers (basic — full update/delete fan-out)
|
- [x] 5.5 Per-remote-host rate limit on outbound: 1 req/sec per host
|
||||||
|
- [x] 5.6 On local activity update or delete, enqueue corresponding `Update`/`Delete` activities to followers (basic — full update/delete fan-out)
|
||||||
|
> Implemented as: visibility flip → public sends Create; flip away from public or hard delete sends Delete(Tombstone). There is no general field-edit server path today, so `Update` has no trigger yet — revisit if/when activity editing ships.
|
||||||
|
|
||||||
## 6. Outbound follow + trails-to-trails check
|
## 6. Outbound follow + trails-to-trails check
|
||||||
|
|
||||||
|
|
|
||||||
196
pnpm-lock.yaml
generated
196
pnpm-lock.yaml
generated
|
|
@ -206,6 +206,9 @@ importers:
|
||||||
'@fedify/fedify':
|
'@fedify/fedify':
|
||||||
specifier: 2.1.16
|
specifier: 2.1.16
|
||||||
version: 2.1.16
|
version: 2.1.16
|
||||||
|
'@js-temporal/polyfill':
|
||||||
|
specifier: ^0.5.1
|
||||||
|
version: 0.5.1
|
||||||
'@react-router/node':
|
'@react-router/node':
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 7.16.0(react-router@7.16.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@6.0.3)
|
version: 7.16.0(react-router@7.16.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@6.0.3)
|
||||||
|
|
@ -9588,7 +9591,7 @@ snapshots:
|
||||||
connect: 3.7.0
|
connect: 3.7.0
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
dnssd-advertise: 1.1.4
|
dnssd-advertise: 1.1.4
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
expo-server: 56.0.4
|
expo-server: 56.0.4
|
||||||
fetch-nodeshim: 0.4.10
|
fetch-nodeshim: 0.4.10
|
||||||
getenv: 2.0.0
|
getenv: 2.0.0
|
||||||
|
|
@ -9614,7 +9617,7 @@ snapshots:
|
||||||
ws: 8.21.0
|
ws: 8.21.0
|
||||||
zod: 3.25.76
|
zod: 3.25.76
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
expo-router: 56.2.8(8f2951496f8771d7ce04c6a782b8e488)
|
expo-router: 56.2.8(8a75af1e83efd87b396be933266b8559)
|
||||||
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@expo/dom-webview'
|
- '@expo/dom-webview'
|
||||||
|
|
@ -9735,7 +9738,7 @@ snapshots:
|
||||||
|
|
||||||
'@expo/dom-webview@55.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)':
|
'@expo/dom-webview@55.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
|
||||||
|
|
@ -9871,7 +9874,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/dom-webview': 55.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
'@expo/dom-webview': 55.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
anser: 1.4.10
|
anser: 1.4.10
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
stacktrace-parser: 0.1.11
|
stacktrace-parser: 0.1.11
|
||||||
|
|
@ -9932,7 +9935,7 @@ snapshots:
|
||||||
postcss: 8.5.15
|
postcss: 8.5.15
|
||||||
resolve-from: 5.0.0
|
resolve-from: 5.0.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
@ -9970,7 +9973,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/log-box': 56.0.12(@expo/dom-webview@55.0.5)(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
'@expo/log-box': 56.0.12(@expo/dom-webview@55.0.5)(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
anser: 1.4.10
|
anser: 1.4.10
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
pretty-format: 29.7.0
|
pretty-format: 29.7.0
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
|
@ -10117,14 +10120,14 @@ snapshots:
|
||||||
'@expo/router-server@56.0.11(@expo/metro-runtime@56.0.13)(expo-constants@56.0.16)(expo-font@56.0.5)(expo-router@56.2.8)(expo-server@56.0.4)(expo@56.0.4)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
|
'@expo/router-server@56.0.11(@expo/metro-runtime@56.0.13)(expo-constants@56.0.16)(expo-font@56.0.5)(expo-router@56.2.8)(expo-server@56.0.4)(expo@56.0.4)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
expo-constants: 56.0.16(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))
|
expo-constants: 56.0.16(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))
|
||||||
expo-font: 56.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
expo-font: 56.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
expo-server: 56.0.4
|
expo-server: 56.0.4
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@expo/metro-runtime': 56.0.13(@expo/log-box@56.0.12)(expo@56.0.4)(react-dom@19.2.6(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
'@expo/metro-runtime': 56.0.13(@expo/log-box@56.0.12)(expo@56.0.4)(react-dom@19.2.6(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
expo-router: 56.2.8(8f2951496f8771d7ce04c6a782b8e488)
|
expo-router: 56.2.8(8a75af1e83efd87b396be933266b8559)
|
||||||
react-dom: 19.2.6(react@19.2.6)
|
react-dom: 19.2.6(react@19.2.6)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
@ -10157,6 +10160,23 @@ snapshots:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
- '@types/react-dom'
|
- '@types/react-dom'
|
||||||
|
|
||||||
|
'@expo/ui@56.0.15(27b64df329b3e7add338555fbe0e3ac9)':
|
||||||
|
dependencies:
|
||||||
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
|
react: 19.2.6
|
||||||
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
sf-symbols-typescript: 2.2.0
|
||||||
|
vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
|
||||||
|
optionalDependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
react-dom: 19.2.6(react@19.2.6)
|
||||||
|
react-native-reanimated: 4.4.0(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
react-native-worklets: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/react'
|
||||||
|
- '@types/react-dom'
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@expo/ui@56.0.15(@babel/core@7.29.7)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(expo@55.0.26)(react-dom@19.2.6(react@19.2.6))(react-native-reanimated@4.4.0(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)':
|
'@expo/ui@56.0.15(@babel/core@7.29.7)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(expo@55.0.26)(react-dom@19.2.6(react@19.2.6))(react-native-reanimated@4.4.0(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
expo: 55.0.26(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.11)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 55.0.26(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.11)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
|
|
@ -12875,7 +12895,7 @@ snapshots:
|
||||||
react-refresh: 0.14.2
|
react-refresh: 0.14.2
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@babel/runtime': 7.29.7
|
'@babel/runtime': 7.29.7
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
@ -13713,7 +13733,7 @@ snapshots:
|
||||||
expo-asset@56.0.14(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3):
|
expo-asset@56.0.14(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/image-utils': 0.10.1(typescript@6.0.3)
|
'@expo/image-utils': 0.10.1(typescript@6.0.3)
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
expo-constants: 56.0.16(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))
|
expo-constants: 56.0.16(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
|
@ -13741,7 +13761,7 @@ snapshots:
|
||||||
expo-constants@56.0.16(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)):
|
expo-constants@56.0.16(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/env': 2.3.0
|
'@expo/env': 2.3.0
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
@ -13791,7 +13811,7 @@ snapshots:
|
||||||
|
|
||||||
expo-file-system@56.0.7(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)):
|
expo-file-system@56.0.7(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)):
|
||||||
dependencies:
|
dependencies:
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
|
||||||
expo-font@55.0.8(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
expo-font@55.0.8(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
|
|
@ -13803,7 +13823,7 @@ snapshots:
|
||||||
|
|
||||||
expo-font@56.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
expo-font@56.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
fontfaceobserver: 2.3.0
|
fontfaceobserver: 2.3.0
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
|
@ -13817,7 +13837,7 @@ snapshots:
|
||||||
|
|
||||||
expo-glass-effect@56.0.4(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
expo-glass-effect@56.0.4(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
|
||||||
|
|
@ -13830,7 +13850,7 @@ snapshots:
|
||||||
|
|
||||||
expo-keep-awake@56.0.3(expo@56.0.4)(react@19.2.6):
|
expo-keep-awake@56.0.3(expo@56.0.4)(react@19.2.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
|
|
||||||
expo-linking@56.0.13(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
expo-linking@56.0.13(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
|
|
@ -13901,6 +13921,16 @@ snapshots:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
react-native-worklets: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
react-native-worklets: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
|
||||||
|
expo-modules-core@56.0.12(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
|
dependencies:
|
||||||
|
'@expo/expo-modules-macros-plugin': 0.0.9
|
||||||
|
expo-modules-jsi: 56.0.7(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))
|
||||||
|
invariant: 2.2.4
|
||||||
|
react: 19.2.6
|
||||||
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
optionalDependencies:
|
||||||
|
react-native-worklets: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
|
||||||
expo-modules-core@56.0.12(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
expo-modules-core@56.0.12(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/expo-modules-macros-plugin': 0.0.9
|
'@expo/expo-modules-macros-plugin': 0.0.9
|
||||||
|
|
@ -13938,6 +13968,57 @@ snapshots:
|
||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
|
expo-router@56.2.8(8a75af1e83efd87b396be933266b8559):
|
||||||
|
dependencies:
|
||||||
|
'@expo/log-box': 56.0.12(@expo/dom-webview@55.0.5)(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
'@expo/metro-runtime': 56.0.13(@expo/log-box@56.0.12)(expo@56.0.4)(react-dom@19.2.6(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
'@expo/schema-utils': 56.0.1
|
||||||
|
'@expo/ui': 56.0.15(27b64df329b3e7add338555fbe0e3ac9)
|
||||||
|
'@radix-ui/react-slot': 1.2.4(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
'@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
|
||||||
|
'@react-native-masked-view/masked-view': 0.3.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
'@testing-library/jest-dom': 6.9.1
|
||||||
|
'@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1)
|
||||||
|
client-only: 0.0.1
|
||||||
|
color: 4.2.3
|
||||||
|
debug: 4.4.3
|
||||||
|
escape-string-regexp: 4.0.0
|
||||||
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
|
expo-constants: 56.0.16(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))
|
||||||
|
expo-glass-effect: 56.0.4(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
expo-linking: 56.0.13(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
expo-server: 56.0.4
|
||||||
|
expo-symbols: 56.0.5(expo-font@56.0.5)(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
invariant: 2.2.4
|
||||||
|
nanoid: 3.3.12
|
||||||
|
query-string: 7.1.3
|
||||||
|
react: 19.2.6
|
||||||
|
react-fast-compare: 3.2.2
|
||||||
|
react-is: 19.2.6
|
||||||
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
react-native-drawer-layout: 4.2.4(7fc434303659a0f47c7bdf85df049415)
|
||||||
|
react-native-safe-area-context: 5.8.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
react-native-screens: 4.25.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
server-only: 0.0.1
|
||||||
|
sf-symbols-typescript: 2.2.0
|
||||||
|
shallowequal: 1.1.0
|
||||||
|
vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
|
||||||
|
optionalDependencies:
|
||||||
|
'@testing-library/react-native': 13.3.3(jest@29.7.0(@types/node@25.9.1))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react-test-renderer@19.2.6(react@19.2.6))(react@19.2.6)
|
||||||
|
react-dom: 19.2.6(react@19.2.6)
|
||||||
|
react-native-gesture-handler: 2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
react-native-reanimated: 4.4.0(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@babel/core'
|
||||||
|
- '@testing-library/dom'
|
||||||
|
- '@types/react'
|
||||||
|
- '@types/react-dom'
|
||||||
|
- expo-font
|
||||||
|
- react-native-worklets
|
||||||
|
- supports-color
|
||||||
|
optional: true
|
||||||
|
|
||||||
expo-router@56.2.8(8f2951496f8771d7ce04c6a782b8e488):
|
expo-router@56.2.8(8f2951496f8771d7ce04c6a782b8e488):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/log-box': 56.0.12(@expo/dom-webview@55.0.5)(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
'@expo/log-box': 56.0.12(@expo/dom-webview@55.0.5)(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
|
@ -14068,7 +14149,7 @@ snapshots:
|
||||||
expo-sqlite@56.0.4(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
expo-sqlite@56.0.4(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
await-lock: 2.2.2
|
await-lock: 2.2.2
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
|
||||||
|
|
@ -14091,7 +14172,7 @@ snapshots:
|
||||||
expo-symbols@56.0.5(expo-font@56.0.5)(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
expo-symbols@56.0.5(expo-font@56.0.5)(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo-google-fonts/material-symbols': 0.4.38
|
'@expo-google-fonts/material-symbols': 0.4.38
|
||||||
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
expo: 56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
expo-font: 56.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
expo-font: 56.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
|
@ -14157,6 +14238,47 @@ snapshots:
|
||||||
- typescript
|
- typescript
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
|
||||||
|
expo@56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3):
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.29.7
|
||||||
|
'@expo/cli': 56.1.11(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-constants@56.0.16)(expo-font@56.0.5)(expo-router@56.2.8)(expo@56.0.4)(react-dom@19.2.6(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
|
'@expo/config': 56.0.9(typescript@6.0.3)
|
||||||
|
'@expo/config-plugins': 56.0.8(typescript@6.0.3)
|
||||||
|
'@expo/devtools': 56.0.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
'@expo/fingerprint': 0.19.3
|
||||||
|
'@expo/local-build-cache-provider': 56.0.7(typescript@6.0.3)
|
||||||
|
'@expo/log-box': 56.0.12(@expo/dom-webview@55.0.5)(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
'@expo/metro': 56.0.0
|
||||||
|
'@expo/metro-config': 56.0.12(expo@56.0.4)(typescript@6.0.3)
|
||||||
|
'@ungap/structured-clone': 1.3.1
|
||||||
|
babel-preset-expo: 56.0.12(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@56.0.4)(react-refresh@0.14.2)
|
||||||
|
expo-asset: 56.0.14(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3)
|
||||||
|
expo-constants: 56.0.16(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))
|
||||||
|
expo-file-system: 56.0.7(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))
|
||||||
|
expo-font: 56.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
expo-keep-awake: 56.0.3(expo@56.0.4)(react@19.2.6)
|
||||||
|
expo-modules-autolinking: 56.0.12(typescript@6.0.3)
|
||||||
|
expo-modules-core: 56.0.12(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
react: 19.2.6
|
||||||
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
react-refresh: 0.14.2
|
||||||
|
whatwg-url-minimum: 0.1.2
|
||||||
|
optionalDependencies:
|
||||||
|
'@expo/dom-webview': 55.0.5(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
'@expo/metro-runtime': 56.0.13(@expo/log-box@56.0.12)(expo@56.0.4)(react-dom@19.2.6(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
react-dom: 19.2.6(react@19.2.6)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@babel/core'
|
||||||
|
- bufferutil
|
||||||
|
- expo-router
|
||||||
|
- expo-widgets
|
||||||
|
- react-native-worklets
|
||||||
|
- react-server-dom-webpack
|
||||||
|
- supports-color
|
||||||
|
- typescript
|
||||||
|
- utf-8-validate
|
||||||
|
|
||||||
expo@56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3):
|
expo@56.0.4(@babel/core@7.29.7)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@56.0.13)(expo-router@56.2.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.29.7
|
'@babel/runtime': 7.29.7
|
||||||
|
|
@ -16312,6 +16434,16 @@ snapshots:
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
react-dom: 19.2.6(react@19.2.6)
|
react-dom: 19.2.6(react@19.2.6)
|
||||||
|
|
||||||
|
react-native-drawer-layout@4.2.4(7fc434303659a0f47c7bdf85df049415):
|
||||||
|
dependencies:
|
||||||
|
color: 4.2.3
|
||||||
|
react: 19.2.6
|
||||||
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
react-native-gesture-handler: 2.31.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
react-native-reanimated: 4.4.0(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
use-latest-callback: 0.2.6(react@19.2.6)
|
||||||
|
optional: true
|
||||||
|
|
||||||
react-native-drawer-layout@4.2.4(c0e69a3071d879bebf456eb4f44ccb1f):
|
react-native-drawer-layout@4.2.4(c0e69a3071d879bebf456eb4f44ccb1f):
|
||||||
dependencies:
|
dependencies:
|
||||||
color: 4.2.3
|
color: 4.2.3
|
||||||
|
|
@ -16370,6 +16502,15 @@ snapshots:
|
||||||
semver: 7.8.1
|
semver: 7.8.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
react-native-reanimated@4.4.0(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
|
dependencies:
|
||||||
|
react: 19.2.6
|
||||||
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
react-native-is-edge-to-edge: 1.3.1(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
react-native-worklets: 0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)
|
||||||
|
semver: 7.8.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
react-native-reanimated@4.4.0(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
react-native-reanimated@4.4.0(react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.2.6
|
react: 19.2.6
|
||||||
|
|
@ -16425,6 +16566,27 @@ snapshots:
|
||||||
- supports-color
|
- supports-color
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/preset-typescript': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@react-native/metro-config': 0.85.3(@babel/core@7.29.7)
|
||||||
|
convert-source-map: 2.0.0
|
||||||
|
react: 19.2.6
|
||||||
|
react-native: 0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6)
|
||||||
|
semver: 7.8.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
optional: true
|
||||||
|
|
||||||
react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
react-native-worklets@0.9.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.29.7
|
'@babel/core': 7.29.7
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue