rip-lang 3.17.2 → 3.17.4

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 CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="https://github.com/shreeve/rip-lang/commits/main"><img src="https://img.shields.io/badge/version-3.17.2-blue.svg" alt="Version"></a>
12
+ <a href="https://github.com/shreeve/rip-lang/commits/main"><img src="https://img.shields.io/badge/version-3.17.4-blue.svg" alt="Version"></a>
13
13
  <a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
14
14
  <a href="#"><img src="https://img.shields.io/badge/tests-1%2C436%2F1%2C436-brightgreen.svg" alt="Tests"></a>
15
15
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
package/docs/RIP-APP.md CHANGED
@@ -246,7 +246,7 @@ export stash =
246
246
  products: source # five minutes of freshness is plenty
247
247
  fetch: -> Product.array.parse(api.get!('products').json!()) # validated Product[]
248
248
  staleTime: '5 min'
249
- order: source { fetch: (id:: string) -> Order.parse(api.get!("orders/#{id}").json!()) }
249
+ order: source { fetch: (id: string) -> Order.parse(api.get!("orders/#{id}").json!()) }
250
250
  ```
251
251
 
252
252
  A source is **lazy** (loads on first touch, not at startup) and **not a
package/docs/RIP-LANG.md CHANGED
@@ -673,8 +673,8 @@ String::shout = -> @toUpperCase() + "!"
673
673
  "hello".shout() # "HELLO!"
674
674
 
675
675
  # Type annotations (space after ::) — unaffected
676
- name:: string = "Alice"
677
- def greet(name:: string):: string
676
+ name: string = "Alice"
677
+ def greet(name: string): string
678
678
  "Hello, #{name}!"
679
679
  ```
680
680
 
@@ -1096,8 +1096,8 @@ Reads return the delayed value; writes update the source immediately. A drop-in
1096
1096
  Reactive operators work with Rip's optional type system:
1097
1097
 
1098
1098
  ```coffee
1099
- count:: number := 0 # Typed state
1100
- doubled:: number ~= count * 2 # Typed computed
1099
+ count: number := 0 # Typed state
1100
+ doubled: number ~= count * 2 # Typed computed
1101
1101
  ```
1102
1102
 
1103
1103
  Type annotations are erased from `.js` output. In `.d.ts` output, reactive state emits `Signal<T>` and computed values emit `Computed<T>`:
@@ -1989,8 +1989,8 @@ Rip supports an optional, compile-time-only type system. Types are erased from
1989
1989
 
1990
1990
  ```coffee
1991
1991
  # Type annotations (::)
1992
- count:: number = 0
1993
- def greet(name:: string):: string
1992
+ count: number = 0
1993
+ def greet(name: string): string
1994
1994
  "Hello, #{name}!"
1995
1995
 
1996
1996
  # Type aliases (type keyword)
@@ -761,7 +761,7 @@ signature (typed params, `this`, and the inferred return) instead of
761
761
  ```coffee
762
762
  greet: -> "Hello, #{@name}!"
763
763
 
764
- add: (other:: Money) ->
764
+ add: (other: Money) ->
765
765
  Money.parse amount: @amount + other.amount, currency: @currency
766
766
  # shadow TS: add(this: Money, other: Money): Money
767
767
 
package/docs/RIP-TYPES.md CHANGED
@@ -23,7 +23,7 @@ def greet(name)
23
23
  "Hello, #{name}!"
24
24
 
25
25
  # With types (also valid, same runtime behavior)
26
- def greet(name:: string):: string
26
+ def greet(name: string): string
27
27
  "Hello, #{name}!"
