@o-zone/scorer-core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # @o-zone/scorer-core
2
+
3
+ Orienteering scoring core library with modular exports for server, race, and competition management.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @o-zone/scorer-core
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Server Module
14
+ ```typescript
15
+ import { Scorer } from '@o-zone/scorer-core/server';
16
+ ```
17
+
18
+ ### Race Module
19
+ ```typescript
20
+ import { ... } from '@o-zone/scorer-core/race';
21
+ ```
22
+
23
+ ### Competition Module
24
+ ```typescript
25
+ import { ... } from '@o-zone/scorer-core/competition';
26
+ ```
27
+
28
+ ## Development
29
+
30
+ ### Setup
31
+ ```bash
32
+ bun install
33
+ ```
34
+
35
+ ### Build
36
+ ```bash
37
+ bun run build # Compile TypeScript → dist/
38
+ bun run build:watch # Watch mode for development
39
+ ```
40
+
41
+ ### Quality Checks
42
+ ```bash
43
+ bun run check:types # TypeScript type checking
44
+ bun run lint # Code quality with Biome
45
+ bun run lint:fix # Auto-fix lint issues
46
+ bun run format # Code formatting
47
+ ```
48
+
49
+ ## Publishing
50
+
51
+ This package uses [Changesets](https://github.com/changesets/changesets) for automated version management and npm publishing.
52
+
53
+ ### Workflow
54
+ 1. Make code changes
55
+ 2. Run `bun changeset add` to document changes
56
+ 3. Push to main branch
57
+ 4. GitHub Actions automatically:
58
+ - Creates a Release PR (bumps version, updates changelog)
59
+ - Publishes to npm when PR is merged
60
+
61
+ ### Documentation
62
+ - [Quick Reference](QUICK_REF.md) - One-page reference
63
+
64
+ ## Package Structure
65
+
66
+ ```
67
+ src/
68
+ ├── server.ts # Server module
69
+ ├── race/index.ts # Race module
70
+ └── competition/ # Competition module
71
+
72
+ dist/ # Built output (published to npm)
73
+ ```
74
+
75
+ ## Scripts
76
+
77
+ | Command | Purpose |
78
+ |---------|---------|
79
+ | `bun run build` | Build TypeScript to dist/ |
80
+ | `bun run build:watch` | Watch mode build |
81
+ | `bun run check:types` | Type check without emit |
82
+ | `bun run lint` | Lint code |
83
+ | `bun run lint:fix` | Auto-fix lint issues |
84
+ | `bun run format` | Format code |
85
+ | `bun changeset add` | Create a changeset |
86
+ | `bun run publish` | Build and publish |
87
+
88
+ ## Technologies
89
+
90
+ - **Language**: TypeScript 5.3+
91
+ - **Runtime**: Bun 1.3.5+
92
+ - **Package Manager**: Bun
93
+ - **Linter**: Biome
94
+ - **Release Management**: Changesets
95
+ - **CI/CD**: GitHub Actions
96
+ - **Module Format**: ES Modules (ESM)
97
+
98
+ ## License
99
+
100
+ MIT
101
+
102
+ ## Contributing
103
+
104
+ 1. Create a feature branch
105
+ 2. Make your changes
106
+ 3. Run `bun run lint:fix` to format
107
+ 4. Create a changeset: `bun changes`
108
+ 5. Push and create a pull request
package/dist/auth.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Check bearer authorization header. Token will be a JWT.
3
+ */
4
+ export declare const authenticate: (authorization: string | null | undefined, req: Request) => Promise<boolean>;
5
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAeA;;GAEG;AACH,eAAO,MAAM,YAAY,GACvB,eAAe,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,KAAK,OAAO,qBAkEb,CAAC"}
package/dist/auth.js ADDED
@@ -0,0 +1,148 @@
1
+ import { Buffer } from 'node:buffer';
2
+ /**
3
+ * Check bearer authorization header. Token will be a JWT.
4
+ */
5
+ export const authenticate = async (authorization, req) => {
6
+ if (!authorization) {
7
+ console.error('No authorization header');
8
+ throw new Response('Unauthorized', { status: 401 });
9
+ }
10
+ const proto = req.headers.get('X-Forwarded-Proto') ?? 'http';
11
+ const host = req.headers.get('X-Forwarded-Host') ??
12
+ req.headers.get('Host') ??
13
+ 'unknown-host';
14
+ const aud = `${proto}://${host}${new URL(req.url).pathname}`;
15
+ const token = authorization.startsWith('Bearer ')
16
+ ? authorization.slice('Bearer '.length).trim()
17
+ : authorization.trim();
18
+ const parts = token.split('.');
19
+ if (parts.length !== 3) {
20
+ throw new Response('Unauthorized', { status: 401 });
21
+ }
22
+ const [encodedHeader, encodedPayload, encodedSignature] = parts;
23
+ const header = parseJsonSection(encodedHeader);
24
+ const payload = parseJsonSection(encodedPayload);
25
+ const iss = typeof payload.iss === 'string' ? payload.iss : null;
26
+ const kid = typeof header.kid === 'string' ? header.kid : null;
27
+ const alg = typeof header.alg === 'string' ? header.alg : null;
28
+ if (!iss || !kid || !alg) {
29
+ console.error('Invalid token structure');
30
+ throw new Response('Unauthorized', { status: 401 });
31
+ }
32
+ if (process.env.AUTH_ALLOWED_ISSUERS &&
33
+ !process.env.AUTH_ALLOWED_ISSUERS.split(',')
34
+ .map((s) => s.trim())
35
+ .includes(iss)) {
36
+ console.error('Unauthorized issuer', iss);
37
+ throw new Response('Unauthorized', { status: 401 });
38
+ }
39
+ const audienceValid = payload.aud === aud ||
40
+ (Array.isArray(payload.aud) && payload.aud.includes(aud));
41
+ if (!audienceValid) {
42
+ console.error('Invalid audience', payload.aud, 'expected', aud);
43
+ throw new Response('Unauthorized', { status: 401 });
44
+ }
45
+ const jwk = await getJwkForIssuer(iss, kid);
46
+ const verified = await verifySignature(alg, jwk, `${encodedHeader}.${encodedPayload}`, encodedSignature);
47
+ if (!verified) {
48
+ console.error('Invalid signature');
49
+ throw new Response('Unauthorized', { status: 401 });
50
+ }
51
+ return true;
52
+ };
53
+ const openIdConfigCache = new Map();
54
+ const jwksCache = new Map();
55
+ const cryptoKeyCache = new Map();
56
+ const parseJsonSection = (section) => {
57
+ const bytes = base64UrlToUint8Array(section);
58
+ const text = new TextDecoder().decode(bytes);
59
+ return JSON.parse(text);
60
+ };
61
+ const base64UrlToUint8Array = (input) => {
62
+ const normalized = input.replace(/-/g, '+').replace(/_/g, '/');
63
+ const padded = normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=');
64
+ return Uint8Array.from(Buffer.from(padded, 'base64'));
65
+ };
66
+ const getOpenIdConfig = async (issuer) => {
67
+ const cached = openIdConfigCache.get(issuer);
68
+ if (cached) {
69
+ return cached;
70
+ }
71
+ const configUrl = new URL('.well-known/openid-configuration', issuer.endsWith('/') ? issuer : `${issuer}/`);
72
+ const res = await fetch(configUrl);
73
+ if (!res.ok) {
74
+ console.error('Failed to fetch OpenID config');
75
+ throw new Response('Unauthorized', { status: 401 });
76
+ }
77
+ const json = (await res.json());
78
+ if (!json.jwks_uri) {
79
+ console.error('Invalid OpenID config');
80
+ throw new Response('Unauthorized', { status: 401 });
81
+ }
82
+ openIdConfigCache.set(issuer, json.jwks_uri);
83
+ return json.jwks_uri;
84
+ };
85
+ const getJwkForIssuer = async (issuer, kid) => {
86
+ const jwksUri = await getOpenIdConfig(issuer);
87
+ const cachedKeys = jwksCache.get(jwksUri);
88
+ if (cachedKeys) {
89
+ const key = cachedKeys.find((k) => k.kid === kid);
90
+ if (key) {
91
+ return key;
92
+ }
93
+ }
94
+ const res = await fetch(jwksUri);
95
+ if (!res.ok) {
96
+ console.error('Failed to fetch JWKS');
97
+ throw new Response('Unauthorized', { status: 401 });
98
+ }
99
+ const json = (await res.json());
100
+ if (!json.keys?.length) {
101
+ console.error('Invalid JWKS');
102
+ throw new Response('Unauthorized', { status: 401 });
103
+ }
104
+ jwksCache.set(jwksUri, json.keys);
105
+ const key = json.keys.find((k) => k.kid === kid);
106
+ if (!key) {
107
+ console.error('JWK not found for kid', kid);
108
+ throw new Response('Unauthorized', { status: 401 });
109
+ }
110
+ return key;
111
+ };
112
+ const verifySignature = async (alg, jwk, signingInput, encodedSignature) => {
113
+ const cryptoAlg = getCryptoAlgorithm(alg, jwk);
114
+ const cacheKey = `${jwk.kid}:${jwk.x5t ?? ''}:${jwk.n ?? ''}`;
115
+ let cryptoKey = cryptoKeyCache.get(cacheKey);
116
+ if (!cryptoKey) {
117
+ cryptoKey = await crypto.subtle.importKey('jwk', jwk, cryptoAlg, false, [
118
+ 'verify',
119
+ ]);
120
+ cryptoKeyCache.set(cacheKey, cryptoKey);
121
+ }
122
+ const data = new TextEncoder().encode(signingInput);
123
+ const signature = base64UrlToUint8Array(encodedSignature);
124
+ return crypto.subtle.verify(cryptoAlg, cryptoKey, signature, data);
125
+ };
126
+ const getCryptoAlgorithm = (alg, jwk) => {
127
+ if (alg === 'RS256') {
128
+ return { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' };
129
+ }
130
+ if (alg === 'RS384') {
131
+ return { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-384' };
132
+ }
133
+ if (alg === 'RS512') {
134
+ return { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-512' };
135
+ }
136
+ if (alg === 'ES256') {
137
+ return { name: 'ECDSA', hash: 'SHA-256', namedCurve: jwk.crv };
138
+ }
139
+ if (alg === 'ES384') {
140
+ return { name: 'ECDSA', hash: 'SHA-384', namedCurve: jwk.crv };
141
+ }
142
+ if (alg === 'ES512') {
143
+ return { name: 'ECDSA', hash: 'SHA-512', namedCurve: jwk.crv };
144
+ }
145
+ console.error('Unsupported algorithm', alg);
146
+ throw new Response('Unauthorized', { status: 401 });
147
+ };
148
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAerC;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,aAAwC,EACxC,GAAY,EACZ,EAAE;IACF,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACzC,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,MAAM,CAAC;IAC7D,MAAM,IAAI,GACR,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;QACnC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;QACvB,cAAc,CAAC;IACjB,MAAM,GAAG,GAAG,GAAG,KAAK,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;IAE7D,MAAM,KAAK,GAAG,aAAa,CAAC,UAAU,CAAC,SAAS,CAAC;QAC/C,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE;QAC9C,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAA6B,CAAC;IAC3D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,CAAC,aAAa,EAAE,cAAc,EAAE,gBAAgB,CAAC,GAAG,KAAK,CAAC;IAChE,MAAM,MAAM,GAAG,gBAAgB,CAA0B,aAAa,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,gBAAgB,CAA0B,cAAc,CAAC,CAAC;IAE1E,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,MAAM,GAAG,GAAG,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/D,MAAM,GAAG,GAAG,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAE/D,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACzC,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,IACE,OAAO,CAAC,GAAG,CAAC,oBAAoB;QAChC,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,KAAK,CAAC,GAAG,CAAC;aACzC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,QAAQ,CAAC,GAAG,CAAC,EAChB,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;QAC1C,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,aAAa,GACjB,OAAO,CAAC,GAAG,KAAK,GAAG;QACnB,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QAChE,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,MAAM,eAAe,CACpC,GAAG,EACH,GAAG,EACH,GAAG,aAAa,IAAI,cAAc,EAAE,EACpC,gBAAgB,CACjB,CAAC;IACF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACnC,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAkB,CAAC;AACpD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAwB,CAAC;AAClD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAqB,CAAC;AAEpD,MAAM,gBAAgB,GAAG,CAAI,OAAe,EAAK,EAAE;IACjD,MAAM,KAAK,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAAC,KAAa,EAAE,EAAE;IAC9C,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAC9B,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EACvD,GAAG,CACJ,CAAC;IACF,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AACxD,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,KAAK,EAAE,MAAc,EAAE,EAAE;IAC/C,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,CACvB,kCAAkC,EAClC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,CAC7C,CAAC;IACF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC/C,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA0B,CAAC;IACzD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACvC,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7C,OAAO,IAAI,CAAC,QAAQ,CAAC;AACvB,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,KAAK,EAAE,MAAc,EAAE,GAAW,EAAE,EAAE;IAC5D,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;QAClD,IAAI,GAAG,EAAE,CAAC;YACR,OAAO,GAAG,CAAC;QACb,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACtC,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAE7B,CAAC;IACF,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC9B,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAElC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;QAC5C,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,KAAK,EAC3B,GAAW,EACX,GAAe,EACf,YAAoB,EACpB,gBAAwB,EACxB,EAAE;IACF,MAAM,SAAS,GAAG,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;IAE9D,IAAI,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE;YACtE,QAAQ;SACT,CAAC,CAAC;QACH,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;AACrE,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAE,GAAe,EAAE,EAAE;IAC1D,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACxD,CAAC;IACD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACxD,CAAC;IACD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACxD,CAAC;IACD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;IACjE,CAAC;IACD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;IACjE,CAAC;IACD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;IACjE,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;AACtD,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ export type Competition = {
2
+ id: string;
3
+ name: string;
4
+ season: number;
5
+ };
6
+ export declare function createCompetition(name: string, season: number): Competition;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/competition/index.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAEhB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAM3E"}
@@ -0,0 +1,10 @@
1
+ // Competition module exports
2
+ // Add your competition-related types and functions here
3
+ export function createCompetition(name, season) {
4
+ return {
5
+ id: Math.random().toString(36).substring(7),
6
+ name,
7
+ season,
8
+ };
9
+ }
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/competition/index.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,wDAAwD;AASxD,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,MAAc;IAC5D,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAC3C,IAAI;QACJ,MAAM;KACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { createRace, type Race } from './race/index.js';
2
+ export { createCompetition, type Competition } from './competition/index.js';
3
+ export * from './server.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC7E,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // Main entry point for @o-zone/scorer-core
2
+ export { createRace } from './race/index.js';
3
+ export { createCompetition } from './competition/index.js';
4
+ export * from './server.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,EAAE,UAAU,EAAa,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAoB,MAAM,wBAAwB,CAAC;AAC7E,cAAc,aAAa,CAAC"}
@@ -0,0 +1,7 @@
1
+ export type Race = {
2
+ id: string;
3
+ name: string;
4
+ date: Date;
5
+ };
6
+ export declare function createRace(name: string, date: Date): Race;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/race/index.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,IAAI,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;CAEZ,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAMzD"}
@@ -0,0 +1,10 @@
1
+ // Race module exports
2
+ // Add your race-related types and functions here
3
+ export function createRace(name, date) {
4
+ return {
5
+ id: Math.random().toString(36).substring(7),
6
+ name,
7
+ date,
8
+ };
9
+ }
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/race/index.ts"],"names":[],"mappings":"AAAA,sBAAsB;AACtB,iDAAiD;AASjD,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,IAAU;IACjD,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAC3C,IAAI;QACJ,IAAI;KACL,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export type RouteHandler = (req: Request) => Promise<Response>;
2
+ export declare const router: (routes: Record<string, RouteHandler>) => ((req: Request) => Promise<Response>);
3
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE/D,eAAO,MAAM,MAAM,GAEf,QAAQ,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,KACnC,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAoCtC,CAAC"}
package/dist/router.js ADDED
@@ -0,0 +1,46 @@
1
+ import { authenticate } from './auth';
2
+ const verify = async (req) => {
3
+ const token = req.headers.get('O-Zone-Endpoint-Verification');
4
+ if (!token) {
5
+ return new Response('Unauthorized', { status: 401 });
6
+ }
7
+ return new Response('Verify', {
8
+ headers: { 'O-Zone-Endpoint-Verification-Response': token },
9
+ });
10
+ };
11
+ export const router = (routes) => async (req) => {
12
+ try {
13
+ const authorization = req.headers.get('Authorization');
14
+ await authenticate(authorization, req);
15
+ switch (req.method) {
16
+ case 'OPTIONS':
17
+ return new Response(null, {
18
+ headers: {
19
+ 'Access-Control-Allow-Origin': 'null',
20
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
21
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization, O-Zone-Endpoint-Verification',
22
+ },
23
+ });
24
+ case 'GET':
25
+ return verify(req);
26
+ case 'POST': {
27
+ const url = new URL(req.url);
28
+ const routeHandler = routes[url.pathname];
29
+ if (routeHandler) {
30
+ return routeHandler(req);
31
+ }
32
+ return new Response('Not Found', { status: 404 });
33
+ }
34
+ default:
35
+ return new Response('Method Not Allowed', { status: 405 });
36
+ }
37
+ }
38
+ catch (err) {
39
+ if (err instanceof Response) {
40
+ return err;
41
+ }
42
+ console.error('Internal server error', err);
43
+ return new Response('Internal Server Error', { status: 500 });
44
+ }
45
+ };
46
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,MAAM,GAAG,KAAK,EAAE,GAAY,EAAE,EAAE;IACpC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IAC9D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE;QAC5B,OAAO,EAAE,EAAE,uCAAuC,EAAE,KAAK,EAAE;KAC5D,CAAC,CAAC;AACL,CAAC,CAAC;AAIF,MAAM,CAAC,MAAM,MAAM,GACjB,CACE,MAAoC,EACG,EAAE,CAC3C,KAAK,EAAE,GAAY,EAAE,EAAE;IACrB,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QACvD,MAAM,YAAY,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAEvC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,SAAS;gBACZ,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;oBACxB,OAAO,EAAE;wBACP,6BAA6B,EAAE,MAAM;wBACrC,8BAA8B,EAAE,oBAAoB;wBACpD,8BAA8B,EAC5B,2DAA2D;qBAC9D;iBACF,CAAC,CAAC;YACL,KAAK,KAAK;gBACR,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;YACrB,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC7B,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBAC1C,IAAI,YAAY,EAAE,CAAC;oBACjB,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;gBAC3B,CAAC;gBACD,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACpD,CAAC;YACD;gBACE,OAAO,IAAI,QAAQ,CAAC,oBAAoB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;YAC5B,OAAO,GAAG,CAAC;QACb,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;QAC5C,OAAO,IAAI,QAAQ,CAAC,uBAAuB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type RouteHandler } from './router.js';
2
+ export declare namespace Scorer {
3
+ const serve: (routes: Record<string, RouteHandler>) => void;
4
+ }
5
+ export type { RouteHandler } from './router.js';
6
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAKxD,yBAAiB,MAAM,CAAC;IACf,MAAM,KAAK,GAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,SAOzD,CAAC;CACH;AAED,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
package/dist/server.js ADDED
@@ -0,0 +1,13 @@
1
+ import { router } from './router.js';
2
+ const getPort = () => process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 3000;
3
+ export var Scorer;
4
+ (function (Scorer) {
5
+ Scorer.serve = (routes) => {
6
+ const server = Bun.serve({
7
+ port: getPort(),
8
+ fetch: router(routes),
9
+ });
10
+ console.info(`Listening on ${server.url}`);
11
+ };
12
+ })(Scorer || (Scorer = {}));
13
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,aAAa,CAAC;AAExD,MAAM,OAAO,GAAG,GAAG,EAAE,CACnB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAElE,MAAM,KAAW,MAAM,CAStB;AATD,WAAiB,MAAM;IACR,YAAK,GAAG,CAAC,MAAoC,EAAE,EAAE;QAC5D,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC;YACvB,IAAI,EAAE,OAAO,EAAE;YACf,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;SACtB,CAAC,CAAC;QAEH,OAAO,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC;AACJ,CAAC,EATgB,MAAM,KAAN,MAAM,QAStB"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@o-zone/scorer-core",
3
+ "version": "0.0.1",
4
+ "description": "Orienteering scoring core library with modular exports",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/orienteering-nz/scorer-core"
8
+ },
9
+ "license": "MIT",
10
+ "type": "module",
11
+ "exports": {
12
+ "./server": {
13
+ "import": "./dist/server.js",
14
+ "types": "./dist/server.d.ts"
15
+ },
16
+ "./race": {
17
+ "import": "./dist/race/index.js",
18
+ "types": "./dist/race/index.d.ts"
19
+ },
20
+ "./competition": {
21
+ "import": "./dist/competition/index.js",
22
+ "types": "./dist/competition/index.d.ts"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsc --project tsconfig.build.json",
30
+ "build:watch": "tsc --project tsconfig.build.json --watch",
31
+ "changes": "changeset add",
32
+ "changeset": "changeset",
33
+ "changeset:release": "changeset version && npm run build",
34
+ "check:types": "tsc --noEmit",
35
+ "format": "biome format . --write",
36
+ "lint": "biome check .",
37
+ "lint:fix": "biome check . --write",
38
+ "publish": "npm run build && changeset publish"
39
+ },
40
+ "dependencies": {
41
+ "zod": "^4.3.6"
42
+ },
43
+ "devDependencies": {
44
+ "@biomejs/biome": "^2.3.11",
45
+ "@changesets/changelog-github": "^0.5.0",
46
+ "@changesets/cli": "^2.29.8",
47
+ "@total-typescript/ts-reset": "^0.6.1",
48
+ "@types/bun": "latest",
49
+ "typescript": "^5.3.3"
50
+ },
51
+ "peerDependencies": {
52
+ "typescript": "^5"
53
+ },
54
+ "packageManager": "bun@1.3.5",
55
+ "publishConfig": {
56
+ "access": "public"
57
+ }
58
+ }