diff --git a/.gitignore b/.gitignore index d570088..a14702c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,34 @@ -node_modules/ +# dependencies (bun install) +node_modules +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/TODO.txt b/TODO.txt index e1e9730..72ab594 100644 --- a/TODO.txt +++ b/TODO.txt @@ -11,8 +11,8 @@ ## apps -[ ] truism -[ ] big clock +[x] truism +[x] big clock [ ] shrimp repl(?) [ ] dungeon party diff --git a/apps/basic/package.json b/apps/basic/package.json index e8be2ee..0f3061a 100644 --- a/apps/basic/package.json +++ b/apps/basic/package.json @@ -5,7 +5,7 @@ "private": true, "scripts": { "toes": "bun run --watch index.tsx", - "start": "bun run index.tsx", + "start": "bun toes", "dev": "bun run --hot index.tsx" }, "devDependencies": { diff --git a/apps/clock/bun.lock b/apps/clock/bun.lock new file mode 100644 index 0000000..f047791 --- /dev/null +++ b/apps/clock/bun.lock @@ -0,0 +1,37 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "toes-clock", + "dependencies": { + "forge": "git+https://git.nose.space/defunkt/forge", + "hype": "git+https://git.nose.space/defunkt/hype", + }, + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5.9.2", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.3.7", "", { "dependencies": { "bun-types": "1.3.7" } }, "sha512-lmNuMda+Z9b7tmhA0tohwy8ZWFSnmQm1UDWXtH5r9F7wZCfkeO3Jx7wKQ1EOiKq43yHts7ky6r8SDJQWRNupkA=="], + + "@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], + + "bun-types": ["bun-types@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-qyschsA03Qz+gou+apt6HNl6HnI+sJJLL4wLDke4iugsE6584CMupOtTY1n+2YC9nGVrEKUlTs99jjRLKgWnjQ=="], + + "forge": ["forge@git+https://git.nose.space/defunkt/forge#9b6e1e91ec77d7e03589cac256d97fb9cd942184", { "peerDependencies": { "typescript": "^5" } }, "9b6e1e91ec77d7e03589cac256d97fb9cd942184"], + + "hono": ["hono@4.11.7", "", {}, "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw=="], + + "hype": ["hype@git+https://git.nose.space/defunkt/hype#7b9cade936c4897539d2ca14299d90f80deb6ebe", { "dependencies": { "hono": "^4.10.4", "kleur": "^4.1.5" }, "peerDependencies": { "typescript": "^5" } }, "7b9cade936c4897539d2ca14299d90f80deb6ebe"], + + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + } +} diff --git a/apps/clock/index.tsx b/apps/clock/index.tsx new file mode 100644 index 0000000..fe75314 --- /dev/null +++ b/apps/clock/index.tsx @@ -0,0 +1,72 @@ +import { Hype } from 'hype' +import { define, stylesToCSS } from 'forge' + +const app = new Hype() + +const Body = define('ClockBody', { + base: 'body', + margin: 0, + padding: 0, + overflow: 'hidden', +}) + +const Container = define('ClockContainer', { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + height: '100vh', + width: '100vw', + background: '#000', +}) + +const Clock = define('Clock', { + fontFamily: "'DS-Digital', monospace", + fontSize: '12vw', + fontWeight: 'normal', + color: '#0f0', + textShadow: '0 0 2px rgba(0, 255, 0, 0.45)', + letterSpacing: '0.05em', + userSelect: 'none', +}) + +const clockScript = ` + function updateClock() { + const now = new Date(); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const seconds = String(now.getSeconds()).padStart(2, '0'); + document.getElementById('clock').textContent = hours + ':' + minutes + ':' + seconds; + } + + updateClock(); + setInterval(updateClock, 1000); +` +app.get('/styles.css', c => c.text(stylesToCSS(), 200, { + 'Content-Type': 'text/css; charset=utf-8', +})) + +app.get('/', c => c.html( + + + Clock + + + +