28
28
  ```
29
29
 
@@ -57,9 +57,9 @@ Both compile to identical JavaScript.
57
57
 
58
58
  | Sigil | Meaning | Example |
59
59
  |-------|---------|---------|
60
- | `::` | Type annotation | `count:: number = 0` |
60
+ | `:` | Type annotation | `count: number = 0` |
61
61
  | `type` | Type alias | `type ID = number` |
62
- | `?::` | Optional parameter / field / prop | `email?:: string` |
62
+ | `?:` | Optional parameter / field / prop | `email?: string` |
63
63
  | `\|` | Union member | `"a" \| "b" \| "c"` |
64
64
  | `=>` | Function type arrow | `(a: number) => string` |
65
65
  | `<T>` | Generic parameter | `Container<T>` |
@@ -68,17 +68,17 @@ This is the complete Rip Types sigil vocabulary.
68
68
 
69
69
  ---
70
70
 
71
- ## Type Annotations (`::`)
71
+ ## Type Annotations (`:`)
72
72
 
73
- The double-colon `::` annotates types on variables, parameters, return values,
74
- and properties.
73
+ A single colon `:` annotates types on variables, parameters, return values,
74
+ and properties. (`::` is prototype access only, e.g. `String::trim`.)
75
75
 
76
76
  ### Variables
77
77
 
78
78
  ```coffee
79
- count:: number = 0
80
- name:: string = "Rip"
81
- items:: string[] = []
79
+ count: number = 0
80
+ name: string = "Rip"
81
+ items: string[] = []
82
82
  ```
83
83
 
84
84
  **Emits:**
@@ -92,8 +92,8 @@ let items: string[]; items = [];
92
92
  ### Constants
93
93
 
94
94
  ```coffee
95
- MAX_RETRIES:: number =! 3
96
- API_URL:: string =! "https://api.example.com"
95
+ MAX_RETRIES: number =! 3
96
+ API_URL: string =! "https://api.example.com"
97
97
  ```
98
98
 
99
99
  **Emits:**
@@ -106,10 +106,10 @@ const API_URL: string = "https://api.example.com";
106
106
  ### Function Parameters
107
107
 
108
108
  ```coffee
109
- def greet(name:: string)
109
+ def greet(name: string)
110
110
  "Hello, #{name}!"
111
111
 
112
- def add(a:: number, b:: number)
112
+ def add(a: number, b: number)
113
113
  a + b
114
114
  ```
115
115
 
@@ -123,10 +123,10 @@ function add(a: number, b: number) { ... }
123
123
  ### Return Types
124
124
 
125
125
  ```coffee
126
- def getUser(id:: number):: User
126
+ def getUser(id: number): User
127
127
  db.find!(id)
128
128
 
129
- def fetchData():: Promise<Data>
129
+ def fetchData(): Promise<Data>
130
130
  fetch!("/api/data")
131
131
  ```
132
132
 
@@ -140,7 +140,7 @@ async function fetchData(): Promise<Data> { ... }
140
140
  ### Combined
141
141
 
142
142
  ```coffee
143
- def processUser(id:: number, options:: Options):: Result
143
+ def processUser(id: number, options: Options): Result
144
144
  user = getUser!(id)
145
145
  transform(user, options)
146
146
  ```
@@ -156,8 +156,8 @@ function processUser(id: number, options: Options): Result { ... }
156
156
  Types work with Rip's reactive operators:
157
157
 
158
158
  ```coffee
159
- count:: number := 0
160
- doubled:: number ~= count * 2
159
+ count: number := 0
160
+ doubled: number ~= count * 2
161
161
  ```
162
162
 
163
163
  **Emits:**
@@ -181,8 +181,8 @@ type Name = string
181
181
 
182
182
  # Complex types
183
183
  type UserID = number | string
184
- type Callback = (error:: Error?, data:: any) => void
185
- type Handler = (req:: Request, res:: Response) => Promise<void>
184
+ type Callback = (error: Error?, data: any) => void
185
+ type Handler = (req: Request, res: Response) => Promise<void>
186
186
  ```
187
187
 
188
188
  **Emits:**
@@ -287,16 +287,16 @@ Rip uses a single optionality marker: `?` placed on the **name**
287
287
  type-suffix operators — write `T | undefined`, `T | null | undefined`,
288
288
  or `NonNullable<T>` directly when you need them.
289
289
 
290
- ### Optional Parameter / Field: `name?:: T`
290
+ ### Optional Parameter / Field: `name?: T`
291
291
 
