@kithinji/orca 1.0.18 → 1.0.19
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 +237 -130
- package/dist/browser/index.iife.js +70 -21
- package/dist/browser/index.iife.js.map +3 -3
- package/dist/browser/index.mjs +70 -21
- package/dist/browser/index.mjs.map +3 -3
- package/dist/types/browser/modules/router_module/navigate.d.ts +4 -0
- package/dist/types/browser/modules/router_module/navigate.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,24 +26,41 @@
|
|
|
26
26
|
|
|
27
27
|
## The Problem
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Modern web apps require two separate projects: one for the frontend, one for the backend. This means:
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
- Two repositories to maintain
|
|
32
|
+
- Two deployment pipelines
|
|
33
|
+
- Two sets of dependencies
|
|
34
|
+
- Manual API contracts that constantly drift
|
|
35
|
+
- Endless context switching between different codebases
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
Building a single feature requires touching multiple repos, keeping types in sync manually, and spending more time wiring things together than actually building.
|
|
38
|
+
|
|
39
|
+
**Orca solves this by unifying your entire stack in one codebase.**
|
|
40
|
+
|
|
41
|
+
---
|
|
34
42
|
|
|
35
43
|
## What is Orca?
|
|
36
44
|
|
|
37
|
-
Orca is a full-stack TypeScript framework that lets you build your entire application
|
|
45
|
+
Orca is a full-stack TypeScript framework that lets you build your entire application (API, business logic, and UI) in a single codebase.
|
|
46
|
+
|
|
47
|
+
Write your backend services, frontend components, and API endpoints together with shared types and zero boilerplate.
|
|
48
|
+
|
|
49
|
+
### Inspired By
|
|
50
|
+
|
|
51
|
+
- **NestJS** for backend architecture (dependency injection, decorators, modules)
|
|
52
|
+
- **Angular** for frontend structure (class-based components, clear organization)
|
|
53
|
+
- **Islands Architecture** for optimal performance (server-render by default, hydrate selectively)
|
|
38
54
|
|
|
39
|
-
|
|
55
|
+
### How It Works
|
|
40
56
|
|
|
41
|
-
|
|
57
|
+
Orca uses three simple directives to control where code runs:
|
|
42
58
|
|
|
43
|
-
|
|
59
|
+
**1. Default: Server Components**
|
|
60
|
+
|
|
61
|
+
Components render on the server by default. Fast initial loads, works without JavaScript.
|
|
44
62
|
|
|
45
63
|
```tsx
|
|
46
|
-
// Server-rendered by default - fast, works without JavaScript
|
|
47
64
|
@Component()
|
|
48
65
|
export class ProductList {
|
|
49
66
|
constructor(private products: ProductService) {}
|
|
@@ -52,7 +69,7 @@ export class ProductList {
|
|
|
52
69
|
const items = await this.products.getAll();
|
|
53
70
|
return (
|
|
54
71
|
<div>
|
|
55
|
-
{items.map(
|
|
72
|
+
{items.map(item => (
|
|
56
73
|
<ProductCard key={item.id} product={item} />
|
|
57
74
|
))}
|
|
58
75
|
</div>
|
|
@@ -61,15 +78,18 @@ export class ProductList {
|
|
|
61
78
|
}
|
|
62
79
|
```
|
|
63
80
|
|
|
81
|
+
**2. `"use interactive"` for Client Components**
|
|
82
|
+
|
|
83
|
+
Add this directive when you need client-side interactivity like click handlers, forms, or animations.
|
|
84
|
+
|
|
64
85
|
```tsx
|
|
65
|
-
// "use interactive" creates islands of interactivity
|
|
66
86
|
"use interactive";
|
|
67
87
|
import { Component, signal } from "@kithinji/orca";
|
|
68
88
|
|
|
69
89
|
@Component()
|
|
70
90
|
export class AddToCartButton {
|
|
71
91
|
constructor(private cart: CartService) {}
|
|
72
|
-
|
|
92
|
+
|
|
73
93
|
props!: { productId: number };
|
|
74
94
|
private adding = signal(false);
|
|
75
95
|
|
|
@@ -90,20 +110,23 @@ export class AddToCartButton {
|
|
|
90
110
|
}
|
|
91
111
|
```
|
|
92
112
|
|
|
113
|
+
**3. `"use public"` for Auto-Generated APIs**
|
|
114
|
+
|
|
115
|
+
Mark services with this directive to automatically create type-safe API endpoints.
|
|
116
|
+
|
|
93
117
|
```tsx
|
|
94
|
-
// "use public" creates type-safe API endpoints automatically
|
|
95
118
|
"use public";
|
|
96
119
|
import { Injectable } from "@kithinji/orca";
|
|
97
120
|
|
|
98
121
|
@Injectable()
|
|
99
122
|
export class CartService {
|
|
123
|
+
// This becomes: POST /cart/addItem
|
|
100
124
|
public async addItem(productId: number) {
|
|
101
|
-
// This code runs on the server
|
|
102
|
-
// But can be called from the client like a local function
|
|
103
125
|
const item = await this.db.products.findUnique({ where: { productId } });
|
|
104
126
|
return this.db.cart.create({ data: { productId, quantity: 1 } });
|
|
105
127
|
}
|
|
106
128
|
|
|
129
|
+
// This becomes: GET /cart/getTotal
|
|
107
130
|
public async getTotal() {
|
|
108
131
|
return this.db.cart.aggregate({ _sum: { price: true } });
|
|
109
132
|
}
|
|
@@ -112,48 +135,48 @@ export class CartService {
|
|
|
112
135
|
|
|
113
136
|
When you call `this.cart.addItem()` from a client component, Orca automatically:
|
|
114
137
|
|
|
115
|
-
1. Generates
|
|
116
|
-
2.
|
|
117
|
-
3.
|
|
118
|
-
4.
|
|
119
|
-
|
|
120
|
-
**You never write `fetch()` calls. The types never drift.**
|
|
138
|
+
1. Generates the `/cart/addItem` endpoint
|
|
139
|
+
2. Serializes your function call into a fetch request
|
|
140
|
+
3. Validates input on the server
|
|
141
|
+
4. Executes your server-side logic
|
|
142
|
+
5. Returns the typed response
|
|
121
143
|
|
|
122
|
-
|
|
144
|
+
**No manual API calls. No type drift. Just call methods like they're local.**
|
|
123
145
|
|
|
124
|
-
|
|
125
|
-
- **Type-Safe APIs**: Mark services with `"use public"` to auto-generate typed API endpoints. No manual fetch calls
|
|
126
|
-
- **Dependency Injection**: Services, controllers, and components work together with clean separation of concerns
|
|
127
|
-
- **Stack-Based Navigation**: Push components onto a navigation stack instead of wrestling with file-based routing
|
|
128
|
-
- **Shared TypeScript Types**: Your types never drift because they're literally the same types across client and server
|
|
129
|
-
- **Decorator-Based APIs**: Express your intent clearly with TypeScript decorators
|
|
130
|
-
- **Built-in Validation**: Request validation using schemas like Zod
|
|
131
|
-
- **Modular Architecture**: Organize code by feature, not by technical layer
|
|
146
|
+
---
|
|
132
147
|
|
|
133
|
-
## Why
|
|
148
|
+
## Why Use Orca?
|
|
134
149
|
|
|
135
|
-
###
|
|
150
|
+
### Type Safety Across Your Entire Stack
|
|
136
151
|
|
|
137
|
-
|
|
152
|
+
Your types never drift because they're literally the same types. Change a service method's signature and TypeScript catches every call site instantly, whether it's in a server component, client component, or controller.
|
|
138
153
|
|
|
139
|
-
###
|
|
154
|
+
### Less Boilerplate, More Features
|
|
140
155
|
|
|
141
|
-
|
|
156
|
+
```tsx
|
|
157
|
+
// Traditional approach: Define API route, write fetch call, handle errors
|
|
158
|
+
const response = await fetch('/api/cart/add', {
|
|
159
|
+
method: 'POST',
|
|
160
|
+
headers: { 'Content-Type': 'application/json' },
|
|
161
|
+
body: JSON.stringify({ productId })
|
|
162
|
+
});
|
|
163
|
+
const data = await response.json();
|
|
142
164
|
|
|
143
|
-
Orca
|
|
165
|
+
// Orca: Just call the method
|
|
166
|
+
await this.cart.addItem(productId);
|
|
167
|
+
```
|
|
144
168
|
|
|
145
|
-
###
|
|
169
|
+
### Islands Architecture for Performance
|
|
146
170
|
|
|
147
|
-
|
|
171
|
+
Server-render everything by default for fast initial loads. Add `"use interactive"` only to components that need client-side JavaScript. Your page stays lightweight while still providing rich interactivity where needed.
|
|
148
172
|
|
|
149
|
-
|
|
173
|
+
### Clear Architecture That Scales
|
|
150
174
|
|
|
151
|
-
|
|
175
|
+
Dependency injection, decorators, and modules keep your code organized as your application grows. No more tangled mess of utility functions and scattered business logic.
|
|
152
176
|
|
|
153
|
-
|
|
177
|
+
---
|
|
154
178
|
|
|
155
|
-
|
|
156
|
-
- A text editor (VS Code recommended)
|
|
179
|
+
## Quick Start
|
|
157
180
|
|
|
158
181
|
### Installation
|
|
159
182
|
|
|
@@ -163,7 +186,7 @@ Install the Orca CLI globally:
|
|
|
163
186
|
npm install -g @kithinji/pod
|
|
164
187
|
```
|
|
165
188
|
|
|
166
|
-
### Create
|
|
189
|
+
### Create a New Project
|
|
167
190
|
|
|
168
191
|
```bash
|
|
169
192
|
pod new my-app
|
|
@@ -171,7 +194,14 @@ cd my-app
|
|
|
171
194
|
npm run dev
|
|
172
195
|
```
|
|
173
196
|
|
|
174
|
-
|
|
197
|
+
Open your browser to `http://localhost:8080` to see your app.
|
|
198
|
+
|
|
199
|
+
### Requirements
|
|
200
|
+
|
|
201
|
+
- Node.js v20.9 or higher
|
|
202
|
+
- A text editor (VS Code recommended)
|
|
203
|
+
|
|
204
|
+
---
|
|
175
205
|
|
|
176
206
|
## Project Structure
|
|
177
207
|
|
|
@@ -179,31 +209,34 @@ Visit `http://localhost:8080` to see your application running. 🎉
|
|
|
179
209
|
src/
|
|
180
210
|
├── features/
|
|
181
211
|
│ ├── user/
|
|
182
|
-
│ │ ├── user.
|
|
183
|
-
│ │ ├── user.
|
|
184
|
-
│ │ ├── pages/
|
|
212
|
+
│ │ ├── user.service.ts # Business logic
|
|
213
|
+
│ │ ├── user.controller.ts # HTTP endpoints (optional)
|
|
214
|
+
│ │ ├── pages/
|
|
185
215
|
│ │ │ ├── user-profile.page.tsx
|
|
186
216
|
│ │ │ └── user-login.page.tsx
|
|
187
|
-
│ │ ├── components/
|
|
217
|
+
│ │ ├── components/
|
|
188
218
|
│ │ │ └── avatar.component.tsx
|
|
189
|
-
│ │ └── user.module.ts
|
|
219
|
+
│ │ └── user.module.ts # Feature module
|
|
190
220
|
│ └── product/
|
|
191
221
|
│ ├── product.service.ts
|
|
192
222
|
│ ├── components/
|
|
193
223
|
│ └── product.module.ts
|
|
194
|
-
└── app.module.ts
|
|
224
|
+
└── app.module.ts # Root module
|
|
195
225
|
```
|
|
196
226
|
|
|
227
|
+
Organize by feature, not by technical layer. Everything related to "products" lives in the products folder.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
197
231
|
## Core Concepts
|
|
198
232
|
|
|
199
|
-
###
|
|
233
|
+
### Services (Business Logic Layer)
|
|
200
234
|
|
|
201
|
-
Services contain your application
|
|
235
|
+
Services contain your application logic. They're injectable classes that can be used anywhere.
|
|
202
236
|
|
|
203
237
|
```typescript
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
import { Injectable, Signature } from "@kithinji/orca";
|
|
238
|
+
"use public";
|
|
239
|
+
import { Injectable, Signature, Observable } from "@kithinji/orca";
|
|
207
240
|
import { z } from "zod";
|
|
208
241
|
|
|
209
242
|
const GetProductSchema = z.object({ id: z.number() });
|
|
@@ -217,7 +250,7 @@ const ProductSchema = z.object({
|
|
|
217
250
|
export class ProductService {
|
|
218
251
|
constructor(private db: DatabaseService) {}
|
|
219
252
|
|
|
220
|
-
@Signature(GetProductSchema, ProductSchema)
|
|
253
|
+
@Signature(GetProductSchema, ProductSchema)
|
|
221
254
|
public async getProduct(id: number) {
|
|
222
255
|
return this.db.products.findUnique({ where: { id } });
|
|
223
256
|
}
|
|
@@ -225,19 +258,23 @@ export class ProductService {
|
|
|
225
258
|
public async listProducts() {
|
|
226
259
|
return this.db.products.findMany();
|
|
227
260
|
}
|
|
261
|
+
|
|
262
|
+
// Type annotate with Observable for Server-Sent Events
|
|
263
|
+
public stream(): Observable<number> {
|
|
264
|
+
// Implementation
|
|
265
|
+
}
|
|
228
266
|
}
|
|
229
267
|
```
|
|
230
268
|
|
|
231
|
-
|
|
269
|
+
The `@Signature` decorator defines validation schemas. Orca uses these to automatically validate requests.
|
|
232
270
|
|
|
233
|
-
Components
|
|
271
|
+
### Components (UI Layer)
|
|
234
272
|
|
|
235
|
-
|
|
273
|
+
Components are classes that return JSX from a `build()` method.
|
|
236
274
|
|
|
237
|
-
|
|
238
|
-
// product-list.component.tsx
|
|
239
|
-
import { Component } from "@kithinji/orca";
|
|
275
|
+
**Server Component:**
|
|
240
276
|
|
|
277
|
+
```tsx
|
|
241
278
|
@Component()
|
|
242
279
|
export class ProductList {
|
|
243
280
|
constructor(private products: ProductService) {}
|
|
@@ -248,7 +285,7 @@ export class ProductList {
|
|
|
248
285
|
return (
|
|
249
286
|
<div>
|
|
250
287
|
<h1>Products</h1>
|
|
251
|
-
{items.map(
|
|
288
|
+
{items.map(item => (
|
|
252
289
|
<div key={item.id}>
|
|
253
290
|
<h3>{item.name}</h3>
|
|
254
291
|
<p>${item.price}</p>
|
|
@@ -261,21 +298,27 @@ export class ProductList {
|
|
|
261
298
|
}
|
|
262
299
|
```
|
|
263
300
|
|
|
264
|
-
**Interactive Component
|
|
301
|
+
**Interactive Component:**
|
|
265
302
|
|
|
266
303
|
```tsx
|
|
267
|
-
// add-to-cart-button.component.tsx
|
|
268
304
|
"use interactive";
|
|
269
|
-
import { Component, signal } from "@kithinji/orca";
|
|
305
|
+
import { Component, signal, toSignal } from "@kithinji/orca";
|
|
270
306
|
|
|
271
307
|
@Component()
|
|
272
308
|
export class AddToCartButton {
|
|
273
|
-
constructor(
|
|
309
|
+
constructor(
|
|
310
|
+
private cart: CartService,
|
|
311
|
+
private products: ProductService
|
|
312
|
+
) {}
|
|
274
313
|
|
|
275
314
|
props!: { productId: number };
|
|
276
315
|
private adding = signal(false);
|
|
277
316
|
|
|
278
317
|
build() {
|
|
318
|
+
// Streams become EventSource for Server-Sent Events
|
|
319
|
+
const stream = this.products.stream();
|
|
320
|
+
const value = toSignal(stream, this);
|
|
321
|
+
|
|
279
322
|
return (
|
|
280
323
|
<button
|
|
281
324
|
onClick={async () => {
|
|
@@ -291,9 +334,9 @@ export class AddToCartButton {
|
|
|
291
334
|
}
|
|
292
335
|
```
|
|
293
336
|
|
|
294
|
-
###
|
|
337
|
+
### Controllers (HTTP Endpoints)
|
|
295
338
|
|
|
296
|
-
Controllers
|
|
339
|
+
Controllers define REST endpoints. You can write them manually or let Orca auto-generate them from `"use public"` services.
|
|
297
340
|
|
|
298
341
|
**Manual Controller:**
|
|
299
342
|
|
|
@@ -321,45 +364,52 @@ export class ProductController {
|
|
|
321
364
|
}
|
|
322
365
|
```
|
|
323
366
|
|
|
324
|
-
**Auto-Generated
|
|
367
|
+
**Auto-Generated:**
|
|
325
368
|
|
|
326
|
-
When you mark a service with `"use public"`, Orca
|
|
369
|
+
When you mark a service with `"use public"`, Orca creates endpoints automatically:
|
|
327
370
|
|
|
328
|
-
- `POST /
|
|
329
|
-
- `GET /
|
|
371
|
+
- `POST /product/getProduct`
|
|
372
|
+
- `GET /product/listProducts`
|
|
330
373
|
|
|
331
|
-
No controller code
|
|
374
|
+
No controller code required.
|
|
332
375
|
|
|
333
|
-
###
|
|
376
|
+
### Modules (Organizing Features)
|
|
334
377
|
|
|
335
|
-
Modules group related functionality
|
|
378
|
+
Modules group related functionality and manage dependencies.
|
|
336
379
|
|
|
337
380
|
```typescript
|
|
338
|
-
// product.module.ts
|
|
339
381
|
import { Module } from "@kithinji/orca";
|
|
340
382
|
import { ProductController } from "./product.controller";
|
|
341
383
|
import { ProductService } from "./product.service";
|
|
342
384
|
import { ProductList, AddToCartButton } from "./components";
|
|
343
385
|
|
|
344
386
|
@Module({
|
|
345
|
-
imports: [DatabaseModule],
|
|
346
|
-
controllers: [ProductController],
|
|
347
|
-
providers: [ProductService],
|
|
387
|
+
imports: [DatabaseModule], // Dependencies
|
|
388
|
+
controllers: [ProductController], // HTTP endpoints
|
|
389
|
+
providers: [ProductService], // Services
|
|
348
390
|
declarations: [ProductList, AddToCartButton], // UI components
|
|
349
|
-
exports: [ProductService, ProductList]
|
|
391
|
+
exports: [ProductService, ProductList] // What other modules can use
|
|
350
392
|
})
|
|
351
393
|
export class ProductModule {}
|
|
352
394
|
```
|
|
353
395
|
|
|
354
|
-
###
|
|
396
|
+
### Navigation (Stack-Based Routing)
|
|
355
397
|
|
|
356
|
-
Instead of file-based routing,
|
|
398
|
+
Instead of file-based routing, push components onto a navigation stack.
|
|
357
399
|
|
|
358
400
|
```tsx
|
|
359
|
-
import { Navigate } from "@kithinji/orca";
|
|
401
|
+
import { Navigate, Component } from "@kithinji/orca";
|
|
360
402
|
|
|
361
|
-
@Component(
|
|
403
|
+
@Component({
|
|
404
|
+
route: "/home/:id?location&browser*",
|
|
405
|
+
})
|
|
362
406
|
export class HomePage {
|
|
407
|
+
props!: {
|
|
408
|
+
id: string;
|
|
409
|
+
location: string;
|
|
410
|
+
browser?: string;
|
|
411
|
+
};
|
|
412
|
+
|
|
363
413
|
constructor(private navigate: Navigate) {}
|
|
364
414
|
|
|
365
415
|
build() {
|
|
@@ -375,21 +425,33 @@ export class HomePage {
|
|
|
375
425
|
}
|
|
376
426
|
```
|
|
377
427
|
|
|
378
|
-
Navigation
|
|
428
|
+
**Navigation Methods:**
|
|
379
429
|
|
|
380
|
-
- `push()` -
|
|
430
|
+
- `push()` - Navigate to a new page
|
|
381
431
|
- `pop()` - Go back to previous page
|
|
382
432
|
- `replace()` - Replace current page
|
|
383
433
|
- `popToRoot()` - Clear stack and return to root
|
|
384
434
|
- `canPop()` - Check if back navigation is possible
|
|
385
435
|
|
|
386
|
-
|
|
436
|
+
**Using `<a>` tags:**
|
|
437
|
+
|
|
438
|
+
You can still use anchor tags, but they're not type-safe:
|
|
387
439
|
|
|
388
|
-
|
|
440
|
+
```tsx
|
|
441
|
+
<a href="/home/123?location=nairobi&browser=FireFox">To Home</a>
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## Complete Example
|
|
447
|
+
|
|
448
|
+
Here's how everything works together:
|
|
389
449
|
|
|
390
450
|
```typescript
|
|
391
451
|
// cart.service.ts
|
|
392
452
|
"use public";
|
|
453
|
+
import { Injectable } from "@kithinji/orca";
|
|
454
|
+
|
|
393
455
|
@Injectable()
|
|
394
456
|
export class CartService {
|
|
395
457
|
constructor(private db: DatabaseService) {}
|
|
@@ -397,7 +459,7 @@ export class CartService {
|
|
|
397
459
|
public async addItem(productId: number, quantity: number = 1) {
|
|
398
460
|
const product = await this.db.products.findUnique({ where: { productId } });
|
|
399
461
|
return this.db.cart.create({
|
|
400
|
-
data: { productId, quantity, price: product.price }
|
|
462
|
+
data: { productId, quantity, price: product.price }
|
|
401
463
|
});
|
|
402
464
|
}
|
|
403
465
|
|
|
@@ -405,8 +467,12 @@ export class CartService {
|
|
|
405
467
|
return this.db.cart.findMany();
|
|
406
468
|
}
|
|
407
469
|
}
|
|
470
|
+
```
|
|
408
471
|
|
|
472
|
+
```tsx
|
|
409
473
|
// cart-page.component.tsx
|
|
474
|
+
import { Component } from "@kithinji/orca";
|
|
475
|
+
|
|
410
476
|
@Component()
|
|
411
477
|
export class CartPage {
|
|
412
478
|
constructor(private cart: CartService) {}
|
|
@@ -416,20 +482,26 @@ export class CartPage {
|
|
|
416
482
|
return (
|
|
417
483
|
<div>
|
|
418
484
|
<h1>Your Cart</h1>
|
|
419
|
-
{items.map(
|
|
485
|
+
{items.map(item => (
|
|
420
486
|
<CartItem key={item.id} item={item} />
|
|
421
487
|
))}
|
|
422
488
|
</div>
|
|
423
489
|
);
|
|
424
490
|
}
|
|
425
491
|
}
|
|
492
|
+
```
|
|
426
493
|
|
|
494
|
+
```tsx
|
|
427
495
|
// add-to-cart-button.component.tsx
|
|
428
|
-
|
|
496
|
+
"use interactive";
|
|
497
|
+
import { Component, Navigate } from "@kithinji/orca";
|
|
429
498
|
|
|
430
499
|
@Component()
|
|
431
500
|
export class AddToCartButton {
|
|
432
|
-
constructor(
|
|
501
|
+
constructor(
|
|
502
|
+
private cart: CartService,
|
|
503
|
+
private navigate: Navigate
|
|
504
|
+
) {}
|
|
433
505
|
|
|
434
506
|
props!: { productId: number };
|
|
435
507
|
|
|
@@ -448,56 +520,83 @@ export class AddToCartButton {
|
|
|
448
520
|
}
|
|
449
521
|
```
|
|
450
522
|
|
|
451
|
-
**What happens
|
|
523
|
+
**What happens:**
|
|
452
524
|
|
|
453
|
-
1. `CartService`
|
|
454
|
-
2. `CartPage` (server component) calls `cart.getCart()` on the server during
|
|
525
|
+
1. `CartService` has `"use public"`, so Orca generates API endpoints
|
|
526
|
+
2. `CartPage` (server component) calls `cart.getCart()` on the server during render
|
|
455
527
|
3. `AddToCartButton` (interactive component) calls `cart.addItem()` from the browser
|
|
456
|
-
4. Orca
|
|
457
|
-
5. Types are preserved
|
|
458
|
-
6. Navigation happens by pushing
|
|
528
|
+
4. Orca converts that call into `fetch('/cart/addItem', ...)`
|
|
529
|
+
5. Types are preserved everywhere. TypeScript catches errors before runtime
|
|
530
|
+
6. Navigation happens by pushing components, not URL strings
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## When to Use Orca
|
|
535
|
+
|
|
536
|
+
**Orca is great for:**
|
|
537
|
+
|
|
538
|
+
- Solo developers or small teams
|
|
539
|
+
- Building features quickly without managing two repos
|
|
540
|
+
- Internal tools, dashboards, or admin panels
|
|
541
|
+
- Highly interactive web applications
|
|
542
|
+
- Projects where you need a real API (for mobile apps, CLIs, webhooks)
|
|
543
|
+
- Teams tired of keeping types in sync between frontend and backend
|
|
544
|
+
|
|
545
|
+
**Orca might not fit if:**
|
|
546
|
+
|
|
547
|
+
- You have separate frontend and backend teams
|
|
548
|
+
- You prefer the traditional split and it works for you
|
|
549
|
+
- You need a purely client-side SPA
|
|
550
|
+
- You're building a static marketing site or blog (use Astro or Next.js)
|
|
551
|
+
- Your team is heavily invested in another stack
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
## Orca vs. Alternatives
|
|
556
|
+
|
|
557
|
+
### Why not separate repos?
|
|
459
558
|
|
|
460
|
-
|
|
559
|
+
Separate repos mean double the maintenance. Two sets of types that drift. Two deployment pipelines. Constant context switching. For solo devs or small teams, you spend more time connecting things than building.
|
|
461
560
|
|
|
462
|
-
|
|
561
|
+
### Why not Next.js?
|
|
463
562
|
|
|
464
|
-
-
|
|
465
|
-
- Want to ship features without maintaining two separate projects
|
|
466
|
-
- Value clear architecture and separation of concerns
|
|
467
|
-
- Need a real API that can grow (mobile apps, CLIs, integrations)
|
|
468
|
-
- Are tired of context switching between frontend and backend mental models
|
|
469
|
-
- Build internal tools, dashboards, or admin panels
|
|
470
|
-
- You're building highly interactive web apps
|
|
563
|
+
Next.js excels at rendering UI fast, but it's optimized for frontend-first development. The moment you need a proper backend with controllers, services, and dependency injection, things get messy. Server Actions blur the lines between UI and backend in ways that make larger apps hard to maintain.
|
|
471
564
|
|
|
472
|
-
|
|
565
|
+
Orca gives you a real backend architecture with clean separation of concerns, plus UI rendering that integrates naturally.
|
|
473
566
|
|
|
474
|
-
|
|
475
|
-
- You prefer the frontend/backend split and it works for your workflow
|
|
476
|
-
- You need a purely client-side SPA framework
|
|
477
|
-
- You're building a marketing site or content-heavy blog (use Astro or Next.js)
|
|
478
|
-
- Your team is already deeply invested in another stack
|
|
567
|
+
### Why not HTMX?
|
|
479
568
|
|
|
480
|
-
|
|
569
|
+
HTMX is elegant for server-rendered apps. But endpoints that return HTML lock you in. When you need a mobile app, CLI, or webhook consumer, you either build a second JSON API or parse HTML on the client.
|
|
570
|
+
|
|
571
|
+
Orca's API returns JSON by default. UI rendering is a layer on top, not a replacement. You're never painted into a corner.
|
|
572
|
+
|
|
573
|
+
---
|
|
481
574
|
|
|
482
575
|
## Philosophy
|
|
483
576
|
|
|
484
|
-
|
|
577
|
+
Orca rejects the artificial divide between frontend and backend. For large teams with dedicated specialists, that split makes sense. But for solo developers and small teams, it doubles the work.
|
|
485
578
|
|
|
486
|
-
|
|
579
|
+
### Core Principles
|
|
487
580
|
|
|
488
|
-
|
|
489
|
-
- **Islands of interactivity**: Server-render by default, add JavaScript only where needed
|
|
490
|
-
- **Type-safe APIs**: No manual fetch calls, no drifting types
|
|
491
|
-
- **Clear architecture**: DI, decorators, and modules keep large apps maintainable
|
|
492
|
-
- **Stack-based navigation**: Route by pushing components, not by folder structure
|
|
581
|
+
**One Codebase** - Write features end to end in one place
|
|
493
582
|
|
|
494
|
-
|
|
583
|
+
**Islands of Interactivity** - Server-render by default, add JavaScript only where needed
|
|
584
|
+
|
|
585
|
+
**Type-Safe APIs** - No manual fetch calls, no drifting types
|
|
586
|
+
|
|
587
|
+
**Clear Architecture** - Dependency injection, decorators, and modules keep large apps maintainable
|
|
588
|
+
|
|
589
|
+
**Stack-Based Navigation** - Route by pushing components, not folder structures
|
|
590
|
+
|
|
591
|
+
This is opinionated software. It has rules, structure, and conventions. The rules exist to free you from decision fatigue so you can focus on building.
|
|
592
|
+
|
|
593
|
+
---
|
|
495
594
|
|
|
496
595
|
## Documentation
|
|
497
596
|
|
|
498
|
-
Full guides, API references, and examples at **
|
|
597
|
+
Full guides, API references, and examples at [**orca.dafifi.net**](https://orca.dafifi.net/)
|
|
499
598
|
|
|
500
|
-
Topics covered
|
|
599
|
+
**Topics covered:**
|
|
501
600
|
|
|
502
601
|
- Components and JSX
|
|
503
602
|
- Dependency Injection
|
|
@@ -506,30 +605,38 @@ Topics covered:
|
|
|
506
605
|
- The `"use interactive"` directive
|
|
507
606
|
- The `"use public"` directive
|
|
508
607
|
- Signals and Reactivity
|
|
509
|
-
- Observables
|
|
608
|
+
- Observables and Server-Sent Events
|
|
510
609
|
- Navigation Stack
|
|
511
610
|
- Validation with Zod
|
|
512
611
|
|
|
612
|
+
---
|
|
613
|
+
|
|
513
614
|
## Roadmap
|
|
514
615
|
|
|
515
616
|
- [ ] Database integrations (TypeORM, Prisma)
|
|
516
|
-
- [ ] Authentication
|
|
617
|
+
- [ ] Authentication and authorization modules
|
|
517
618
|
- [ ] WebSocket support
|
|
518
619
|
- [ ] GraphQL adapter
|
|
519
620
|
- [ ] CLI scaffolding improvements
|
|
520
621
|
- [ ] File upload handling
|
|
521
|
-
- [ ] Background jobs
|
|
622
|
+
- [ ] Background jobs and task queues
|
|
623
|
+
|
|
624
|
+
---
|
|
522
625
|
|
|
523
626
|
## Contributing
|
|
524
627
|
|
|
525
628
|
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
|
|
526
629
|
|
|
527
|
-
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
## Stay Connected
|
|
528
633
|
|
|
529
634
|
- **Author**: [Kithinji Brian](https://www.linkedin.com/in/kithinjibrian/)
|
|
530
635
|
- **Website**: [orca.dafifi.net](https://orca.dafifi.net/)
|
|
531
636
|
- **NPM**: [@kithinji/orca](https://www.npmjs.com/package/@kithinji/orca)
|
|
532
637
|
|
|
638
|
+
---
|
|
639
|
+
|
|
533
640
|
## License
|
|
534
641
|
|
|
535
642
|
MIT
|
|
@@ -538,4 +645,4 @@ MIT
|
|
|
538
645
|
|
|
539
646
|
<p align="center">
|
|
540
647
|
Made in Nairobi with ❤️ for developers who just want to ship
|
|
541
|
-
</p>
|
|
648
|
+
</p>
|