${truism}

+ + + `) +}) + +app.get("/txt", (c) => c.text(TRUISMS[Math.floor(Math.random() * TRUISMS.length)]!)) + +export default { + port: process.env.PORT || 3000, + fetch: app.fetch, +} + +const TRUISMS = [ + "A LITTLE KNOWLEDGE CAN GO A LONG WAY", + "A LOT OF PROFESSIONALS ARE CRACKPOTS", + "A MAN CAN'T KNOW WHAT IT'S LIKE TO BE A MOTHER", + "A NAME MEANS A LOT JUST BY ITSELF", + "A POSITIVE ATTITUDE MAKES ALL THE DIFFERENCE IN THE WORLD", + "A RELAXED MAN IS NOT NECESSARILY A BETTER MAN", + "A SENSE OF TIMING IS THE MARK OF GENIUS", + "A SINCERE EFFORT IS ALL YOU CAN ASK", + "A SINGLE EVENT CAN HAVE INFINITELY MANY INTERPRETATIONS", + "A SOLID HOME BASE BUILDS A SENSE OF SELF", + "A STRONG SENSE OF DUTY IMPRISONS YOU", + "ABSOLUTE SUBMISSION CAN BE A FORM OF FREEDOM", + "ABSTRACTION IS A TYPE OF DECADENCE", + "ABUSE OF POWER COMES AS NO SURPRISE", + "ACTION CAUSES MORE TROUBLE THAN THOUGHT", + "ALIENATION PRODUCES ECCENTRICS OR REVOLUTIONARIES", + "ALL THINGS ARE DELICATELY INTERCONNECTED", + "AMBITION IS JUST AS DANGEROUS AS COMPLACENCY", + "AMBIVALENCE CAN RUIN YOUR LIFE", + "AN ELITE IS INEVITABLE", + "ANGER OR HATE CAN BE A USEFUL MOTIVATING FORCE", + "ANIMALISM IS PERFECTLY HEALTHY", + "ANY SURPLUS IS IMMORAL", + "ANYTHING IS A LEGITIMATE AREA OF INVESTIGATION", + "ARTIFICIAL DESIRES ARE DESPOILING THE EARTH", + "AT TIMES INACTIVITY IS PREFERABLE TO MINDLESS FUNCTIONING", + "AT TIMES YOUR UNCONSCIOUS IS TRUER THAN YOUR CONSCIOUS MIND", + "AUTOMATION IS DEADLY", + "AWFUL PUNISHMENT AWAITS REALLY BAD PEOPLE", + "BAD INTENTIONS CAN YIELD GOOD RESULTS", + "BEING ALONE WITH YOURSELF IS INCREASINGLY UNPOPULAR", + "BEING HAPPY IS MORE IMPORTANT THAN ANYTHING ELSE", + "BEING JUDGMENTAL IS A SIGN OF LIFE", + "BEING SURE OF YOURSELF MEANS YOU'RE A FOOL", + "BELIEVING IN REBIRTH IS THE SAME AS ADMITTING DEFEAT", + "BOREDOM MAKES YOU DO CRAZY THINGS", + "CALM IS MORE CONDUCIVE TO CREATIVITY THAN IS ANXIETY", + "CATEGORIZING FEAR IS CALMING", + "CHANGE IS VALUABLE WHEN THE OPPRESSED BECOME TYRANTS", + "CHASING THE NEW IS DANGEROUS TO SOCIETY", + "CHILDREN ARE THE HOPE OF THE FUTURE", + "CHILDREN ARE THE MOST CRUEL OF ALL", + "CLASS ACTION IS A NICE IDEA WITH NO SUBSTANCE", + "CLASS STRUCTURE IS AS ARTIFICIAL AS PLASTIC", + "CONFUSING YOURSELF IS A WAY TO STAY HONEST", + "CRIME AGAINST PROPERTY IS RELATIVELY UNIMPORTANT", + "DECADENCE CAN BE AN END IN ITSELF", + "DECENCY IS A RELATIVE THING", + "DEPENDENCE CAN BE A MEAL TICKET", + "DESCRIPTION IS MORE VALUABLE THAN METAPHOR", + "DEVIANTS ARE SACRIFICED TO INCREASE GROUP SOLIDARITY", + "DISGUST IS THE APPROPRIATE RESPONSE TO MOST SITUATIONS", + "DISORGANIZATION IS A KIND OF ANESTHESIA", + "DON'T PLACE TOO MUCH TRUST IN EXPERTS", + "DRAMA OFTEN OBSCURES THE REAL ISSUES", + "DREAMING WHILE AWAKE IS A FRIGHTENING CONTRADICTION", + "DYING AND COMING BACK GIVES YOU CONSIDERABLE PERSPECTIVE", + "DYING SHOULD BE AS EASY AS FALLING OFF A LOG", + "EATING TOO MUCH IS CRIMINAL", + "ELABORATION IS A FORM OF POLLUTION", + "EMOTIONAL RESPONSES ARE AS VALUABLE AS INTELLECTUAL RESPONSES", + "ENJOY YOURSELF BECAUSE YOU CAN'T CHANGE ANYTHING ANYWAY", + "ENSURE THAT YOUR LIFE STAYS IN FLUX", + "EVEN YOUR FAMILY CAN BETRAY YOU", + "EVERY ACHIEVEMENT REQUIRES A SACRIFICE", + "EVERYONE'S WORK IS EQUALLY IMPORTANT", + "EVERYTHING THAT'S INTERESTING IS NEW", + "EXCEPTIONAL PEOPLE DESERVE SPECIAL CONCESSIONS", + "EXPIRING FOR LOVE IS BEAUTIFUL BUT STUPID", + "EXPRESSING ANGER IS NECESSARY", + "EXTREME BEHAVIOR HAS ITS BASIS IN PATHOLOGICAL PSYCHOLOGY", + "EXTREME SELF-CONSCIOUSNESS LEADS TO PERVERSION", + "FAITHFULNESS IS A SOCIAL NOT A BIOLOGICAL LAW", + "FAKE OR REAL INDIFFERENCE IS A POWERFUL PERSONAL WEAPON", + "FATHERS OFTEN USE TOO MUCH FORCE", + "FEAR IS THE GREATEST INCAPACITATOR", + "FREEDOM IS A LUXURY NOT A NECESSITY", + "GIVING FREE REIN TO YOUR EMOTIONS IS AN HONEST WAY TO LIVE", + "GO ALL OUT IN ROMANCE AND LET THE CHIPS FALL WHERE THEY MAY", + "GOING WITH THE FLOW IS SOOTHING BUT RISKY", + "GOOD DEEDS EVENTUALLY ARE REWARDED", + "GOVERNMENT IS A BURDEN ON THE PEOPLE", + "GRASS ROOTS AGITATION IS THE ONLY HOPE", + "GUILT AND SELF-LACERATION ARE INDULGENCES", + "HABITUAL CONTEMPT DOESN'T REFLECT A FINER SENSIBILITY", + "HIDING YOUR MOTIVES IS DESPICABLE", + "HOLDING BACK PROTECTS YOUR VITAL ENERGIES", + "HUMANISM IS OBSOLETE", + "HUMOR IS A RELEASE", + "IDEALS ARE REPLACED BY CONVENTIONAL GOALS AT A CERTAIN AGE", + "IF YOU AREN'T POLITICAL YOUR PERSONAL LIFE SHOULD BE EXEMPLARY", + "IF YOU CAN'T LEAVE YOUR MARK GIVE UP", + "IF YOU HAVE MANY DESIRES YOUR LIFE WILL BE INTERESTING", + "IF YOU LIVE SIMPLY THERE IS NOTHING TO WORRY ABOUT", + "IGNORING ENEMIES IS THE BEST WAY TO FIGHT", + "ILLNESS IS A STATE OF MIND", + "IMPOSING ORDER IS MAN'S VOCATION FOR CHAOS IS HELL", + "IN SOME INSTANCES IT'S BETTER TO DIE THAN TO CONTINUE", + "INHERITANCE MUST BE ABOLISHED", + "IT CAN BE HELPFUL TO KEEP GOING NO MATTER WHAT", + "IT IS HEROIC TO TRY TO STOP TIME", + "IT IS MAN'S FATE TO OUTSMART HIMSELF", + "IT'S A GIFT TO THE WORLD NOT TO HAVE BABIES", + "IT'S BETTER TO BE A GOOD PERSON THAN A FAMOUS PERSON", + "IT'S BETTER TO BE LONELY THAN TO BE WITH INFERIOR PEOPLE", + "IT'S BETTER TO BE NAIVE THAN JADED", + "IT'S BETTER TO STUDY THE LIVING FACT THAN TO ANALYZE HISTORY", + "IT'S CRUCIAL TO HAVE AN ACTIVE FANTASY LIFE", + "IT'S GOOD TO GIVE EXTRA MONEY TO CHARITY", + "IT'S IMPORTANT TO STAY CLEAN ON ALL LEVELS", + "IT'S JUST AN ACCIDENT THAT YOUR PARENTS ARE YOUR PARENTS", + "IT'S NOT GOOD TO HOLD TOO MANY ABSOLUTES", + "IT'S NOT GOOD TO OPERATE ON CREDIT", + "IT'S VITAL TO LIVE IN HARMONY WITH NATURE", + "JUST BELIEVING SOMETHING CAN MAKE IT HAPPEN", + "KEEP SOMETHING IN RESERVE FOR EMERGENCIES", + "KILLING IS UNAVOIDABLE BUT IS NOTHING TO BE PROUD OF", + "KNOWING YOURSELF LETS YOU UNDERSTAND OTHERS", + "KNOWLEDGE SHOULD BE ADVANCED AT ALL COSTS", + "LABOR IS A LIFE-DESTROYING ACTIVITY", + "LACK OF CHARISMA CAN BE FATAL", + "LEISURE TIME IS A GIGANTIC SMOKE SCREEN", + "LISTEN WHEN YOUR BODY TALKS", + "LOOKING BACK IS THE FIRST SIGN OF AGING AND DECAY", + "LOVING ANIMALS IS A SUBSTITUTE ACTIVITY", + "LOW EXPECTATIONS ARE GOOD PROTECTION", + "MANUAL LABOR CAN BE REFRESHING AND WHOLESOME", + "MEN ARE NOT MONOGAMOUS BY NATURE", + "MODERATION KILLS THE SPIRIT", + "MONEY CREATES TASTE", + "MONOMANIA IS A PREREQUISITE OF SUCCESS", + "MORALS ARE FOR LITTLE PEOPLE", + "MOST PEOPLE ARE NOT FIT TO RULE THEMSELVES", + "MOSTLY YOU SHOULD MIND YOUR OWN BUSINESS", + "MOTHERS SHOULDN'T MAKE TOO MANY SACRIFICES", + "MUCH WAS DECIDED BEFORE YOU WERE BORN", + "MURDER HAS ITS SEXUAL SIDE", + "MYTHS CAN MAKE REALITY MORE INTELLIGIBLE", + "NOISE CAN BE HOSTILE", + "NOTHING UPSETS THE BALANCE OF GOOD AND EVIL", + "OCCASIONALLY PRINCIPLES ARE MORE VALUABLE THAN PEOPLE", + "OFFER VERY LITTLE INFORMATION ABOUT YOURSELF", + "OFTEN YOU SHOULD ACT LIKE YOU ARE SEXLESS", + "OLD FRIENDS ARE BETTER LEFT IN THE PAST", + "OPACITY IS AN IRRESISTIBLE CHALLENGE", + "PAIN CAN BE A VERY POSITIVE THING", + "PEOPLE ARE BORING UNLESS THEY'RE EXTREMISTS", + "PEOPLE ARE NUTS IF THEY THINK THEY ARE IMPORTANT", + "PEOPLE ARE RESPONSIBLE FOR WHAT THEY DO UNLESS THEY'RE INSANE", + "PEOPLE WHO DON'T WORK WITH THEIR HANDS ARE PARASITES", + "PEOPLE WHO GO CRAZY ARE TOO SENSITIVE", + "PEOPLE WON'T BEHAVE IF THEY HAVE NOTHING TO LOSE", + "PHYSICAL CULTURE IS SECOND-BEST", + "PLANNING FOR THE FUTURE IS ESCAPISM", + "PLAYING IT SAFE CAN CAUSE A LOT OF DAMAGE IN THE LONG RUN", + "POLITICS IS USED FOR PERSONAL GAIN", + "POTENTIAL COUNTS FOR NOTHING UNTIL IT'S REALIZED", + "PRIVATE PROPERTY CREATED CRIME", + "PURSUING PLEASURE FOR THE SAKE OF PLEASURE WILL RUIN YOU", + "PUSH YOURSELF TO THE LIMIT AS OFTEN AS POSSIBLE", + "RAISE BOYS AND GIRLS THE SAME WAY", + "RANDOM MATING IS GOOD FOR DEBUNKING SEX MYTHS", + "RECHANNELING DESTRUCTIVE IMPULSES IS A SIGN OF MATURITY", + "RECLUSES ALWAYS GET WEAK", + "REDISTRIBUTING WEALTH IS IMPERATIVE", + "RELATIVITY IS NO BOON TO MANKIND", + "RELIGION CAUSES AS MANY PROBLEMS AS IT SOLVES", + "REMEMBER YOU ALWAYS HAVE FREEDOM OF CHOICE", + "REPETITION IS THE BEST WAY TO LEARN", + "RESOLUTIONS SERVE TO EASE YOUR CONSCIENCE", + "REVOLUTION BEGINS WITH CHANGES IN THE INDIVIDUAL", + "ROMANTIC LOVE WAS INVENTED TO MANIPULATE WOMEN", + "ROUTINE IS A LINK WITH THE PAST", + "ROUTINE SMALL EXCESSES ARE WORSE THAN THE OCCASIONAL DEBAUCH", + "SACRIFICING YOURSELF FOR A BAD CAUSE IS NOT A MORAL ACT", + "SALVATION CAN'T BE BOUGHT AND SOLD", + "SELF-AWARENESS CAN BE CRIPPLING", + "SELF-CONTEMPT CAN DO MORE HARM THAN GOOD", + "SELFISHNESS IS THE MOST BASIC MOTIVATION", + "SELFLESSNESS IS THE HIGHEST ACHIEVEMENT", + "SEPARATISM IS THE WAY TO A NEW BEGINNING", + "SEX DIFFERENCES ARE HERE TO STAY", + "SIN IS A MEANS OF SOCIAL CONTROL", + "SLIPPING INTO MADNESS IS GOOD FOR THE SAKE OF COMPARISON", + "SLOPPY THINKING GETS WORSE OVER TIME", + "SOLITUDE IS ENRICHING", + "SOMETIMES SCIENCE ADVANCES FASTER THAN IT SHOULD", + "SOMETIMES THINGS SEEM TO HAPPEN OF THEIR OWN ACCORD", + "SPENDING TOO MUCH TIME ON SELF-IMPROVEMENT IS ANTISOCIAL", + "STARVATION IS NATURE'S WAY", + "STASIS IS A DREAM STATE", + "STERILIZATION IS A WEAPON OF THE RULERS", + "STRONG EMOTIONAL ATTACHMENT STEMS FROM BASIC INSECURITY", + "STUPID PEOPLE SHOULDN'T BREED", + "SURVIVAL OF THE FITTEST APPLIES TO MEN AND ANIMALS", + "SYMBOLS ARE MORE MEANINGFUL THAN THINGS THEMSELVES", + "TAKING A STRONG STAND PUBLICIZES THE OPPOSITE POSITION", + "TALKING IS USED TO HIDE ONE'S INABILITY TO ACT", + "TEASING PEOPLE SEXUALLY CAN HAVE UGLY CONSEQUENCES", + "TECHNOLOGY WILL MAKE OR BREAK US", + "THE CRUELEST DISAPPOINTMENT IS WHEN YOU LET YOURSELF DOWN", + "THE DESIRE TO REPRODUCE IS A DEATH WISH", + "THE FAMILY IS LIVING ON BORROWED TIME", + "THE IDEA OF REVOLUTION IS AN ADOLESCENT FANTASY", + "THE IDEA OF TRANSCENDENCE IS USED TO OBSCURE OPPRESSION", + "THE IDIOSYNCRATIC HAS LOST ITS AUTHORITY", + "THE MOST PROFOUND THINGS ARE INEXPRESSIBLE", + "THE MUNDANE IS TO BE CHERISHED", + "THE NEW IS NOTHING BUT A RESTATEMENT OF THE OLD", + "THE ONLY WAY TO BE PURE IS TO STAY BY YOURSELF", + "THE SUM OF YOUR ACTIONS DETERMINES WHAT YOU ARE", + "THE UNATTAINABLE IS INVARIABLY ATTRACTIVE", + "THE WORLD OPERATES ACCORDING TO DISCOVERABLE LAWS", + "THERE ARE TOO FEW IMMUTABLE TRUTHS TODAY", + "THERE'S NOTHING EXCEPT WHAT YOU SENSE", + "THERE'S NOTHING REDEEMING IN TOIL", + "THINKING TOO MUCH CAN ONLY CAUSE PROBLEMS", + "THREATENING SOMEONE SEXUALLY IS A HORRIBLE ACT", + "TIMIDITY IS LAUGHABLE", + "TO DISAGREE PRESUPPOSES MORAL INTEGRITY", + "TO VOLUNTEER IS REACTIONARY", + "TORTURE IS BARBARIC", + "TRADING A LIFE FOR A LIFE IS FAIR ENOUGH", + "TRUE FREEDOM IS FRIGHTFUL", + "UNIQUE THINGS MUST BE THE MOST VALUABLE", + "UNQUESTIONING LOVE DEMONSTRATES LARGESSE OF SPIRIT", + "USING FORCE TO STOP FORCE IS ABSURD", + "VIOLENCE IS PERMISSIBLE EVEN DESIRABLE OCCASIONALLY", + "WAR IS A PURIFICATION RITE", + "WE MUST MAKE SACRIFICES TO MAINTAIN OUR QUALITY OF LIFE", + "WHEN SOMETHING TERRIBLE HAPPENS PEOPLE WAKE UP", + "WISHING THINGS AWAY IS NOT EFFECTIVE", + "WITH PERSEVERANCE YOU CAN DISCOVER ANY TRUTH", + "WORDS TEND TO BE INADEQUATE", + "WORRYING CAN HELP YOU PREPARE", + "YOU ARE A VICTIM OF THE RULES YOU LIVE BY", + "YOU ARE GUILELESS IN YOUR DREAMS", + "YOU ARE RESPONSIBLE FOR CONSTITUTING MEANING", + "YOU ARE THE PAST PRESENT AND FUTURE", + "YOU CAN LIVE ON THROUGH YOUR DESCENDANTS", + "YOU CAN'T EXPECT PEOPLE TO BE SOMETHING THEY'RE NOT", + "YOU CAN'T FOOL OTHERS IF YOU'RE FOOLING YOURSELF", + "YOU DON'T KNOW WHAT'S WHAT UNTIL YOU SUPPORT YOURSELF", + "YOU HAVE TO HURT OTHERS TO BE EXTRAORDINARY", + "YOU MUST BE INTIMATE WITH A TOKEN FEW", + "YOU MUST DISAGREE WITH AUTHORITY FIGURES", + "YOU MUST HAVE ONE GRAND PASSION", + "YOU MUST KNOW WHERE YOU STOP AND THE WORLD BEGINS", + "YOU ONLY CAN UNDERSTAND SOMEONE OF YOUR OWN SEX", + "YOU OWE THE WORLD NOT THE OTHER WAY AROUND", + "YOU SHOULD STUDY AS MUCH AS POSSIBLE", + "YOUR ACTIONS ARE POINTLESS IF NO ONE NOTICES", + "YOUR OLDEST FEARS ARE THE WORST ONES" +] diff --git a/apps/truisms/package.json b/apps/truisms/package.json new file mode 100644 index 0000000..b13aab3 --- /dev/null +++ b/apps/truisms/package.json @@ -0,0 +1,20 @@ +{ + "name": "toes-app", + "module": "src/index.ts", + "type": "module", + "private": true, + "scripts": { + "toes": "bun run --watch index.ts", + "dev": "bun run --hot index.ts" + }, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.9.2" + }, + "dependencies": { + "hype": "git+https://git.nose.space/defunkt/hype", + "forge": "git+https://git.nose.space/defunkt/forge" + } +} diff --git a/apps/truisms/tsconfig.json b/apps/truisms/tsconfig.json new file mode 100644 index 0000000..545396c --- /dev/null +++ b/apps/truisms/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "jsxImportSource": "hono/jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/src/server/apps.ts b/src/server/apps.ts index f992191..5e290b5 100644 --- a/src/server/apps.ts +++ b/src/server/apps.ts @@ -40,25 +40,24 @@ export const runApps = () => { } } -const isApp = (dir: string): boolean => { - try { - const file = readFileSync(join(APPS_DIR, dir, 'package.json'), 'utf-8') - const json = JSON.parse(file) - return !!json.scripts?.toes - } catch (e) { - return false - } -} +const isApp = (dir: string): boolean => + Object.values(loadApp(dir)).length > 0 const loadApp = (dir: string) => { try { const file = readFileSync(join(APPS_DIR, dir, 'package.json'), 'utf-8') - const json = JSON.parse(file) - if (json.scripts?.toes) { - return json - } else { - err(dir, 'No `bun toes` script in package.json') + try { + const json = JSON.parse(file) + + if (json.scripts?.toes) { + return json + } else { + err(dir, 'No `bun toes` script in package.json') + return {} + } + } catch (e) { + err(dir, 'Invalid JSON in package.json:', e instanceof Error ? e.message : String(e)) return {} } } catch (e) { @@ -135,17 +134,22 @@ const stopApp = (dir: string) => { } const watchAppsDir = () => { - watch(APPS_DIR, (event, filename) => { - console.log('apps dir') + watch(APPS_DIR, { recursive: true }, (event, filename) => { if (!filename) return - if (isApp(filename) && !_runningApps.has(filename)) { + // Only care about package.json changes + if (!filename.endsWith('package.json')) return + + // Extract the app directory name from the path (e.g., "myapp/package.json" -> "myapp") + const dir = filename.split('/')[0]! + + if (isApp(dir) && !_runningApps.has(dir)) { const port = getPort() - runApp(filename, port) + runApp(dir, port) } - if (_runningApps.has(filename) && !isApp(filename)) - stopApp(filename) + if (_runningApps.has(dir) && !isApp(dir)) + stopApp(dir) }) }