292
292
  ```coffee
293
- def greet(name?:: string):: void
293
+ def greet(name?: string): void
294
294
  console.log "hello #{name ?? 'world'}"
295
295
 
296
296
  type User =
297
- id:: number
298
- name:: string
299
- email?:: string # Optional field — may be absent
297
+ id: number
298
+ name: string
299
+ email?: string # Optional field — may be absent
300
300
  ```
301
301
 
302
302
  **Emits:**
@@ -311,7 +311,7 @@ type User = {
311
311
  };
312
312
  ```
313
313
 
314
- ### Component Props: `@prop?:: T [:= default]`
314
+ ### Component Props: `@prop?: T [:= default]`
315
315
 
316
316
  A `?` on a component prop name marks it optional in the generated
317
317
  constructor type. A `:=` default is purely a runtime initializer — it
@@ -319,9 +319,9 @@ does NOT make the prop optional on its own. The three canonical shapes:
319
319
 
320
320
  ```coffee
321
321
  export Counter = component
322
- @value:: number # required, no default
323
- @step?:: number # optional, no default
324
- @label?:: string := "Count" # optional with default
322
+ @value: number # required, no default
323
+ @step?: number # optional, no default
324
+ @label?: string := "Count" # optional with default
325
325
  ```
326
326
 
327
327
  **Emits:**
@@ -336,7 +336,7 @@ export declare class Counter {
336
336
  }
337
337
  ```
338
338
 
339
- Writing `@prop:: T := V` (typed, defaulted, no `?`) declares a
339
+ Writing `@prop: T := V` (typed, defaulted, no `?`) declares a
340
340
  **required prop with a default** — the parent must still pass it. To
341
341
  make a prop optional, add `?` to the name.
342
342
 
@@ -346,9 +346,9 @@ When the **value** itself needs to permit `undefined` or `null`, write
346
346
  the union explicitly:
347
347
 
348
348
  ```coffee
349
- email:: string | undefined
350
- middle:: string | null | undefined
351
- id:: NonNullable<ID>
349
+ email: string | undefined
350
+ middle: string | null | undefined
351
+ id: NonNullable<ID>
352
352
  ```
353
353
 
354
354
  This is the same form TypeScript uses, so the emitted DTS is a
@@ -406,23 +406,23 @@ numeric values.
406
406
 
407
407
  ```coffee
408
408
  # Type aliases for function signatures
409
- type Comparator = (a:: any, b:: any) => number
410
- type AsyncFetcher = (url:: string) => Promise<Response>
409
+ type Comparator = (a: any, b: any) => number
410
+ type AsyncFetcher = (url: string) => Promise<Response>
411
411
 
412
412
  # Overloads
413
- def toHtml(content:: string):: string
414
- def toHtml(nodes:: Element[]):: string
415
- def toHtml(input:: any):: string
413
+ def toHtml(content: string): string
414
+ def toHtml(nodes: Element[]): string
415
+ def toHtml(input: any): string
416
416
  switch typeof input
417
417
  when "string" then escapeHtml(input)
418
418
  else renderNodes(input)
419
419
 
420
420
  # Method signatures in classes
421
421
  class UserService
422
- find: (id:: number):: User? ->
422
+ find: (id: number): User? ->
423
423
  @db.find(id)
424
424
 
425
- create: (data:: CreateUserInput):: User ->
425
+ create: (data: CreateUserInput): User ->
426
426
  @db.create(data)
427
427
  ```
428
428
 
@@ -461,16 +461,16 @@ type Pair<K, V> =
461
461
  # With constraints
462
462
  type Comparable<T extends Ordered> =
463
463
  value: T
464
- compareTo: (other:: T) => number
464
+ compareTo: (other: T) => number
465
465
 
466
466
  # Generic functions
467
- def identity<T>(value:: T):: T
467
+ def identity<T>(value: T): T
468
468
  value
469
469
 
470
- def map<T, U>(items:: T[], fn:: (item:: T) => U):: U[]
470
+ def map<T, U>(items: T[], fn: (item: T) => U): U[]
471
471
  items.map(fn)
