infra: add linting and formatting for js projects (#230)

* wip

* wip

* wip

* format

* wip(mobile): lint and format

* cleanup

* nits

* nits

* idiomatic react

* nit

* format all root files
This commit was merged in pull request #230.
This commit is contained in:
Arjun Patel
2026-06-02 07:44:24 -07:00
committed by GitHub
parent 2fe562ce2b
commit a8a0b7db1b
258 changed files with 7822 additions and 5195 deletions
+38 -41
View File
@@ -1,6 +1,6 @@
import { appConfig } from "@/config/env";
import { ApiError } from "@/lib/errors";
import type { z } from "zod";
import { appConfig } from '@/config/env';
import { ApiError } from '@/lib/errors';
import type { z } from 'zod';
import {
BillingStatusSchema,
CheckoutSessionResponseSchema,
@@ -15,7 +15,7 @@ import {
PortalSessionResponseSchema,
PrepareUploadResponseSchema,
SignInResponseSchema,
} from "./types";
} from './types';
import type {
AcceptInvitationRequest,
AddMembersRequest,
@@ -25,7 +25,7 @@ import type {
RequestCodeRequest,
RevokeInvitationRequest,
SignInRequest,
} from "./types";
} from './types';
/**
* HTTP transport for Orion. Holds the bearer token as private state — the auth
@@ -51,11 +51,11 @@ class ApiClient {
const headers: Record<string, string> = {};
if (body) {
headers["Content-Type"] = "application/json";
headers['Content-Type'] = 'application/json';
}
if (this.token) {
headers["Authorization"] = `Bearer ${this.token}`;
headers['Authorization'] = `Bearer ${this.token}`;
}
const response = await fetch(`${this.baseUrl}${path}`, {
@@ -65,11 +65,11 @@ class ApiClient {
});
if (response.status === 401) {
throw new ApiError(401, "Unauthorized");
throw new ApiError(401, 'Unauthorized');
}
if (!response.ok) {
const text = await response.text().catch(() => "Unknown error");
const text = await response.text().catch(() => 'Unknown error');
throw new ApiError(response.status, text);
}
@@ -98,35 +98,32 @@ class ApiClient {
// --- Auth ---
async requestCode(data: RequestCodeRequest): Promise<void> {
await this.requestVoid("POST", "/auth/request-code", data);
await this.requestVoid('POST', '/auth/request-code', data);
}
async signIn(data: SignInRequest) {
return this.request(SignInResponseSchema, "POST", "/auth/sign-in", data);
return this.request(SignInResponseSchema, 'POST', '/auth/sign-in', data);
}
async me() {
return this.request(HumanSchema, "GET", "/auth/me");
return this.request(HumanSchema, 'GET', '/auth/me');
}
async signOut(): Promise<void> {
await this.requestVoid("POST", "/auth/sign-out");
await this.requestVoid('POST', '/auth/sign-out');
}
async getFirebaseToken() {
return this.request(
FirebaseTokenResponseSchema,
"POST",
"/auth/firebase-token",
'POST',
'/auth/firebase-token',
);
}
// TODO: security: require passing in the particle id once api deprecates this
async getParticleDownloadUrl(objectId: string): Promise<string> {
const response = await this.fetch(
"GET",
`/particles/${objectId}/download`,
);
const response = await this.fetch('GET', `/particles/${objectId}/download`);
const data = await response.json();
return data.url;
}
@@ -136,21 +133,21 @@ class ApiClient {
async updateSettings(data: {
email_notifications_enabled?: boolean;
}): Promise<void> {
await this.requestVoid("PATCH", "/humans/me/settings", data);
await this.requestVoid('PATCH', '/humans/me/settings', data);
}
// --- Push notification tokens ---
async registerPushToken(data: {
token: string;
platform: "ios" | "android";
platform: 'ios' | 'android';
app_version: string;
}): Promise<void> {
await this.requestVoid("POST", "/humans/me/push-tokens", data);
await this.requestVoid('POST', '/humans/me/push-tokens', data);
}
async unregisterPushToken(token: string): Promise<void> {
await this.requestVoid("DELETE", "/humans/me/push-tokens", { token });
await this.requestVoid('DELETE', '/humans/me/push-tokens', { token });
}
// --- Depot ---
@@ -158,8 +155,8 @@ class ApiClient {
async prepareUpload(data: PrepareUploadRequest) {
return this.request(
PrepareUploadResponseSchema,
"POST",
"/depot/upload",
'POST',
'/depot/upload',
data,
);
}
@@ -167,7 +164,7 @@ class ApiClient {
async confirmUpload(objectId: string) {
return this.request(
DepotObjectSchema,
"POST",
'POST',
`/depot/objects/${objectId}/confirm`,
);
}
@@ -175,24 +172,24 @@ class ApiClient {
// --- Networks ---
async listNetworks() {
return this.request(ListNetworksResponseSchema, "GET", "/networks");
return this.request(ListNetworksResponseSchema, 'GET', '/networks');
}
async createNetwork(data: CreateNetworkRequest) {
return this.request(NetworkSchema, "POST", "/networks", data);
return this.request(NetworkSchema, 'POST', '/networks', data);
}
async getNetwork(id: string) {
return this.request(NetworkSchema, "GET", `/networks/${id}`);
return this.request(NetworkSchema, 'GET', `/networks/${id}`);
}
async addMembers(networkId: string, data: AddMembersRequest): Promise<void> {
await this.requestVoid("POST", `/networks/${networkId}/members`, data);
await this.requestVoid('POST', `/networks/${networkId}/members`, data);
}
async removeMember(networkId: string, humanId: string): Promise<void> {
await this.requestVoid(
"DELETE",
'DELETE',
`/networks/${networkId}/members/${humanId}`,
);
}
@@ -202,17 +199,17 @@ class ApiClient {
async listNetworkInvitations(networkId: string) {
return this.request(
ListInvitationsResponseSchema,
"GET",
'GET',
`/networks/${networkId}/invitations`,
);
}
async listMyInvitations() {
return this.request(ListInvitationsResponseSchema, "GET", "/invitations");
return this.request(ListInvitationsResponseSchema, 'GET', '/invitations');
}
async acceptInvitation(data: AcceptInvitationRequest): Promise<void> {
await this.requestVoid("POST", "/invitations/accept", data);
await this.requestVoid('POST', '/invitations/accept', data);
}
async revokeInvitation(
@@ -220,7 +217,7 @@ class ApiClient {
data: RevokeInvitationRequest,
): Promise<void> {
await this.requestVoid(
"DELETE",
'DELETE',
`/networks/${networkId}/invitations`,
data,
);
@@ -231,8 +228,8 @@ class ApiClient {
async getLivekitToken(networkId: string, streamId: string) {
return this.request(
GetLivekitTokenResponseSchema,
"POST",
"/livekit/token",
'POST',
'/livekit/token',
{ network_id: networkId, stream_id: streamId },
);
}
@@ -242,7 +239,7 @@ class ApiClient {
async getNetworkBilling(networkId: string) {
return this.request(
BillingStatusSchema,
"GET",
'GET',
`/networks/${networkId}/billing`,
);
}
@@ -250,7 +247,7 @@ class ApiClient {
async createCheckoutSession(networkId: string, cadence: BillingCadence) {
return this.request(
CheckoutSessionResponseSchema,
"POST",
'POST',
`/networks/${networkId}/billing/checkout-session`,
{ cadence },
);
@@ -259,7 +256,7 @@ class ApiClient {
async createPortalSession(networkId: string) {
return this.request(
PortalSessionResponseSchema,
"POST",
'POST',
`/networks/${networkId}/billing/portal-session`,
);
}
@@ -267,7 +264,7 @@ class ApiClient {
async getNetworkUsage(networkId: string) {
return this.request(
NetworkUsageSchema,
"GET",
'GET',
`/networks/${networkId}/usage`,
);
}
+72 -32
View File
@@ -1,4 +1,4 @@
import { z } from "zod";
import { z } from 'zod';
export const HumanSchema = z.object({
id: z.string(),
@@ -25,12 +25,12 @@ export type ListNetworksResponse = z.infer<typeof ListNetworksResponseSchema>;
// --- Network request/response types ---
const CreateNetworkRequestSchema = z.object({
export const CreateNetworkRequestSchema = z.object({
name: z.string(),
});
export type CreateNetworkRequest = z.infer<typeof CreateNetworkRequestSchema>;
const AddMembersRequestSchema = z.object({
export const AddMembersRequestSchema = z.object({
email_addresses: z.array(z.string().email()),
});
export type AddMembersRequest = z.infer<typeof AddMembersRequestSchema>;
@@ -52,7 +52,7 @@ export type RevokeInvitationRequest = { email: string };
// --- Depot types ---
const PrepareUploadRequestSchema = z.object({
export const PrepareUploadRequestSchema = z.object({
network_id: z.string(),
name: z.string(),
content_type: z.string(),
@@ -122,7 +122,7 @@ export const MediaPropertiesSchema = z.object({
duration_ms: z.number(),
size_bytes: z.number(),
transcript: TranscriptSchema.optional(),
source: z.enum(["camera", "screen"]).optional(),
source: z.enum(['camera', 'screen']).optional(),
// Set by the particle processor worker once an iOS-playable MP4/m4a variant
// has been produced from a non-iOS-playable original (e.g. WebM from desktop).
// When present, clients should prefer these over object_id/mime_type for playback.
@@ -162,7 +162,9 @@ export type PaperProperties = z.infer<typeof PaperPropertiesSchema>;
// --- Reactions ---
export const ReactionsSchema = z.record(z.string(), z.array(z.string())).optional();
export const ReactionsSchema = z
.record(z.string(), z.array(z.string()))
.optional();
export type Reactions = z.infer<typeof ReactionsSchema>;
// --- Tombstone (soft-delete) ---
@@ -175,7 +177,15 @@ const TombstoneFields = {
deleted_by_human_id: z.string().optional(),
};
export const REACTION_EMOJIS = ["\u{1F44D}", "\u{2764}\u{FE0F}", "\u{1F525}", "\u{1F440}", "\u{2705}", "\u{2753}", "\u{1F602}"] as const;
export const REACTION_EMOJIS = [
'\u{1F44D}',
'\u{2764}\u{FE0F}',
'\u{1F525}',
'\u{1F440}',
'\u{2705}',
'\u{2753}',
'\u{1F602}',
] as const;
export interface ParticlePropertiesMap {
stream: StreamProperties;
@@ -196,9 +206,9 @@ const ParticleBaseSchema = z.object({
updated_at: z.coerce.date().optional(),
});
export const ParticleSchema = z.discriminatedUnion("type", [
export const ParticleSchema = z.discriminatedUnion('type', [
ParticleBaseSchema.extend({
type: z.literal("stream"),
type: z.literal('stream'),
properties: StreamPropertiesSchema,
// e.g. ["human:human_xxxx", "human:human_yyyy"] - visible only to Aron and John
// e.g. ["network:xywx"] - visible to everyone in the network
@@ -210,27 +220,53 @@ export const ParticleSchema = z.discriminatedUnion("type", [
last_child_created_at: z.coerce.date().optional(),
// Array of humanIds currently in the huddle (updated via LiveKit webhooks)
huddle_active_participants: z.array(z.string()).optional(),
status: z.enum(["open", "closed"]).optional(),
status: z.enum(['open', 'closed']).optional(),
}),
ParticleBaseSchema.extend({
type: z.literal("folder"), properties: FolderPropertiesSchema,
type: z.literal('folder'),
properties: FolderPropertiesSchema,
// e.g. ["human:human_xxxx", "human:human_yyyy"] - visible only to Aron and John
// e.g. ["network:123"] - visible to everyone in the network
visible_to: z.array(z.string()),
}),
ParticleBaseSchema.extend({ type: z.literal("media"), properties: MediaPropertiesSchema, reactions: ReactionsSchema, ...TombstoneFields }),
ParticleBaseSchema.extend({ type: z.literal("file"), properties: FilePropertiesSchema, ...TombstoneFields }),
ParticleBaseSchema.extend({ type: z.literal("text"), properties: TextPropertiesSchema, reactions: ReactionsSchema, ...TombstoneFields }),
ParticleBaseSchema.extend({ type: z.literal("quest"), properties: QuestPropertiesSchema, ...TombstoneFields }),
ParticleBaseSchema.extend({ type: z.literal("paper"), properties: PaperPropertiesSchema, ...TombstoneFields }),
ParticleBaseSchema.extend({
type: z.literal('media'),
properties: MediaPropertiesSchema,
reactions: ReactionsSchema,
...TombstoneFields,
}),
ParticleBaseSchema.extend({
type: z.literal('file'),
properties: FilePropertiesSchema,
...TombstoneFields,
}),
ParticleBaseSchema.extend({
type: z.literal('text'),
properties: TextPropertiesSchema,
reactions: ReactionsSchema,
...TombstoneFields,
}),
ParticleBaseSchema.extend({
type: z.literal('quest'),
properties: QuestPropertiesSchema,
...TombstoneFields,
}),
ParticleBaseSchema.extend({
type: z.literal('paper'),
properties: PaperPropertiesSchema,
...TombstoneFields,
}),
]);
export type Particle = z.infer<typeof ParticleSchema>;
export type ParticleType = Particle["type"];
export type ParticleType = Particle['type'];
/** Container types can have children subcollections */
export const CONTAINER_TYPES: ReadonlySet<ParticleType> = new Set(["stream", "folder"]);
export const CONTAINER_TYPES: ReadonlySet<ParticleType> = new Set([
'stream',
'folder',
]);
export function isContainerType(type: ParticleType): boolean {
return CONTAINER_TYPES.has(type);
@@ -238,7 +274,7 @@ export function isContainerType(type: ParticleType): boolean {
/** True when a non-container particle has been soft-deleted (tombstoned). */
export function isParticleDeleted(particle: Particle): boolean {
return "deleted_at" in particle && particle.deleted_at != null;
return 'deleted_at' in particle && particle.deleted_at != null;
}
// --- LiveKit types ---
@@ -247,16 +283,18 @@ export const GetLivekitTokenResponseSchema = z.object({
token: z.string(),
server_url: z.string(),
});
export type GetLivekitTokenResponse = z.infer<typeof GetLivekitTokenResponseSchema>;
export type GetLivekitTokenResponse = z.infer<
typeof GetLivekitTokenResponseSchema
>;
// --- Auth types ---
const RequestCodeRequestSchema = z.object({
export const RequestCodeRequestSchema = z.object({
email: z.string().email(),
});
export type RequestCodeRequest = z.infer<typeof RequestCodeRequestSchema>;
const SignInRequestSchema = z.object({
export const SignInRequestSchema = z.object({
email: z.string().email(),
code: z.string(),
});
@@ -275,21 +313,21 @@ export type FirebaseTokenResponse = z.infer<typeof FirebaseTokenResponseSchema>;
// --- Billing types ---
export const BillingCadenceSchema = z.enum(["monthly", "annual"]);
export const BillingCadenceSchema = z.enum(['monthly', 'annual']);
export type BillingCadence = z.infer<typeof BillingCadenceSchema>;
export const NetworkPlanSchema = z.enum(["free", "pro"]);
export const NetworkPlanSchema = z.enum(['free', 'pro']);
export type NetworkPlan = z.infer<typeof NetworkPlanSchema>;
// Mirrors Stripe subscription.status plus "active" as the default free-tier value.
export const BillingPlanStatusSchema = z.enum([
"active",
"trialing",
"past_due",
"canceled",
"incomplete",
"incomplete_expired",
"unpaid",
'active',
'trialing',
'past_due',
'canceled',
'incomplete',
'incomplete_expired',
'unpaid',
]);
export type BillingPlanStatus = z.infer<typeof BillingPlanStatusSchema>;
@@ -308,7 +346,9 @@ export type BillingStatus = z.infer<typeof BillingStatusSchema>;
export const CheckoutSessionResponseSchema = z.object({
url: z.string().url(),
});
export type CheckoutSessionResponse = z.infer<typeof CheckoutSessionResponseSchema>;
export type CheckoutSessionResponse = z.infer<
typeof CheckoutSessionResponseSchema
>;
export const PortalSessionResponseSchema = z.object({
url: z.string().url(),