this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

stuff sulad

+324 -304
+3 -4
drizzle.config.ts
··· 1 1 import { defineConfig } from "drizzle-kit"; 2 - import { env } from "./env.js"; 2 + import { env } from "./src/env.js"; 3 3 4 4 export default defineConfig({ 5 5 schema: "./src/data/schema.ts", 6 6 out: "./migrations", 7 - dialect: "turso", 7 + dialect: "postgresql", 8 8 dbCredentials: { 9 - url: env.TURSO_DATABASE_URL, 10 - authToken: env.TURSO_AUTH_TOKEN, 9 + url: env.DATABASE_URL, 11 10 }, 12 11 });
+1 -2
env.ts src/env.ts
··· 3 3 4 4 export const env = createEnv({ 5 5 server: { 6 - TURSO_DATABASE_URL: v.pipe(v.string(), v.url(), v.minLength(1)), 7 - TURSO_AUTH_TOKEN: v.pipe(v.string(), v.minLength(1)), 6 + DATABASE_URL: v.pipe(v.string(), v.url(), v.minLength(1)), 8 7 }, 9 8 10 9 /**
+1 -2
knip.json
··· 2 2 "$schema": "https://unpkg.com/knip@6/schema.json", 3 3 "tags": ["-lintignore"], 4 4 "ignoreDependencies": ["@typescript/native-preview"], 5 - "ignoreFiles": ["drizzle.config.ts", ".agents/skills/**/*.mjs"], 6 - "drizzle": false 5 + "ignoreFiles": [".agents/skills/**/*.mjs"] 7 6 }
-20
migrations/20260402124436_overjoyed_whiplash/migration.sql
··· 1 - CREATE TABLE `pokemon` ( 2 - `id` integer PRIMARY KEY NOT NULL, 3 - `name` text NOT NULL, 4 - `dex_id` integer NOT NULL 5 - ); 6 - --> statement-breakpoint 7 - CREATE TABLE `pokemon_types` ( 8 - `id` integer PRIMARY KEY NOT NULL, 9 - `pokemon_id` integer NOT NULL, 10 - `type_id` integer NOT NULL, 11 - FOREIGN KEY (`pokemon_id`) REFERENCES `pokemon`(`id`) ON UPDATE no action ON DELETE no action, 12 - FOREIGN KEY (`type_id`) REFERENCES `types`(`id`) ON UPDATE no action ON DELETE no action 13 - ); 14 - --> statement-breakpoint 15 - CREATE INDEX `idx_pt_pokemon` ON `pokemon_types` (`pokemon_id`);--> statement-breakpoint 16 - CREATE INDEX `idx_pt_type` ON `pokemon_types` (`type_id`);--> statement-breakpoint 17 - CREATE TABLE `types` ( 18 - `id` integer PRIMARY KEY NOT NULL, 19 - `name` text NOT NULL 20 - );
-172
migrations/20260402124436_overjoyed_whiplash/snapshot.json
··· 1 - { 2 - "dialect": "sqlite", 3 - "id": "ab4a5db2-40a4-42ef-aed0-67f72bb9e5ef", 4 - "prevIds": ["00000000-0000-0000-0000-000000000000"], 5 - "version": "7", 6 - "ddl": [ 7 - { 8 - "name": "pokemon", 9 - "entityType": "tables" 10 - }, 11 - { 12 - "type": "integer", 13 - "notNull": false, 14 - "autoincrement": false, 15 - "default": null, 16 - "generated": null, 17 - "name": "id", 18 - "table": "pokemon", 19 - "entityType": "columns" 20 - }, 21 - { 22 - "columns": ["id"], 23 - "nameExplicit": false, 24 - "name": "pokemon_pk", 25 - "table": "pokemon", 26 - "entityType": "pks" 27 - }, 28 - { 29 - "type": "text", 30 - "notNull": true, 31 - "autoincrement": false, 32 - "default": null, 33 - "generated": null, 34 - "name": "name", 35 - "table": "pokemon", 36 - "entityType": "columns" 37 - }, 38 - { 39 - "type": "integer", 40 - "notNull": true, 41 - "autoincrement": false, 42 - "default": null, 43 - "generated": null, 44 - "name": "dex_id", 45 - "table": "pokemon", 46 - "entityType": "columns" 47 - }, 48 - { 49 - "name": "pokemon_types", 50 - "entityType": "tables" 51 - }, 52 - { 53 - "type": "integer", 54 - "notNull": false, 55 - "autoincrement": false, 56 - "default": null, 57 - "generated": null, 58 - "name": "id", 59 - "table": "pokemon_types", 60 - "entityType": "columns" 61 - }, 62 - { 63 - "columns": ["id"], 64 - "nameExplicit": false, 65 - "name": "pokemon_types_pk", 66 - "table": "pokemon_types", 67 - "entityType": "pks" 68 - }, 69 - { 70 - "type": "integer", 71 - "notNull": true, 72 - "autoincrement": false, 73 - "default": null, 74 - "generated": null, 75 - "name": "pokemon_id", 76 - "table": "pokemon_types", 77 - "entityType": "columns" 78 - }, 79 - { 80 - "type": "integer", 81 - "notNull": true, 82 - "autoincrement": false, 83 - "default": null, 84 - "generated": null, 85 - "name": "type_id", 86 - "table": "pokemon_types", 87 - "entityType": "columns" 88 - }, 89 - { 90 - "columns": [ 91 - { 92 - "value": "pokemon_id", 93 - "isExpression": false 94 - } 95 - ], 96 - "isUnique": false, 97 - "where": null, 98 - "origin": "manual", 99 - "name": "idx_pt_pokemon", 100 - "table": "pokemon_types", 101 - "entityType": "indexes" 102 - }, 103 - { 104 - "columns": [ 105 - { 106 - "value": "type_id", 107 - "isExpression": false 108 - } 109 - ], 110 - "isUnique": false, 111 - "where": null, 112 - "origin": "manual", 113 - "name": "idx_pt_type", 114 - "table": "pokemon_types", 115 - "entityType": "indexes" 116 - }, 117 - { 118 - "columns": ["pokemon_id"], 119 - "tableTo": "pokemon", 120 - "columnsTo": ["id"], 121 - "onUpdate": "NO ACTION", 122 - "onDelete": "NO ACTION", 123 - "nameExplicit": false, 124 - "name": "pokemon_types_pokemon_id_pokemon_id_fk", 125 - "table": "pokemon_types", 126 - "entityType": "fks" 127 - }, 128 - { 129 - "columns": ["type_id"], 130 - "tableTo": "types", 131 - "columnsTo": ["id"], 132 - "onUpdate": "NO ACTION", 133 - "onDelete": "NO ACTION", 134 - "nameExplicit": false, 135 - "name": "pokemon_types_type_id_types_id_fk", 136 - "table": "pokemon_types", 137 - "entityType": "fks" 138 - }, 139 - { 140 - "name": "types", 141 - "entityType": "tables" 142 - }, 143 - { 144 - "type": "integer", 145 - "notNull": false, 146 - "autoincrement": false, 147 - "default": null, 148 - "generated": null, 149 - "name": "id", 150 - "table": "types", 151 - "entityType": "columns" 152 - }, 153 - { 154 - "columns": ["id"], 155 - "nameExplicit": false, 156 - "name": "types_pk", 157 - "table": "types", 158 - "entityType": "pks" 159 - }, 160 - { 161 - "type": "text", 162 - "notNull": true, 163 - "autoincrement": false, 164 - "default": null, 165 - "generated": null, 166 - "name": "name", 167 - "table": "types", 168 - "entityType": "columns" 169 - } 170 - ], 171 - "renames": [] 172 - }
+21
migrations/20260427191921_happy_lady_ursula/migration.sql
··· 1 + CREATE TABLE "pokemon" ( 2 + "id" integer PRIMARY KEY, 3 + "name" text NOT NULL, 4 + "dex_id" integer NOT NULL 5 + ); 6 + --> statement-breakpoint 7 + CREATE TABLE "pokemon_types" ( 8 + "id" integer PRIMARY KEY, 9 + "pokemon_id" integer NOT NULL, 10 + "type_id" integer NOT NULL 11 + ); 12 + --> statement-breakpoint 13 + CREATE TABLE "types" ( 14 + "id" integer PRIMARY KEY, 15 + "name" text NOT NULL 16 + ); 17 + --> statement-breakpoint 18 + CREATE INDEX "idx_pt_type" ON "pokemon_types" ("type_id");--> statement-breakpoint 19 + CREATE INDEX "idx_pt_pokemon" ON "pokemon_types" ("pokemon_id");--> statement-breakpoint 20 + ALTER TABLE "pokemon_types" ADD CONSTRAINT "pokemon_types_pokemon_id_pokemon_id_fkey" FOREIGN KEY ("pokemon_id") REFERENCES "pokemon"("id");--> statement-breakpoint 21 + ALTER TABLE "pokemon_types" ADD CONSTRAINT "pokemon_types_type_id_types_id_fkey" FOREIGN KEY ("type_id") REFERENCES "types"("id");
+223
migrations/20260427191921_happy_lady_ursula/snapshot.json
··· 1 + { 2 + "version": "8", 3 + "dialect": "postgres", 4 + "id": "1054bff9-3e73-462f-a401-f2752e04bfee", 5 + "prevIds": ["00000000-0000-0000-0000-000000000000"], 6 + "ddl": [ 7 + { 8 + "isRlsEnabled": false, 9 + "name": "pokemon", 10 + "entityType": "tables", 11 + "schema": "public" 12 + }, 13 + { 14 + "isRlsEnabled": false, 15 + "name": "pokemon_types", 16 + "entityType": "tables", 17 + "schema": "public" 18 + }, 19 + { 20 + "isRlsEnabled": false, 21 + "name": "types", 22 + "entityType": "tables", 23 + "schema": "public" 24 + }, 25 + { 26 + "type": "integer", 27 + "typeSchema": null, 28 + "notNull": true, 29 + "dimensions": 0, 30 + "default": null, 31 + "generated": null, 32 + "identity": null, 33 + "name": "id", 34 + "entityType": "columns", 35 + "schema": "public", 36 + "table": "pokemon" 37 + }, 38 + { 39 + "type": "text", 40 + "typeSchema": null, 41 + "notNull": true, 42 + "dimensions": 0, 43 + "default": null, 44 + "generated": null, 45 + "identity": null, 46 + "name": "name", 47 + "entityType": "columns", 48 + "schema": "public", 49 + "table": "pokemon" 50 + }, 51 + { 52 + "type": "integer", 53 + "typeSchema": null, 54 + "notNull": true, 55 + "dimensions": 0, 56 + "default": null, 57 + "generated": null, 58 + "identity": null, 59 + "name": "dex_id", 60 + "entityType": "columns", 61 + "schema": "public", 62 + "table": "pokemon" 63 + }, 64 + { 65 + "type": "integer", 66 + "typeSchema": null, 67 + "notNull": true, 68 + "dimensions": 0, 69 + "default": null, 70 + "generated": null, 71 + "identity": null, 72 + "name": "id", 73 + "entityType": "columns", 74 + "schema": "public", 75 + "table": "pokemon_types" 76 + }, 77 + { 78 + "type": "integer", 79 + "typeSchema": null, 80 + "notNull": true, 81 + "dimensions": 0, 82 + "default": null, 83 + "generated": null, 84 + "identity": null, 85 + "name": "pokemon_id", 86 + "entityType": "columns", 87 + "schema": "public", 88 + "table": "pokemon_types" 89 + }, 90 + { 91 + "type": "integer", 92 + "typeSchema": null, 93 + "notNull": true, 94 + "dimensions": 0, 95 + "default": null, 96 + "generated": null, 97 + "identity": null, 98 + "name": "type_id", 99 + "entityType": "columns", 100 + "schema": "public", 101 + "table": "pokemon_types" 102 + }, 103 + { 104 + "type": "integer", 105 + "typeSchema": null, 106 + "notNull": true, 107 + "dimensions": 0, 108 + "default": null, 109 + "generated": null, 110 + "identity": null, 111 + "name": "id", 112 + "entityType": "columns", 113 + "schema": "public", 114 + "table": "types" 115 + }, 116 + { 117 + "type": "text", 118 + "typeSchema": null, 119 + "notNull": true, 120 + "dimensions": 0, 121 + "default": null, 122 + "generated": null, 123 + "identity": null, 124 + "name": "name", 125 + "entityType": "columns", 126 + "schema": "public", 127 + "table": "types" 128 + }, 129 + { 130 + "nameExplicit": true, 131 + "columns": [ 132 + { 133 + "value": "type_id", 134 + "isExpression": false, 135 + "asc": true, 136 + "nullsFirst": false, 137 + "opclass": null 138 + } 139 + ], 140 + "isUnique": false, 141 + "where": null, 142 + "with": "", 143 + "method": "btree", 144 + "concurrently": false, 145 + "name": "idx_pt_type", 146 + "entityType": "indexes", 147 + "schema": "public", 148 + "table": "pokemon_types" 149 + }, 150 + { 151 + "nameExplicit": true, 152 + "columns": [ 153 + { 154 + "value": "pokemon_id", 155 + "isExpression": false, 156 + "asc": true, 157 + "nullsFirst": false, 158 + "opclass": null 159 + } 160 + ], 161 + "isUnique": false, 162 + "where": null, 163 + "with": "", 164 + "method": "btree", 165 + "concurrently": false, 166 + "name": "idx_pt_pokemon", 167 + "entityType": "indexes", 168 + "schema": "public", 169 + "table": "pokemon_types" 170 + }, 171 + { 172 + "nameExplicit": false, 173 + "columns": ["pokemon_id"], 174 + "schemaTo": "public", 175 + "tableTo": "pokemon", 176 + "columnsTo": ["id"], 177 + "onUpdate": "NO ACTION", 178 + "onDelete": "NO ACTION", 179 + "name": "pokemon_types_pokemon_id_pokemon_id_fkey", 180 + "entityType": "fks", 181 + "schema": "public", 182 + "table": "pokemon_types" 183 + }, 184 + { 185 + "nameExplicit": false, 186 + "columns": ["type_id"], 187 + "schemaTo": "public", 188 + "tableTo": "types", 189 + "columnsTo": ["id"], 190 + "onUpdate": "NO ACTION", 191 + "onDelete": "NO ACTION", 192 + "name": "pokemon_types_type_id_types_id_fkey", 193 + "entityType": "fks", 194 + "schema": "public", 195 + "table": "pokemon_types" 196 + }, 197 + { 198 + "columns": ["id"], 199 + "nameExplicit": false, 200 + "name": "pokemon_pkey", 201 + "schema": "public", 202 + "table": "pokemon", 203 + "entityType": "pks" 204 + }, 205 + { 206 + "columns": ["id"], 207 + "nameExplicit": false, 208 + "name": "pokemon_types_pkey", 209 + "schema": "public", 210 + "table": "pokemon_types", 211 + "entityType": "pks" 212 + }, 213 + { 214 + "columns": ["id"], 215 + "nameExplicit": false, 216 + "name": "types_pkey", 217 + "schema": "public", 218 + "table": "types", 219 + "entityType": "pks" 220 + } 221 + ], 222 + "renames": [] 223 + }
+39 -57
src/data/db.ts
··· 1 - import { sql } from "drizzle-orm"; 2 - import { drizzle } from "drizzle-orm/libsql"; 3 - import { env } from "~env"; 1 + import postgres from "postgres"; 2 + import { drizzle } from "drizzle-orm/postgres-js"; 3 + import { env } from "~/env.js"; 4 4 import { relations } from "./relations.js"; 5 5 6 + const client = postgres(env.DATABASE_URL, { prepare: false }); 6 7 const db = drizzle({ 7 - connection: { 8 - url: env.TURSO_DATABASE_URL, 9 - authToken: env.TURSO_AUTH_TOKEN, 10 - }, 8 + client, 11 9 relations, 12 10 }); 13 11 14 - const preparedGetPokemonAtOffset = db.query.pokemon 15 - .findMany({ 16 - columns: { 17 - id: true, 18 - name: true, 19 - dexId: true, 20 - }, 21 - orderBy: { 22 - dexId: "asc", 23 - }, 24 - limit: sql.placeholder("limit"), 25 - offset: sql.placeholder("offset"), 26 - with: { 27 - types: { 12 + export const DB = { 13 + queries: { 14 + getPokemonAtOffset: async (offset: number, limit: number) => { 15 + return db.query.pokemon.findMany({ 28 16 columns: { 17 + id: true, 29 18 name: true, 19 + dexId: true, 30 20 }, 31 - }, 32 - }, 33 - }) 34 - .prepare(); 35 - 36 - const preparedGetFilteredPokemonAtOffset = db.query.pokemon 37 - .findMany({ 38 - columns: { 39 - id: true, 40 - name: true, 41 - dexId: true, 42 - }, 43 - where: { 44 - RAW: (pokemon, { sql }) => 45 - sql`lower(${pokemon.name}) like lower(${sql.placeholder("nameFilter")})`, 46 - }, 47 - orderBy: { 48 - dexId: "asc", 49 - }, 50 - limit: sql.placeholder("limit"), 51 - offset: sql.placeholder("offset"), 52 - with: { 53 - types: { 54 - columns: { 55 - name: true, 21 + orderBy: { 22 + dexId: "asc", 56 23 }, 57 - }, 58 - }, 59 - }) 60 - .prepare(); 61 - 62 - export const DB = { 63 - queries: { 64 - getPokemonAtOffset: async (offset: number, limit: number) => { 65 - return preparedGetPokemonAtOffset.execute({ 66 24 limit, 67 25 offset, 26 + with: { 27 + types: { 28 + columns: { 29 + name: true, 30 + }, 31 + }, 32 + }, 68 33 }); 69 34 }, 70 35 getFilteredPokemonAtOffset: async (offset: number, limit: number, nameFilter: string) => { 71 36 // Convert the filter to a SQL LIKE pattern (case-insensitive) 72 37 const likePattern = `%${nameFilter.toLowerCase()}%`; 73 - return preparedGetFilteredPokemonAtOffset.execute({ 38 + return db.query.pokemon.findMany({ 39 + columns: { 40 + id: true, 41 + name: true, 42 + dexId: true, 43 + }, 44 + where: { 45 + RAW: (pokemon, { sql }) => sql`lower(${pokemon.name}) like lower(${likePattern})`, 46 + }, 47 + orderBy: { 48 + dexId: "asc", 49 + }, 74 50 limit, 75 51 offset, 76 - nameFilter: likePattern, 52 + with: { 53 + types: { 54 + columns: { 55 + name: true, 56 + }, 57 + }, 58 + }, 77 59 }); 78 60 }, 79 61 },
+4 -4
src/data/schema.ts
··· 1 - import { sqliteTable, index, integer, text } from "drizzle-orm/sqlite-core"; 1 + import { pgTable, index, integer, text } from "drizzle-orm/pg-core"; 2 2 3 - export const pokemon = sqliteTable("pokemon", { 3 + export const pokemon = pgTable("pokemon", { 4 4 id: integer().primaryKey(), 5 5 name: text().notNull(), 6 6 dexId: integer("dex_id").notNull(), 7 7 }); 8 8 9 - export const pokemonTypes = sqliteTable( 9 + export const pokemonTypes = pgTable( 10 10 "pokemon_types", 11 11 { 12 12 id: integer().primaryKey(), ··· 20 20 (table) => [index("idx_pt_type").on(table.typeId), index("idx_pt_pokemon").on(table.pokemonId)], 21 21 ); 22 22 23 - export const types = sqliteTable("types", { 23 + export const types = pgTable("types", { 24 24 id: integer().primaryKey(), 25 25 name: text().notNull(), 26 26 });
+7 -7
src/data/seed.ts
··· 1 1 import { readFileSync } from "node:fs"; 2 2 import { dirname, resolve } from "node:path"; 3 3 import { fileURLToPath } from "node:url"; 4 - import { env } from "~env"; 5 - import { drizzle } from "drizzle-orm/libsql"; 4 + import { env } from "~/env.js"; 5 + import { drizzle } from "drizzle-orm/postgres-js"; 6 + import postgres from "postgres"; 6 7 import * as schema from "./schema.js"; 8 + import { relations } from "./relations.js"; 7 9 8 10 type SeedData = { 9 11 pokemon: Array<{ ··· 27 29 28 30 const seedData = JSON.parse(readFileSync(seedDataPath, "utf8")) as SeedData; 29 31 32 + const client = postgres(env.DATABASE_URL, { prepare: false }); 30 33 const db = drizzle({ 31 - connection: { 32 - url: env.TURSO_DATABASE_URL, 33 - authToken: env.TURSO_AUTH_TOKEN, 34 - }, 35 - schema, 34 + client, 35 + relations, 36 36 }); 37 37 38 38 const BATCH_SIZE = 500;
+25 -34
src/routes/basic.tsx
··· 1 1 import { Suspense } from "react"; 2 2 import { createFileRoute } from "@tanstack/react-router"; 3 - import { useQuery, useSuspenseQuery } from "@tanstack/react-query"; 3 + import { useSuspenseQuery } from "@tanstack/react-query"; 4 4 import * as v from "valibot"; 5 5 import { QueryTrace } from "~/components/console/query-trace"; 6 6 import { ··· 48 48 <h1 className="text-lg font-mono text-(--text-primary) mb-4"> 49 49 National Pokédex: Pokémon {currentOffset + 1}-{currentOffset + POKEMON_LIMIT} 50 50 </h1> 51 - <div className="min-h-[500px]"> 52 - <Suspense 53 - fallback={ 54 - <> 51 + <Suspense 52 + fallback={ 53 + <> 54 + <PokemonTableShell> 55 55 <QueryTrace 56 56 {...getBasicQueryTraceProps(currentOffset)} 57 57 cacheStatus={getLoadingCacheStatus()} 58 58 fetchStatus={getLoadingFetchStatus()} 59 59 /> 60 60 <PokemonTableSkeleton rowCount={POKEMON_LIMIT} /> 61 - </> 62 - } 63 - > 64 - <PokemonTableContent currentOffset={currentOffset} /> 65 - </Suspense> 66 - </div> 67 - <PaginationNavOutlet /> 61 + </PokemonTableShell> 62 + <PaginationNav prevOffset={null} nextOffset={null} to="/basic" /> 63 + </> 64 + } 65 + > 66 + <PokemonTableContent currentOffset={currentOffset} /> 67 + </Suspense> 68 68 </ConsoleCard> 69 69 </div> 70 70 </main> 71 71 ); 72 + } 73 + 74 + function PokemonTableShell({ children }: { children: React.ReactNode }) { 75 + return <div className="min-h-125">{children}</div>; 72 76 } 73 77 74 78 function PokemonTableContent({ currentOffset }: { currentOffset: number }) { ··· 80 84 81 85 return ( 82 86 <> 83 - <QueryTrace 84 - {...getBasicQueryTraceProps(currentOffset)} 85 - cacheStatus={getCacheStatus(dataUpdatedAt)} 86 - fetchStatus={getFetchStatus(fetchStatus, isFetching ? "pending" : "success")} 87 - /> 88 - <PokemonTable pokemon={data.pokemon} /> 87 + <PokemonTableShell> 88 + <QueryTrace 89 + {...getBasicQueryTraceProps(currentOffset)} 90 + cacheStatus={getCacheStatus(dataUpdatedAt)} 91 + fetchStatus={getFetchStatus(fetchStatus, isFetching ? "pending" : "success")} 92 + /> 93 + <PokemonTable pokemon={data.pokemon} /> 94 + </PokemonTableShell> 95 + <PaginationNav prevOffset={data.prevOffset} nextOffset={data.nextOffset} to="/basic" /> 89 96 </> 90 97 ); 91 98 } 92 - 93 - function PaginationNavOutlet() { 94 - const { offset: currentOffset } = Route.useSearch(); 95 - const { data } = useQuery({ 96 - queryKey: getPokemonListQueryKey("suspense", currentOffset), 97 - queryFn: getPokemonListQueryFn, 98 - }); 99 - 100 - return ( 101 - <PaginationNav 102 - prevOffset={data?.prevOffset ?? null} 103 - nextOffset={data?.nextOffset ?? null} 104 - to="/basic" 105 - /> 106 - ); 107 - }
-1
src/routes/preloading.tsx
··· 19 19 import { POKEMON_LIMIT } from "~/constants"; 20 20 import { getPokemonListQueryKey, getPokemonListQueryFn } from "~/util/pokemon"; 21 21 import { lazily } from "~/util/lazily"; 22 - import { is } from "drizzle-orm"; 23 22 24 23 const { PokemonTable } = lazily(() => import("~/components/console/pokemon-table")); 25 24
-1
tsconfig.json
··· 21 21 "noFallthroughCasesInSwitch": true, 22 22 "noUncheckedSideEffectImports": true, 23 23 "paths": { 24 - "~env": ["./env.ts"], 25 24 "~/*": ["./src/*"], 26 25 "~drizzle/*": ["./drizzle/*"] 27 26 }