472
472
 
473
- def merge<T extends object, U extends object>(a:: T, b:: U):: T & U
473
+ def merge<T extends object, U extends object>(a: T, b: U): T & U
474
474
  {...a, ...b}
475
475
  ```
476
476
 
@@ -586,14 +586,14 @@ Types should be explicit at boundaries, optional elsewhere:
586
586
 
587
587
  ```coffee
588
588
  # Explicit — function signatures, exports, class properties
589
- def processUser(id:: number, options:: Options):: Result
589
+ def processUser(id: number, options: Options): Result
590
590
  ...
591
591
 
592
- export config:: Config = loadConfig()
592
+ export config: Config = loadConfig()
593
593
 
594
594
  class UserService
595
- db:: Database
596
- cache:: Map<string, User>
595
+ db: Database
596
+ cache: Map<string, User>
597
597
 
598
598
  # Inferred — local variables
599
599
  user = getUser!(id) # Inferred from return type
@@ -668,7 +668,7 @@ export type User =
668
668
  id: number
669
669
  name: string
670
670
 
671
- export def getUser(id:: number):: User?
671
+ export def getUser(id: number): User?
672
672
  db.find(id)
673
673
  ```
674
674
 
@@ -705,7 +705,7 @@ User = schema :input
705
705
  email! email
706
706
 
707
707
  # Validate at an API boundary
708
- def createUser(req:: Request):: User
708
+ def createUser(req: Request): User
709
709
  User.parse req.json! # throws SchemaError on invalid
710
710
 
711
711
  # Or non-throwing for structured error responses
@@ -907,7 +907,7 @@ rewriteTypes() {
907
907
  this.scanTokens((token, i, tokens) => {
908
908
  let tag = token[0];
909
909
 
910
- // --- Handle :: (type annotations) ---
910
+ // --- Handle : (type annotations) ---
911
911
  if (tag === 'TYPE_ANNOTATION') {
912
912
  let prevToken = tokens[i - 1];
913
913
  if (!prevToken) return 1;
@@ -998,7 +998,7 @@ rewriteTypes() {
998
998
  if (k >= 0) target = tokens[k];
999
999
  propName = 'returnType';
1000
1000
  } else if (prevToken[0] === 'PARAM_END') {
1001
- // Return type on arrow function: (x:: number):: string -> ...
1001
+ // Return type on arrow function: (x: number): string -> ...
1002
1002
  // Scan forward past the type tokens to find the -> token.
1003
1003
  let arrowIdx = i + 1 + typeTokens.length;
1004
1004
  let arrowToken = tokens[arrowIdx];
@@ -1008,15 +1008,15 @@ rewriteTypes() {
1008
1008
  propName = 'returnType';
1009
1009
  } else if (prevToken[0] === 'IDENTIFIER' && i >= 2 &&
1010
1010
  tokens[i - 2]?.[0] === 'DEF') {
1011
- // Return type on parameterless function: def foo:: string
1011
+ // Return type on parameterless function: def foo: string
1012
1012
  propName = 'returnType';
1013
1013
  }
1014
1014
 
1015
1015
  if (!target.data) target.data = {};
1016
1016
  target.data[propName] = typeStr;
1017
1017
 
1018
- // Remove :: and type tokens from stream
1019
- let removeCount = 1 + typeTokens.length; // :: + type tokens
1018
+ // Remove : and type tokens from stream
1019
+ let removeCount = 1 + typeTokens.length; // : + type tokens
1020
1020
  tokens.splice(i, removeCount);
1021
1021
  return 0; // Re-examine current position
1022
1022
  }
@@ -1062,14 +1062,14 @@ always ends a type expression.
1062
1062
  **Worked examples:**
1063
1063
 
1064
1064
  ```
1065
- INPUT: count:: number = 0
1066
- SCAN: :: → start collecting
1065
+ INPUT: count: number = 0
1066
+ SCAN: : → start collecting
1067
1067
  number → depth=0, type token
