svelte-origin 1.0.0-next.48 → 1.0.0-next.49

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/LLM.md CHANGED
@@ -1,540 +1,545 @@
1
- # svelte-origin - LLM Reference
2
-
3
- > Quick-reference documentation for LLM assistance with svelte-origin v3.0
4
-
5
- ## Overview
6
-
7
- `svelte-origin` provides a macro-based system for defining reusable state + props + methods patterns in Svelte 5. Origins are factories that create reactive instances with props, state, derived values, and methods.
8
-
9
- **IMPORTANT**: This library uses **compile-time macros** (prefixed with `$`). These are transformed during build and do NOT exist at runtime.
10
-
11
- ---
12
-
13
- ## The `$origin` Namespace
14
-
15
- All macros live under the `$origin` namespace:
16
-
17
- | Macro | Context | Description |
18
- |-------|---------|-------------|
19
- | `$origin({...})` | `.svelte.ts` files | Define an origin factory |
20
- | `$origin.props({...})` | Anywhere | Define props schema with defaults |
21
- | `$origin.component(Factory)` | ⚠️ `.svelte` only | Create origin from component `$props()` |
22
- | `$origin.create(Factory, {...})` | ✅ Everywhere | Programmatic origin creation with reactivity |
23
- | `$origin.bind(variable)` | Inside `$origin.create()` | Bind prop to existing reactive variable |
24
- | `$origin.for(Factory)` | ⚠️ `.svelte` only | Forward props to child origin/element |
25
- | `$origin.Props<typeof Factory>` | Type context | Extract component props type |
26
- | `$origin.Of<typeof Factory>` | Type context | Extract origin instance type |
27
- | `$bindable(value)` | Inside `$origin.props({})` | Mark a prop as two-way bindable |
28
-
29
- ### Context Restrictions
30
-
31
- - ⚠️ **Component-only macros** (`$origin.component()`, `$origin.for()`): Using these in `.svelte.ts` or `.svelte.js` module files will throw a helpful compile-time error
32
- - ✅ **Universal macros** (`$origin.create()`, `$origin.props()`): Work in any context
33
-
34
- ---
35
-
36
- ## Defining Origins (`.svelte.ts` files)
37
-
38
- Origins are defined in `.svelte.ts` files using the `$origin()` macro:
39
-
40
- ```ts
41
- // counter.svelte.ts
42
- export const Counter = $origin({
43
- props: $origin.props({
44
- label: 'Counter', // Default string
45
- count: $bindable(0), // Bindable number (two-way)
46
- step: 1 // Default number
47
- }),
48
-
49
- // Internal state (not exposed - prefix with _)
50
- _history: [] as number[],
51
-
52
- // Derived values (use getters + $derived)
53
- get doubled() {
54
- return $derived(this.props.count * 2)
55
- },
56
-
57
- // Methods
58
- increment() {
59
- this._history.push(this.props.count)
60
- this.props.count += this.props.step
61
- },
62
-
63
- decrement() {
64
- this._history.push(this.props.count)
65
- this.props.count -= this.props.step
66
- }
67
- })
68
- ```
69
-
70
- ### Key Rules for Origin Definitions
71
-
72
- 1. **Use `this.props.*`** to access props (not `props.*`)
73
- 2. **Use `$derived()`** for computed/derived values (must be inside a getter)
74
- 3. **Use `$bindable(defaultValue)`** for two-way bindable props
75
- 4. **Prefix with `_`** for internal/private state (convention - excluded from public types)
76
- 5. **Never use `$state()`** for props - props are automatically reactive
77
- 6. **Use `$state()`** for internal state (non-props) that needs reactivity
78
-
79
- ---
80
-
81
- ## Using Origins in Components
82
-
83
- ### `$origin.component(Factory)` — Standard Component Usage
84
-
85
- **This is the ONLY way to use origins inside `.svelte` components when you want props to flow from the parent.**
86
-
87
- > ⚠️ **Component-only:** This macro throws an error if used in `.svelte.ts` module files.
88
-
89
- ```svelte
90
- <!-- Counter.svelte -->
91
- <script lang="ts">
92
- import { Counter } from './counter.svelte'
93
-
94
- let counter = $origin.component(Counter)
95
- </script>
96
-
97
- <div>
98
- <span>{counter.props.label}: {counter.props.count}</span>
99
- <button onclick={counter.increment}>+{counter.props.step}</button>
100
- <button onclick={counter.decrement}>-{counter.props.step}</button>
101
- <small>Doubled: {counter.doubled}</small>
102
- </div>
103
- ```
104
-
105
- **What happens at compile time:**
106
-
107
- ```svelte
108
- <script lang="ts">
109
- import { Counter } from './counter.svelte'
110
-
111
- type $$Props = $origin.Props<typeof Counter>
112
- let { label = 'Counter', count = $bindable(0), step = 1 } = $props()
113
- let __attrs = {
114
- get label() { return label },
115
- get count() { return count },
116
- set count(v) { count = v },
117
- get step() { return step }
118
- }
119
- let counter = Counter(__attrs)
120
- </script>
121
- ```
122
-
123
- The component's props are **automatically extracted** from the origin's schema. The parent component can pass these props normally:
124
-
125
- ```svelte
126
- <Counter label="My Counter" count={5} step={2} />
127
- ```
128
-
129
- ---
130
-
131
- ## Programmatic Origin Creation
132
-
133
- ### ⚠️ NEVER Call the Factory Directly
134
-
135
- **WRONG** - This DOES NOT maintain reactivity:
136
-
137
- ```ts
138
- // ❌ BAD - Never do this!
139
- const counter = Counter({
140
- count: 0,
141
- label: 'Test'
142
- })
143
- // counter.props.count = 5 ← This WON'T trigger reactivity!
144
- ```
145
-
146
- The input object `{ count: 0, label: 'Test' }` is a **plain object**, not a reactive proxy. Setting `counter.props.count` won't trigger Svelte's reactivity system.
147
-
148
- ### ✅ Use `$origin.create()` Instead
149
-
150
- **CORRECT** - Use `$origin.create()` for programmatic creation:
151
-
152
- ```ts
153
- // ✅ GOOD - Always use $origin.create()
154
- const counter = $origin.create(Counter, {
155
- count: 0,
156
- label: 'Test'
157
- })
158
- // counter.props.count ← Readable, but NOT settable (read-only)
159
- // counter.increment() ← Methods can still modify props internally!
160
- ```
161
-
162
- > **Note:** Props passed directly are **read-only** (getter-only). To set props externally, use `$origin.bind()`.
163
-
164
- ### `$origin.create()` Works Everywhere
165
-
166
- Unlike `$origin.component()` which only works in `.svelte` files, `$origin.create()` can be used in:
167
-
168
- - ✅ `.svelte` component scripts
169
- - ✅ `.svelte.ts` / `.svelte.js` module files
170
- - ✅ Inside `$origin()` init callbacks
171
- - ✅ Inside origin method bodies
172
- - ✅ Module scripts (`<script module>`)
173
-
174
- **What happens at compile time (regular props → getter-only):**
175
-
176
- ```ts
177
- let __attrs = {
178
- // Regular props are getter-only (read-only from outside)
179
- get count() { return 0 },
180
- get label() { return 'Test' }
181
- }
182
- const counter = Counter(__attrs)
183
- ```
184
-
185
- **What happens with `$origin.bind()` (getter + setter):**
186
-
187
- ```ts
188
- let myCount = $state(0)
189
- let __attrs = {
190
- get count() { return myCount },
191
- set count(v) { myCount = v } // Two-way binding!
192
- }
193
- const counter = Counter(__attrs)
194
- ```
195
-
196
- ### `$origin.bind(variable)` — Required for Settable Props
197
-
198
- To set an origin's prop externally (or sync with existing state), you **must** use `$origin.bind()`:
199
-
200
- ```ts
201
- // In an origin's init or .svelte.ts file
202
- let sharedItems = $state(['a', 'b', 'c'])
203
-
204
- const list = $origin.create(ListOrigin, {
205
- items: $origin.bind(sharedItems) // Two-way binding!
206
- })
207
-
208
- // Both are now in sync:
209
- sharedItems.push('d') // list.props.items now includes 'd'
210
- list.props.items = ['x'] // sharedItems is now ['x']
211
- ```
212
-
213
- Mix bound and unbound props:
214
-
215
- ```ts
216
- let count = $state(0)
217
-
218
- const counter = $origin.create(Counter, {
219
- count: $origin.bind(count), // Settable (two-way)
220
- label: 'My Counter', // Read-only
221
- step: 2 // Read-only
222
- })
223
-
224
- counter.props.count = 10 // ✅ Works - updates `count` too
225
- counter.props.label = 'New' // ❌ No effect (read-only)
226
- ```
227
-
228
- ---
229
-
230
- ## Prop Forwarding
231
-
232
- ### `$origin.for(Factory)` — Forward Props to Child
233
-
234
- > ⚠️ **Component-only:** This macro throws an error if used in `.svelte.ts` module files.
235
-
236
- Use in wrapper components that need to pass props through:
237
-
238
- ```svelte
239
- <!-- Wrapper.svelte -->
240
- <script lang="ts">
241
- import { Counter } from './counter.svelte'
242
- import CounterComponent from './Counter.svelte'
243
-
244
- let counterProps = $origin.for(Counter)
245
- </script>
246
-
247
- <div class="wrapper">
248
- <CounterComponent {...counterProps} />
249
- </div>
250
- ```
251
-
252
- ### `$origin.for('element')` — Forward Props to HTML Elements
253
-
254
- ```svelte
255
- <!-- CustomInput.svelte -->
256
- <script lang="ts">
257
- let inputProps = $origin.for('input')
258
- </script>
259
-
260
- <input {...inputProps} class="custom-input" />
261
- ```
262
-
263
- ---
264
-
265
- ## Inheritance
266
-
267
- Origins can extend other origins using array syntax:
268
-
269
- ```ts
270
- // base.svelte.ts
271
- export const BaseCounter = $origin({
272
- props: $origin.props({
273
- count: $bindable(0)
274
- }),
275
-
276
- increment() {
277
- this.props.count++
278
- }
279
- })
280
-
281
- // extended.svelte.ts
282
- export const ExtendedCounter = $origin([BaseCounter], {
283
- props: $origin.props({
284
- step: 1 // Additional prop (merged with parent)
285
- }),
286
-
287
- // Override with super call
288
- increment() {
289
- this.super.increment() // Call parent
290
- console.log('Incremented!')
291
- },
292
-
293
- // New method
294
- incrementByStep() {
295
- this.props.count += this.props.step
296
- }
297
- })
298
- ```
299
-
300
- ### Inheritance Rules
301
-
302
- 1. Child props are **merged** with parent props (left-to-right, child overrides)
303
- 2. Use `this.super.*` to call parent methods
304
- 3. Child methods override parent methods with same name
305
- 4. Derived values can be overridden
306
- 5. Multi-level inheritance works: `this.super.super.*` for grandparent
307
-
308
- ### Standalone Props Inheritance
309
-
310
- Props can also inherit from other props schemas:
311
-
312
- ```ts
313
- export const BaseAttrs = $origin.props({
314
- id: '',
315
- class: ''
316
- })
317
-
318
- export const WidgetAttrs = $origin.props([BaseAttrs], {
319
- label: 'Widget',
320
- active: false
321
- })
322
- // WidgetAttrs has: id, class, label, active
323
- ```
324
-
325
- ---
326
-
327
- ## Type Helpers
328
-
329
- ### `$origin.Props<typeof Factory>`
330
-
331
- Extract the component props type from an origin factory:
332
-
333
- ```svelte
334
- <script lang="ts">
335
- import { Counter } from './counter.svelte'
336
-
337
- // Explicit type annotation (optional but helps IDE)
338
- type $$Props = $origin.Props<typeof Counter>
339
-
340
- let counter = $origin.component(Counter)
341
- </script>
342
- ```
343
-
344
- ### `$origin.Of<typeof Factory>`
345
-
346
- Extract the instance type from an origin factory:
347
-
348
- ```ts
349
- import { Counter } from './counter.svelte'
350
-
351
- // Type a variable that will hold an origin instance
352
- let counter: $origin.Of<typeof Counter>
353
-
354
- // Useful for optional or lazy initialization
355
- let maybeCounter: $origin.Of<typeof Counter> | undefined = undefined
356
-
357
- // Also works with generic factories
358
- const List = <T>() => $origin({ props: $origin.props({ items: [] as T[] }) })
359
- type StringListInstance = $origin.Of<ReturnType<typeof List<string>>>
360
- ```
361
-
362
- ---
363
-
364
- ## Reactivity Internals
365
-
366
- ### How Props Become Reactive
367
-
368
- Origins use **getter/setter pairs** to maintain reactivity. The key distinction:
369
-
370
- | Source | Props become | Settable externally? |
371
- |--------|--------------|---------------------|
372
- | `$origin.component()` | Connected to `$props()` | Bindable props only |
373
- | `$origin.create({ prop: value })` | Getter-only | ❌ No |
374
- | `$origin.create({ prop: $origin.bind(var) })` | Getter + setter | ✅ Yes |
375
-
376
- **Getter-only props (default for `$origin.create`):**
377
-
378
- ```ts
379
- // Input
380
- $origin.create(Counter, { count: 0 })
381
-
382
- // Output - getter-only
383
- let __attrs = { get count() { return 0 } }
384
- ```
385
-
386
- **Getter + setter props (with `$origin.bind`):**
387
-
388
- ```ts
389
- // Input
390
- let count = $state(0)
391
- $origin.create(Counter, { count: $origin.bind(count) })
392
-
393
- // Output - getter AND setter
394
- let __attrs = {
395
- get count() { return count },
396
- set count(v) { count = v }
397
- }
398
- ```
399
-
400
- ### Why Internal Methods Still Work
401
-
402
- Even though external setting doesn't work for regular props, **origin methods** can still modify props internally because they work through `this.props.*` which bypasses the external getter-only constraint:
403
-
404
- ```ts
405
- // Origin method can modify props
406
- increment() {
407
- this.props.count++ // Works - internal modification
408
- }
409
- ```
410
-
411
- ---
412
-
413
- ## Common Patterns
414
-
415
- ### State Machine Origin
416
-
417
- ```ts
418
- export const StateMachine = $origin({
419
- props: $origin.props({
420
- initial: 'idle' as 'idle' | 'loading' | 'success' | 'error'
421
- }),
422
-
423
- _currentState: null as string | null,
424
-
425
- get state() {
426
- return $derived(this._currentState ?? this.props.initial)
427
- },
428
-
429
- transition(to: string) {
430
- this._currentState = to
431
- },
432
-
433
- get isLoading() {
434
- return $derived(this.state === 'loading')
435
- }
436
- })
437
- ```
438
-
439
- ### Form Field Origin
440
-
441
- ```ts
442
- export const FormField = $origin({
443
- props: $origin.props({
444
- value: $bindable(''),
445
- label: '',
446
- required: false,
447
- validate: ((v: string) => true) as (v: string) => boolean | string
448
- }),
449
-
450
- _touched: false,
451
-
452
- get error() {
453
- if (!this._touched) return null
454
- const result = this.props.validate(this.props.value)
455
- return $derived(result === true ? null : result)
456
- },
457
-
458
- get isValid() {
459
- return $derived(this.error === null)
460
- },
461
-
462
- touch() {
463
- this._touched = true
464
- },
465
-
466
- reset() {
467
- this.props.value = ''
468
- this._touched = false
469
- }
470
- })
471
- ```
472
-
473
- ### Composing Origins
474
-
475
- ```ts
476
- // Create child origin inside parent using init callback
477
- export const Parent = $origin({
478
- props: $origin.props({
479
- items: [] as string[]
480
- }),
481
-
482
- // Child origin with bound props
483
- _child: null as ReturnType<typeof ChildOrigin> | null
484
- }, function() {
485
- // Init callback - runs when instance is created
486
- this._child = $origin.create(ChildOrigin, {
487
- items: $origin.bind(this.props.items) // Sync with parent
488
- })
489
- })
490
- ```
491
-
492
- ---
493
-
494
- ## Quick Reference
495
-
496
- ### File Types & Allowed Macros
497
-
498
- | Extension | Purpose | Macros Available |
499
- |-----------|---------|------------------|
500
- | `.svelte.ts` | Origin definitions | `$origin()`, `$origin.props()`, `$origin.create()`, `$origin.bind()`, `$bindable()` |
501
- | `.svelte` | Svelte components | `$origin.component()`, `$origin.for()`, `$origin.create()`, `$origin.bind()` |
502
-
503
- ### Context-Specific Macros
504
-
505
- | Macro | `.svelte` | `.svelte.ts` | Init callbacks |
506
- |-------|:---------:|:------------:|:--------------:|
507
- | `$origin.component()` | | throws | throws |
508
- | `$origin.for()` | ✅ | ❌ throws | ❌ throws |
509
- | `$origin.create()` | ✅ | | |
510
- | `$origin.bind()` | ✅ | | |
511
- | `$origin.props()` | ✅ | ✅ | ✅ |
512
-
513
- ### Do's and Don'ts
514
-
515
- | DO | ❌ DON'T |
516
- |-------|----------|
517
- | Use `$origin.component(Factory)` in `.svelte` components | Use `$origin.component()` in `.svelte.ts` files |
518
- | Use `$origin.create(Factory, props)` for programmatic creation | Call `Factory({...})` directly (breaks reactivity) |
519
- | Use `$origin.bind(variable)` for two-way shared state | Pass reactive variables without binding |
520
- | Use `$derived()` for computed values in getters | Use `$state()` for props (they're already reactive) |
521
- | Access props via `this.props.*` | Destructure props at top level of definition |
522
- | Prefix private state with `_` | Expose internal state publicly |
523
- | Use `$origin.Of<typeof F>` for instance types | Use `ReturnType<typeof F>` (less inference) |
524
- | Use `$origin.Props<typeof F>` for props types | Manually define props types |
525
-
526
- ### Common LLM Pitfalls
527
-
528
- 1. **Calling factory directly** - `Counter({...})` loses reactivity. Always use `$origin.create()` or `$origin.component()`.
529
-
530
- 2. **Using `$origin.component()` in module files** - This only works in `.svelte` files. In `.svelte.ts`, use `$origin.create()`.
531
-
532
- 3. **Using `$origin.for()` in module files** - This only works in `.svelte` files.
533
-
534
- 4. **Forgetting `this.props`** - Inside origins, always use `this.props.count`, not just `count` or `props.count`.
535
-
536
- 5. **Using `$state()` for props** - Props passed to `$origin.props({...})` are automatically reactive. Don't wrap defaults in `$state()`.
537
-
538
- 6. **Missing `$derived()` in getters** - Computed values must use `$derived()` inside getters for reactivity.
539
-
540
- 7. **Expecting settable props without `$origin.bind()`** - Props in `$origin.create({ prop: value })` are **read-only**. To set props externally, use `$origin.bind(variable)`.
1
+ # svelte-origin - LLM Reference
2
+
3
+ > Quick-reference documentation for LLM assistance with svelte-origin v3.0
4
+
5
+ ## Overview
6
+
7
+ `svelte-origin` provides a macro-based system for defining reusable state + props + methods patterns in Svelte 5. Origins are factories that create reactive instances with props, state, derived values, and methods.
8
+
9
+ **IMPORTANT**: This library uses **compile-time macros** (prefixed with `$`). These are transformed during build and do NOT exist at runtime.
10
+
11
+ ---
12
+
13
+ ## The `$origin` Namespace
14
+
15
+ All macros live under the `$origin` namespace:
16
+
17
+ | Macro | Context | Description |
18
+ |-------|---------|-------------|
19
+ | `$origin({...})` | `.svelte.ts` files | Define an origin factory |
20
+ | `$origin.props({...})` | Anywhere | Define props schema with defaults |
21
+ | `$origin.component(Factory)` | ⚠️ `.svelte` only | Create origin from component `$props()` |
22
+ | `$origin.create(Factory, {...})` | ✅ Everywhere | Programmatic origin creation with reactivity |
23
+ | `$origin.bind(variable)` | Inside `$origin.create()` | Bind prop to existing reactive variable |
24
+ | `$origin.for(Factory)` | ⚠️ `.svelte` only | Forward props to child origin/element |
25
+ | `$origin.Props<typeof Factory>` | Type context | Extract component props type |
26
+ | `$origin.Of<typeof Factory>` | Type context | Extract origin instance type |
27
+ | `$bindable(value)` | Inside `$origin.props({})` | Mark a prop as two-way bindable |
28
+
29
+ ### Context Restrictions
30
+
31
+ - ⚠️ **Component-only macros** (`$origin.component()`, `$origin.for()`): Using these in `.svelte.ts` or `.svelte.js` module files will throw a helpful compile-time error
32
+ - ✅ **Universal macros** (`$origin.create()`, `$origin.props()`): Work in any context
33
+
34
+ ---
35
+
36
+ ## Defining Origins (`.svelte.ts` files)
37
+
38
+ Origins are defined in `.svelte.ts` files using the `$origin()` macro:
39
+
40
+ ```ts
41
+ // counter.svelte.ts
42
+ export const Counter = $origin({
43
+ props: $origin.props({
44
+ label: 'Counter', // Default string
45
+ count: $bindable(0), // Bindable number (two-way)
46
+ step: 1 // Default number
47
+ }),
48
+
49
+ // Internal state (not exposed - prefix with _)
50
+ _history: [] as number[],
51
+
52
+ // Derived values (use getters + $derived)
53
+ get doubled() {
54
+ return $derived(this.props.count * 2)
55
+ },
56
+
57
+ // Methods
58
+ increment() {
59
+ this._history.push(this.props.count)
60
+ this.props.count += this.props.step
61
+ },
62
+
63
+ decrement() {
64
+ this._history.push(this.props.count)
65
+ this.props.count -= this.props.step
66
+ }
67
+ })
68
+ ```
69
+
70
+ ### Key Rules for Origin Definitions
71
+
72
+ 1. **Use `this.props.*`** to access props (not `props.*`)
73
+ 2. **Use `$derived()`** for computed/derived values (must be inside a getter)
74
+ 3. **Use `$bindable(defaultValue)`** for two-way bindable props
75
+ 4. **Prefix with `_`** for internal/private state (convention - excluded from public types)
76
+ 5. **Never use `$state()`** for props - props are automatically reactive
77
+ 6. **Use `$state()`** for internal state (non-props) that needs reactivity
78
+
79
+ ---
80
+
81
+ ## Using Origins in Components
82
+
83
+ ### `$origin.component(Factory)` — Standard Component Usage
84
+
85
+ **This is the ONLY way to use origins inside `.svelte` components when you want props to flow from the parent.**
86
+
87
+ > ⚠️ **Component-only:** This macro throws an error if used in `.svelte.ts` module files.
88
+
89
+ ```svelte
90
+ <!-- Counter.svelte -->
91
+ <script lang="ts">
92
+ import { Counter } from './counter.svelte'
93
+
94
+ let counter = $origin.component(Counter)
95
+ </script>
96
+
97
+ <div>
98
+ <span>{counter.props.label}: {counter.props.count}</span>
99
+ <button onclick={counter.increment}>+{counter.props.step}</button>
100
+ <button onclick={counter.decrement}>-{counter.props.step}</button>
101
+ <small>Doubled: {counter.doubled}</small>
102
+ </div>
103
+ ```
104
+
105
+ **What happens at compile time:**
106
+
107
+ ```svelte
108
+ <script lang="ts">
109
+ import { Counter } from './counter.svelte'
110
+
111
+ type $$Props = $origin.Props<typeof Counter>
112
+ let { label = 'Counter', count = $bindable(0), step = 1 } = $props()
113
+ let __attrs = {
114
+ get label() { return label },
115
+ get count() { return count },
116
+ set count(v) { count = v },
117
+ get step() { return step }
118
+ }
119
+ let counter = Counter(__attrs)
120
+ </script>
121
+ ```
122
+
123
+ The component's props are **automatically extracted** from the origin's schema. The parent component can pass these props normally:
124
+
125
+ ```svelte
126
+ <Counter label="My Counter" count={5} step={2} />
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Programmatic Origin Creation
132
+
133
+ ### ⚠️ NEVER Call the Factory Directly
134
+
135
+ **WRONG** - This DOES NOT maintain reactivity:
136
+
137
+ ```ts
138
+ // ❌ BAD - Never do this!
139
+ const counter = Counter({
140
+ count: 0,
141
+ label: 'Test'
142
+ })
143
+ // counter.props.count = 5 ← This WON'T trigger reactivity!
144
+ ```
145
+
146
+ The input object `{ count: 0, label: 'Test' }` is a **plain object**, not a reactive proxy. Setting `counter.props.count` won't trigger Svelte's reactivity system.
147
+
148
+ ### ✅ Use `$origin.create()` Instead
149
+
150
+ **CORRECT** - Use `$origin.create()` for programmatic creation:
151
+
152
+ ```ts
153
+ // ✅ GOOD - Always use $origin.create()
154
+ const counter = $origin.create(Counter, {
155
+ count: 0,
156
+ label: 'Test'
157
+ })
158
+ // counter.props.count ← Readable, but NOT settable (read-only)
159
+ // counter.increment() ← Methods can still modify props internally!
160
+ ```
161
+
162
+ > **Note:** Props passed directly are **read-only** (getter-only). To set props externally, use `$origin.bind()`.
163
+
164
+ ### `$origin.create()` Works Everywhere
165
+
166
+ Unlike `$origin.component()` which only works in `.svelte` files, `$origin.create()` can be used in:
167
+
168
+ - ✅ `.svelte` component scripts
169
+ - ✅ `.svelte.ts` / `.svelte.js` module files
170
+ - ✅ Inside `$origin()` init callbacks
171
+ - ✅ Inside origin method bodies
172
+ - ✅ Module scripts (`<script module>`)
173
+
174
+ **What happens at compile time (regular props → getter-only):**
175
+
176
+ ```ts
177
+ let __attrs = {
178
+ // Regular props are getter-only (read-only from outside)
179
+ get count() { return 0 },
180
+ get label() { return 'Test' }
181
+ }
182
+ const counter = Counter(__attrs)
183
+ ```
184
+
185
+ **What happens with `$origin.bind()` (getter + setter):**
186
+
187
+ ```ts
188
+ let myCount = $state(0)
189
+ let __attrs = {
190
+ get count() { return myCount },
191
+ set count(v) { myCount = v } // Two-way binding!
192
+ }
193
+ const counter = Counter(__attrs)
194
+ ```
195
+
196
+ ### `$origin.bind(variable)` — Required for Settable Props
197
+
198
+ To set an origin's prop externally (or sync with existing state), you **must** use `$origin.bind()`:
199
+
200
+ ```ts
201
+ // In an origin's init or .svelte.ts file
202
+ let sharedItems = $state(['a', 'b', 'c'])
203
+
204
+ const list = $origin.create(ListOrigin, {
205
+ items: $origin.bind(sharedItems) // Two-way binding!
206
+ })
207
+
208
+ // Both are now in sync:
209
+ sharedItems.push('d') // list.props.items now includes 'd'
210
+ list.props.items = ['x'] // sharedItems is now ['x']
211
+ ```
212
+
213
+ Mix bound and unbound props:
214
+
215
+ ```ts
216
+ let count = $state(0)
217
+
218
+ const counter = $origin.create(Counter, {
219
+ count: $origin.bind(count), // Settable (two-way)
220
+ label: 'My Counter', // Read-only
221
+ step: 2 // Read-only
222
+ })
223
+
224
+ counter.props.count = 10 // ✅ Works - updates `count` too
225
+ counter.props.label = 'New' // ❌ No effect (read-only)
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Prop Forwarding
231
+
232
+ ### `$origin.for(Factory)` — Forward Props to Child
233
+
234
+ > ⚠️ **Component-only:** This macro throws an error if used in `.svelte.ts` module files.
235
+
236
+ Use in wrapper components that need to pass props through:
237
+
238
+ ```svelte
239
+ <!-- Wrapper.svelte -->
240
+ <script lang="ts">
241
+ import { Counter } from './counter.svelte'
242
+ import CounterComponent from './Counter.svelte'
243
+
244
+ let counterProps = $origin.for(Counter)
245
+ </script>
246
+
247
+ <div class="wrapper">
248
+ <CounterComponent {...counterProps} />
249
+ </div>
250
+ ```
251
+
252
+ ### `$origin.for('element')` — Forward Props to HTML Elements
253
+
254
+ ```svelte
255
+ <!-- CustomInput.svelte -->
256
+ <script lang="ts">
257
+ let inputProps = $origin.for('input')
258
+ </script>
259
+
260
+ <input {...inputProps} class="custom-input" />
261
+ ```
262
+
263
+ ---
264
+
265
+ ## Inheritance
266
+
267
+ Origins can extend other origins using array syntax:
268
+
269
+ ```ts
270
+ // base.svelte.ts
271
+ export const BaseCounter = $origin({
272
+ props: $origin.props({
273
+ count: $bindable(0)
274
+ }),
275
+
276
+ increment() {
277
+ this.props.count++
278
+ }
279
+ })
280
+
281
+ // extended.svelte.ts
282
+ export const ExtendedCounter = $origin([BaseCounter], {
283
+ props: $origin.props({
284
+ step: 1 // Additional prop (merged with parent)
285
+ }),
286
+
287
+ // Override with super call
288
+ increment() {
289
+ this.super.increment() // Call parent
290
+ console.log('Incremented!')
291
+ },
292
+
293
+ // New method
294
+ incrementByStep() {
295
+ this.props.count += this.props.step
296
+ }
297
+ })
298
+ ```
299
+
300
+ ### Inheritance Rules
301
+
302
+ 1. Child props are **merged** with parent props (left-to-right, child overrides)
303
+ 2. Use `this.super.*` to call parent methods
304
+ 3. Child methods override parent methods with same name
305
+ 4. Derived values can be overridden
306
+ 5. Multi-level inheritance works: `this.super.super.*` for grandparent
307
+
308
+ ### Standalone Props Inheritance
309
+
310
+ Props can also inherit from other props schemas:
311
+
312
+ ```ts
313
+ export const BaseAttrs = $origin.props({
314
+ id: '',
315
+ class: ''
316
+ })
317
+
318
+ export const WidgetAttrs = $origin.props([BaseAttrs], {
319
+ label: 'Widget',
320
+ active: false
321
+ })
322
+ // WidgetAttrs has: id, class, label, active
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Type Helpers
328
+
329
+ ### `$origin.Props<typeof Factory>`
330
+
331
+ Extract the component props type from an origin factory. Useful for typing in non-component contexts:
332
+
333
+ ```ts
334
+ import { Counter } from './counter.svelte'
335
+
336
+ // Use in utility functions or type definitions
337
+ function processProps(props: $origin.Props<typeof Counter>) {
338
+ console.log(props.count)
339
+ }
340
+ ```
341
+
342
+ > ⚠️ **Anti-pattern:** Do NOT manually declare `type $$Props = ...` in `.svelte` components.
343
+ > `svelte-origin` automatically generates the correct `$$Props` type from the origin schema.
344
+ > Manually declaring it can cause type mismatches and breaks the automatic prop extraction.
345
+
346
+ ### `$origin.Of<typeof Factory>`
347
+
348
+ Extract the instance type from an origin factory:
349
+
350
+ ```ts
351
+ import { Counter } from './counter.svelte'
352
+
353
+ // Type a variable that will hold an origin instance
354
+ let counter: $origin.Of<typeof Counter>
355
+
356
+ // Useful for optional or lazy initialization
357
+ let maybeCounter: $origin.Of<typeof Counter> | undefined = undefined
358
+
359
+ // Also works with generic factories
360
+ const List = <T>() => $origin({ props: $origin.props({ items: [] as T[] }) })
361
+ type StringListInstance = $origin.Of<ReturnType<typeof List<string>>>
362
+ ```
363
+
364
+ ---
365
+
366
+ ## Reactivity Internals
367
+
368
+ ### How Props Become Reactive
369
+
370
+ Origins use **getter/setter pairs** to maintain reactivity. The key distinction:
371
+
372
+ | Source | Props become | Settable externally? |
373
+ |--------|--------------|---------------------|
374
+ | `$origin.component()` | Connected to `$props()` | ✅ Bindable props only |
375
+ | `$origin.create({ prop: value })` | Getter-only | ❌ No |
376
+ | `$origin.create({ prop: $origin.bind(var) })` | Getter + setter | ✅ Yes |
377
+
378
+ **Getter-only props (default for `$origin.create`):**
379
+
380
+ ```ts
381
+ // Input
382
+ $origin.create(Counter, { count: 0 })
383
+
384
+ // Output - getter-only
385
+ let __attrs = { get count() { return 0 } }
386
+ ```
387
+
388
+ **Getter + setter props (with `$origin.bind`):**
389
+
390
+ ```ts
391
+ // Input
392
+ let count = $state(0)
393
+ $origin.create(Counter, { count: $origin.bind(count) })
394
+
395
+ // Output - getter AND setter
396
+ let __attrs = {
397
+ get count() { return count },
398
+ set count(v) { count = v }
399
+ }
400
+ ```
401
+
402
+ ### Why Internal Methods Still Work
403
+
404
+ Even though external setting doesn't work for regular props, **origin methods** can still modify props internally because they work through `this.props.*` which bypasses the external getter-only constraint:
405
+
406
+ ```ts
407
+ // Origin method can modify props
408
+ increment() {
409
+ this.props.count++ // ✅ Works - internal modification
410
+ }
411
+ ```
412
+
413
+ ---
414
+
415
+ ## Common Patterns
416
+
417
+ ### State Machine Origin
418
+
419
+ ```ts
420
+ export const StateMachine = $origin({
421
+ props: $origin.props({
422
+ initial: 'idle' as 'idle' | 'loading' | 'success' | 'error'
423
+ }),
424
+
425
+ _currentState: null as string | null,
426
+
427
+ get state() {
428
+ return $derived(this._currentState ?? this.props.initial)
429
+ },
430
+
431
+ transition(to: string) {
432
+ this._currentState = to
433
+ },
434
+
435
+ get isLoading() {
436
+ return $derived(this.state === 'loading')
437
+ }
438
+ })
439
+ ```
440
+
441
+ ### Form Field Origin
442
+
443
+ ```ts
444
+ export const FormField = $origin({
445
+ props: $origin.props({
446
+ value: $bindable(''),
447
+ label: '',
448
+ required: false,
449
+ validate: ((v: string) => true) as (v: string) => boolean | string
450
+ }),
451
+
452
+ _touched: false,
453
+
454
+ get error() {
455
+ if (!this._touched) return null
456
+ const result = this.props.validate(this.props.value)
457
+ return $derived(result === true ? null : result)
458
+ },
459
+
460
+ get isValid() {
461
+ return $derived(this.error === null)
462
+ },
463
+
464
+ touch() {
465
+ this._touched = true
466
+ },
467
+
468
+ reset() {
469
+ this.props.value = ''
470
+ this._touched = false
471
+ }
472
+ })
473
+ ```
474
+
475
+ ### Composing Origins
476
+
477
+ ```ts
478
+ // Create child origin inside parent using init callback
479
+ export const Parent = $origin({
480
+ props: $origin.props({
481
+ items: [] as string[]
482
+ }),
483
+
484
+ // Child origin with bound props
485
+ _child: null as ReturnType<typeof ChildOrigin> | null
486
+ }, function() {
487
+ // Init callback - runs when instance is created
488
+ this._child = $origin.create(ChildOrigin, {
489
+ items: $origin.bind(this.props.items) // Sync with parent
490
+ })
491
+ })
492
+ ```
493
+
494
+ ---
495
+
496
+ ## Quick Reference
497
+
498
+ ### File Types & Allowed Macros
499
+
500
+ | Extension | Purpose | Macros Available |
501
+ |-----------|---------|------------------|
502
+ | `.svelte.ts` | Origin definitions | `$origin()`, `$origin.props()`, `$origin.create()`, `$origin.bind()`, `$bindable()` |
503
+ | `.svelte` | Svelte components | `$origin.component()`, `$origin.for()`, `$origin.create()`, `$origin.bind()` |
504
+
505
+ ### Context-Specific Macros
506
+
507
+ | Macro | `.svelte` | `.svelte.ts` | Init callbacks |
508
+ |-------|:---------:|:------------:|:--------------:|
509
+ | `$origin.component()` | ✅ | throws | throws |
510
+ | `$origin.for()` | ✅ | throws | throws |
511
+ | `$origin.create()` | ✅ | ✅ | ✅ |
512
+ | `$origin.bind()` | ✅ | ✅ | ✅ |
513
+ | `$origin.props()` | ✅ | ✅ | ✅ |
514
+
515
+ ### Do's and Don'ts
516
+
517
+ | DO | DON'T |
518
+ |-------|----------|
519
+ | Use `$origin.component(Factory)` in `.svelte` components | Use `$origin.component()` in `.svelte.ts` files |
520
+ | Use `$origin.create(Factory, props)` for programmatic creation | Call `Factory({...})` directly (breaks reactivity) |
521
+ | Use `$origin.bind(variable)` for two-way shared state | Pass reactive variables without binding |
522
+ | Use `$derived()` for computed values in getters | Use `$state()` for props (they're already reactive) |
523
+ | Access props via `this.props.*` | Destructure props at top level of definition |
524
+ | Prefix private state with `_` | Expose internal state publicly |
525
+ | Let svelte-origin auto-generate `$$Props` | Manually declare `type $$Props = ...` |
526
+ | Use `$origin.Of<typeof F>` for instance types | Use `ReturnType<typeof F>` (less inference) |
527
+ | Use `$origin.Props<typeof F>` for type utilities | Use `$$Props` directly in component code |
528
+
529
+ ### Common LLM Pitfalls
530
+
531
+ 1. **Calling factory directly** - `Counter({...})` loses reactivity. Always use `$origin.create()` or `$origin.component()`.
532
+
533
+ 2. **Using `$origin.component()` in module files** - This only works in `.svelte` files. In `.svelte.ts`, use `$origin.create()`.
534
+
535
+ 3. **Using `$origin.for()` in module files** - This only works in `.svelte` files.
536
+
537
+ 4. **Forgetting `this.props`** - Inside origins, always use `this.props.count`, not just `count` or `props.count`.
538
+
539
+ 5. **Using `$state()` for props** - Props passed to `$origin.props({...})` are automatically reactive. Don't wrap defaults in `$state()`.
540
+
541
+ 6. **Missing `$derived()` in getters** - Computed values must use `$derived()` inside getters for reactivity.
542
+
543
+ 7. **Expecting settable props without `$origin.bind()`** - Props in `$origin.create({ prop: value })` are **read-only**. To set props externally, use `$origin.bind(variable)`.
544
+
545
+ 8. **Manually declaring `$$Props`** - Never write `type $$Props = ...` in components using `$origin.component()`. The transformer auto-generates this from the origin schema. Manual declarations cause type conflicts and may break prop extraction. Ensure `svelte.config.js` includes the svelte-origin preprocessor for proper type generation.