feat(typy): ship one-word typing game with tempo and shareable replays
ci / test (pull_request) Successful in 50s
ci / test (pull_request) Successful in 50s
This commit is contained in:
1 parent
4c4456168e
commit
08c86681ab
43 files changed
+2960
No files matched your search
@@ -0,0 +1,71 @@
|
||||
import { createServer } from 'node:http';
|
||||
import worker, { QuotaGate } from './src/worker.js';
|
||||
|
||||
class MemoryKV {
|
||||
constructor() { this.values = new Map(); }
|
||||
async get(key, options) {
|
||||
const item = this.values.get(key);
|
||||
if (!item || item.expiration && item.expiration < Date.now()) { this.values.delete(key); return null; }
|
||||
const type = typeof options === 'string' ? options : options?.type;
|
||||
return type === 'json' ? JSON.parse(item.value) : item.value;
|
||||
}
|
||||
async put(key, value, options = {}) {
|
||||
this.values.set(key, {value:String(value),metadata:options.metadata,expiration:options.expirationTtl ? Date.now() + options.expirationTtl * 1000 : null});
|
||||
}
|
||||
async delete(key) { this.values.delete(key); }
|
||||
async list({prefix = '', limit = 1000, cursor = ''} = {}) {
|
||||
const items = [...this.values.entries()].filter(([key, value]) => key.startsWith(prefix) && (!value.expiration || value.expiration > Date.now())).sort(([a],[b]) => a.localeCompare(b));
|
||||
const start = Number(cursor) || 0;
|
||||
return {keys:items.slice(start,start+limit).map(([name,value]) => ({name,metadata:value.metadata})),list_complete:start+limit >= items.length,cursor:start+limit < items.length ? String(start+limit) : ''};
|
||||
}
|
||||
}
|
||||
|
||||
class MemoryObjectStorage {
|
||||
constructor() { this.values = new Map(); this.queue = Promise.resolve(); }
|
||||
async get(key) { return this.values.get(key); }
|
||||
async put(key, value) { if (typeof key === 'object') { for (const [k,v] of Object.entries(key)) this.values.set(k,v); } else this.values.set(key,value); }
|
||||
async delete(key) { if (Array.isArray(key)) key.forEach(item => this.values.delete(item)); else this.values.delete(key); }
|
||||
async deleteAll() { this.values.clear(); }
|
||||
async setAlarm() {}
|
||||
async getAlarm() { return null; }
|
||||
async list({prefix = ''} = {}) { return new Map([...this.values].filter(([key]) => key.startsWith(prefix))); }
|
||||
transaction(fn) {
|
||||
const result = this.queue.then(() => fn(this));
|
||||
this.queue = result.catch(() => {});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const env = {
|
||||
REV:'local', SOURCE_SHA:'local',
|
||||
KV:new MemoryKV(),
|
||||
VENICE_API_KEY:process.env.VENICE_API_KEY || '',
|
||||
TOPIC_MODEL:process.env.TOPIC_MODEL || 'mistral-small-3-2-24b-instruct',
|
||||
TOPICS_PER_VISITOR:'8', TOPICS_PER_DAY:'80',
|
||||
SAVES_PER_VISITOR:'30', SAVES_PER_DAY:'2000',
|
||||
};
|
||||
const storage = new MemoryObjectStorage();
|
||||
const gate = new QuotaGate({storage,blockConcurrencyWhile:fn=>fn()},env);
|
||||
env.QUOTA = {idFromName:name=>name,get:()=>({fetch:(input,init)=>gate.fetch(new Request(input,init))})};
|
||||
|
||||
const port = Number(process.env.PORT || 8787);
|
||||
createServer(async (request,response) => {
|
||||
try {
|
||||
const chunks = [];
|
||||
let length = 0;
|
||||
for await (const chunk of request) {
|
||||
length += chunk.length;
|
||||
if (length > 2_000_000) { response.writeHead(413); response.end('Payload too large'); return; }
|
||||
chunks.push(chunk);
|
||||
}
|
||||
const body = chunks.length && !['GET','HEAD'].includes(request.method) ? Buffer.concat(chunks) : undefined;
|
||||
const headers = {...request.headers,'cf-connecting-ip':'127.0.0.1'};
|
||||
const result = await worker.fetch(new Request('http://' + request.headers.host + request.url, {method:request.method,headers,body}),env,{waitUntil:promise=>promise.catch(()=>{})});
|
||||
response.writeHead(result.status,Object.fromEntries(result.headers));
|
||||
response.end(Buffer.from(await result.arrayBuffer()));
|
||||
} catch(error) {
|
||||
console.error('Local request failed:',error.message);
|
||||
response.writeHead(500,{'Content-Type':'application/json'});
|
||||
response.end(JSON.stringify({error:'Local server error.'}));
|
||||
}
|
||||
}).listen(port,'127.0.0.1',()=>console.log('typy on http://127.0.0.1:' + port + ' (ephemeral local storage)'));
|
||||
Reference in new issue
Block a user