1068
1068
  = → depth=0, assignment delimiter, STOP
1069
1069
  RESULT: type = "number"
1070
1070
 
1071
- INPUT: items:: Map<string, number> = x
1072
- SCAN: :: → start collecting
1071
+ INPUT: items: Map<string, number> = x
1072
+ SCAN: : → start collecting
1073
1073
  Map → depth=0, type token
1074
1074
  < → depth=1, type token
1075
1075
  string → depth=1, type token
@@ -1079,18 +1079,18 @@ SCAN: :: → start collecting
1079
1079
  = → depth=0, assignment delimiter, STOP
1080
1080
  RESULT: type = "Map<string, number>"
1081
1081
 
1082
- INPUT: def f(a:: number, b:: string)
1083
- SCAN: :: (after a) → start collecting
1082
+ INPUT: def f(a: number, b: string)
1083
+ SCAN: : (after a) → start collecting
1084
1084
  number → depth=0, type token
1085
1085
  , → depth=0, parameter delimiter, STOP
1086
1086
  RESULT: type on a = "number"
1087
- SCAN: :: (after b) → start collecting
1087
+ SCAN: : (after b) → start collecting
1088
1088
  string → depth=0, type token
1089
1089
  ) → depth=0, close paren, STOP
1090
1090
  RESULT: type on b = "string"
1091
1091
 
1092
- INPUT: fn:: (a: number, b: string) => void = ...
1093
- SCAN: :: → start collecting
1092
+ INPUT: fn: (a: number, b: string) => void = ...
1093
+ SCAN: : → start collecting
1094
1094
  ( → depth=1, type token
1095
1095
  a → depth=1, type token
1096
1096
  : → depth=1, type token
@@ -1105,8 +1105,8 @@ SCAN: :: → start collecting
1105
1105
  = → depth=0, assignment delimiter, STOP
1106
1106
  RESULT: type = "(a: number, b: string) => void"
1107
1107
 
1108
- INPUT: (name:: string):: string -> "Hello!"
1109
- SCAN: :: (after PARAM_END) → start collecting return type
1108
+ INPUT: (name: string): string -> "Hello!"
1109
+ SCAN: : (after PARAM_END) → start collecting return type
1110
1110
  string → depth=0, type token
1111
1111
  -> → depth=0, code arrow delimiter, STOP
1112
1112
  RESULT: returnType = "string"
@@ -1137,7 +1137,7 @@ collection — the token itself is the answer:
1137
1137
  Return types appear after the parameter list close:
1138
1138
 
1139
1139
  ```coffee
1140
- def greet(name:: string):: string
1140
+ def greet(name: string): string
1141
1141
  ```
1142
1142
 
1143
1143
  After `rewriteTypes()` processes the `::` after `)`, the return type must be
@@ -1169,7 +1169,7 @@ Each example shows: Rip source → tokens after rewrite → s-expression → out
1169
1169
  **Typed variable:**
1170
1170
 
1171
1171
  ```coffee
1172
- count:: number = 0
1172
+ count: number = 0
1173
1173
  ```
1174
1174
 
1175
1175
  Tokens after `rewriteTypes()`:
@@ -1195,7 +1195,7 @@ let count: number;
1195
1195
  **Typed constant:**
1196
1196
 
1197
1197
  ```coffee
1198
- MAX:: number =! 100
1198
+ MAX: number =! 100
1199
1199
  ```
1200
1200
 
1201
1201
  Tokens: `IDENTIFIER("MAX", {type: "number"}), READONLY_ASSIGN, NUMBER(100)`
@@ -1209,8 +1209,8 @@ S-expression: `["readonly", MAX, 100]` — MAX carries `.data.type = "number"`
1209
1209
  **Typed reactive state:**
1210
1210
 
1211
1211
  ```coffee
1212
- count:: number := 0
1213
- doubled:: number ~= count * 2
1212
+ count: number := 0
1213
+ doubled: number ~= count * 2
1214
1214
  ```
1215
1215
 
1216
1216
  S-expressions:
@@ -1228,7 +1228,7 @@ declare const doubled: Computed<number>;
1228
1228
  **Typed function:**
1229
1229
 
1230
1230
  ```coffee
1231
- def getUser(id:: number):: User
1231
+ def getUser(id: number): User
1232
1232
  db.find!(id)
1233
1233
  ```
1234
1234
 
@@ -1250,7 +1250,7 @@ S-expression: `["def", getUser, [id], body]`
1250
1250
  **Typed arrow function:**
1251
1251
 
1252
1252
  ```coffee
1253
- greet = (name:: string):: string -> "Hello, #{name}!"
1253
+ greet = (name: string): string -> "Hello, #{name}!"
1254
1254
  ```
1255
1255
 
1256
1256
  Tokens after rewrite:
@@ -1264,8 +1264,8 @@ PARAM_END, ->({returnType: "string"}), ...
1264
1264
 
1265
1265
  ```coffee
1266
1266
  class UserService
1267
- db:: Database
1268
- cache:: Map<string, User>
1267
+ db: Database
1268
+ cache: Map<string, User>
1269
1269
  ```
1270
1270
 
1271
1271
  Tokens: identifiers carry `.data.type`, grammar sees normal class body.
@@ -1592,10 +1592,10 @@ the rewriter and removed before parsing.
1592
1592
  #### 2.6 Generic Type Parameters
1593
1593
 
1594
1594
  ```coffee
1595
- def identity<T>(value:: T):: T
1595
+ def identity<T>(value: T): T
1596
1596
  value
1597
1597
 
1598
- def map<T, U>(items:: T[], fn:: (item:: T) => U):: U[]
1598
+ def map<T, U>(items: T[], fn: (item: T) => U): U[]
1599
1599
  items.map(fn)
1600
1600
  ```
1601
1601
 
@@ -1752,12 +1752,12 @@ A `?` placed unspaced **before** the `::` annotation on a parameter,
1752
1752
  field, or component prop name marks the name as optional:
1753
1753
 
1754
1754
  ```coffee
1755
- def fetch(url:: string, opts?:: RequestInit):: Response
1755
+ def fetch(url: string, opts?: RequestInit): Response
1756
1756
  ...
1757
1757
 
1758
1758
  type User =
1759
- id:: number
1760
- email?:: string # optional field
1759
+ id: number
1760
+ email?: string # optional field
1761
1761
  ```
1762
1762
 
1763
1763
  In the lexer, when `?` appears immediately before `::`, it is stripped
@@ -1781,7 +1781,7 @@ export type User =
1781
1781
  id: number
1782
1782
  name: string
1783
1783
 
1784
- export def getUser(id:: number):: User?
1784
+ export def getUser(id: number): User?
1785
1785
  db.find(id)
1786
1786
  ```
1787
1787
 
@@ -1812,23 +1812,23 @@ Each row is a test case. Verify both .js and .d.ts output.
1812
1812
 
1813
1813
  | # | Rip Input | .js Output | .d.ts Output |
1814
1814
  |---|-----------|-----------|-------------|
1815
- | 1 | `count:: number = 0` | `count = 0` | `let count: number;` |
1816
- | 2 | `MAX:: number =! 100` | `const MAX = 100` | `declare const MAX: number;` |
1817
- | 3 | `count:: number := 0` | `const count = __state(0)` | `declare const count: Signal<number>;` |
1818
- | 4 | `doubled:: number ~= x * 2` | `const doubled = __computed(...)` | `declare const doubled: Computed<number>;` |
1819
- | 5 | `def f(a:: number):: string` | `function f(a) { ... }` | `declare function f(a: number): string;` |
1820
- | 6 | `(x:: number):: number -> x + 1` | `(x) => x + 1` | `(x: number) => number` |
1815
+ | 1 | `count: number = 0` | `count = 0` | `let count: number;` |
1816
+ | 2 | `MAX: number =! 100` | `const MAX = 100` | `declare const MAX: number;` |
1817
+ | 3 | `count: number := 0` | `const count = __state(0)` | `declare const count: Signal<number>;` |
1818
+ | 4 | `doubled: number ~= x * 2` | `const doubled = __computed(...)` | `declare const doubled: Computed<number>;` |
1819
+ | 5 | `def f(a: number): string` | `function f(a) { ... }` | `declare function f(a: number): string;` |
1820
+ | 6 | `(x: number): number -> x + 1` | `(x) => x + 1` | `(x: number) => number` |
1821
1821
  | 7 | `type ID = number` | *(empty)* | `type ID = number;` |
1822
1822
  | 8 | `type User =` (+ block) | *(empty)* | `type User = { id: number; ... };` |
1823
1823
  | 9 | `type Status = \| "a" \| "b"` | *(empty)* | `type Status = "a" \| "b";` |
1824
1824
  | 10 | `interface Foo` (+ block) | *(empty)* | `interface Foo { ... }` |
1825
1825
  | 11 | `enum Code` (+ block) | `const Code = { ... }` | `enum Code { ... }` |
1826
- | 12 | `items:: Map<string, number> = x` | `items = x` | `let items: Map<string, number>;` |
1827
- | 13 | `email:: string?` | — | `let email: string \| undefined;` |
1828
- | 14 | `id:: ID!` | — | `let id: NonNullable<ID>;` |
1829
- | 15 | `def identity<T>(v:: T):: T` | `function identity(v) { ... }` | `declare function identity<T>(v: T): T;` |
1826
+ | 12 | `items: Map<string, number> = x` | `items = x` | `let items: Map<string, number>;` |
1827
+ | 13 | `email: string?` | — | `let email: string \| undefined;` |
1828
+ | 14 | `id: ID!` | — | `let id: NonNullable<ID>;` |
1829
+ | 15 | `def identity<T>(v: T): T` | `function identity(v) { ... }` | `declare function identity<T>(v: T): T;` |
1830
1830
  | 16 | `export type User =` (+ block) | *(empty)* | `export type User = { ... };` |
1831
- | 17 | `export def f(x:: number):: number` | `export function f(x) { ... }` | `export function f(x: number): number;` |
1831
+ | 17 | `export def f(x: number): number` | `export function f(x) { ... }` | `export function f(x: number): number;` |
1832
1832
 
1833
1833
  ---
1834
1834
 
@@ -1859,21 +1859,21 @@ The type system uses a two-phase approach:
1859
1859
 
1860
1860
  | Rip Input | .d.ts Output |
1861
1861
  |-----------|-------------|
1862
- | `x:: number = 0` | `let x: number;` |
1863
- | `x:: number =! 100` | `declare const x: number;` |
1864
- | `x:: number := 0` | `declare const x: Signal<number>;` |
1865
- | `x:: number ~= y * 2` | `declare const x: Computed<number>;` |
1866
- | `x:: Function ~> ...` | `declare const x: () => void;` |
1862
+ | `x: number = 0` | `let x: number;` |
1863
+ | `x: number =! 100` | `declare const x: number;` |
1864
+ | `x: number := 0` | `declare const x: Signal<number>;` |
1865
+ | `x: number ~= y * 2` | `declare const x: Computed<number>;` |
1866
+ | `x: Function ~> ...` | `declare const x: () => void;` |
1867
1867
  | `export` variants of all above | `export const/let ...` |
1868
1868
 
1869
1869
  **Functions:**
1870
1870
 
1871
1871
  | Rip Input | .d.ts Output |
1872
1872
  |-----------|-------------|
1873
- | `def f(a:: T):: R` | `declare function f(a: T): R;` |
1874
- | `def f<T>(a:: T):: T` | `declare function f<T>(a: T): T;` |
1873
+ | `def f(a: T): R` | `declare function f(a: T): R;` |
1874
+ | `def f<T>(a: T): T` | `declare function f<T>(a: T): T;` |
1875
1875
  | `def f<T extends Base>(...)` | `declare function f<T extends Base>(...);` |
1876
- | `f = (a:: T) -> ...` | `declare function f(a: T);` |
1876
+ | `f = (a: T) -> ...` | `declare function f(a: T);` |
1877
1877
  | `export def ...` | `export function ...;` |
1878
1878
 
1879
1879
  **Types, Interfaces, and Enums:**
@@ -1892,9 +1892,9 @@ The type system uses a two-phase approach:
1892
1892
 
1893
1893
  | Rip Input | .d.ts Output |
1894
1894
  |-----------|-------------|
1895
- | `class X { prop:: T = val }` | `declare class X { prop: T; }` |
1896
- | `constructor: (@name:: T) ->` | `constructor(name: T);` |
1897
- | `method: (a:: T) ->` | `method(a: T);` |
1895
+ | `class X { prop: T = val }` | `declare class X { prop: T; }` |
1896
+ | `constructor: (@name: T) ->` | `constructor(name: T);` |
1897
+ | `method: (a: T) ->` | `method(a: T);` |
1898
1898
  | `class X extends Y` | `declare class X extends Y { ... }` |
1899
1899
 
1900
1900
  **Components:**
@@ -1902,7 +1902,7 @@ The type system uses a two-phase approach:
1902
1902
  | Rip Input | .d.ts Output |
1903
1903
  |-----------|-------------|
1904
1904
  | `Name = component { ... }` | `declare class Name { ... }` |
1905
- | Reactive state `count:: number := 0` | `count: number;` |
1905
+ | Reactive state `count: number := 0` | `count: number;` |
1906
1906
  | Computed `doubled ~= ...` | `readonly doubled: any;` |
1907
1907
  | Methods | Method declarations |
1908
1908
  | Auto-generated | `constructor`, `mount()`, `unmount()` |
package/docs/dist/rip.js CHANGED
@@ -1036,6 +1036,13 @@ class __SchemaDef {
1036
1036
  // a non-enumerable accessor so order.user_id and order.userId read
1037
1037
  // the same slot — useful when DB column names leak into user code
1038
1038
  // via raw SQL helpers.
1039
+ //
1040
+ // Temporal values arrive already decoded by the adapter: @rip-lang/db
1041
+ // decodes date/datetime columns to real \`Date\` at the wire boundary
1042
+ // (naive TIMESTAMP is read as UTC), so hydrate stores them verbatim —
1043
+ // do NOT add a decode here. That keeps a single decode seam and honors
1044
+ // the \`date\`/\`datetime\` -> \`Date\` .d.ts contract. (\`_coerceDates\` below
1045
+ // is parse-only, for JSON/HTTP input — it never runs on this path.)
1039
1046
  const data = {};
1040
1047
  for (let i = 0; i < columns.length; i++) {
1041
1048
  data[__schemaCamel(columns[i].name)] = row[i];
@@ -7303,8 +7310,7 @@ Expecting ${expected.join(", ")}, got '${this.tokenNames[symbol] || symbol}'`;
7303
7310
  this.emit(".", ".");
7304
7311
  return 2;
7305
7312
  } else if (val === "::") {
7306
- tag = "TYPE_ANNOTATION";
7307
- this.inTypeAnnotation = true;
7313
+ syntaxError("type annotations use a single ':' (e.g. `x: number`), not '::'", { row: this.row, col: this.col, len: 2 });
7308
7314
  } else if (val === "~=")
7309
7315
  tag = "COMPUTED_ASSIGN";
7310
7316
  else if (val === ":=")
@@ -16938,8 +16944,8 @@ if (typeof globalThis !== 'undefined') {
16938
16944
  return new CodeEmitter({}).getComponentRuntime();
16939
16945
  }
16940
16946
  // src/browser.js
16941
- var VERSION = "3.17.2";
16942
- var BUILD_DATE = "2026-06-28@03:29:47GMT";
16947
+ var VERSION = "3.17.4";
16948
+ var BUILD_DATE = "2026-06-28@05:09:42GMT";
16943
16949
  if (typeof globalThis !== "undefined") {
16944
16950
  if (!globalThis.__rip)
16945
16951
  new Function(getReactiveRuntime())();