@modularizer/plat 0.1.0

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.
Files changed (287) hide show
  1. package/README.md +574 -0
  2. package/bin/z2.js +5 -0
  3. package/dist/args/coerce.d.ts +54 -0
  4. package/dist/args/coerce.d.ts.map +1 -0
  5. package/dist/args/coerce.js +236 -0
  6. package/dist/args/coerce.js.map +1 -0
  7. package/dist/args/index.d.ts +2 -0
  8. package/dist/args/index.d.ts.map +1 -0
  9. package/dist/args/index.js +2 -0
  10. package/dist/args/index.js.map +1 -0
  11. package/dist/args/scalars.d.ts +87 -0
  12. package/dist/args/scalars.d.ts.map +1 -0
  13. package/dist/args/scalars.js +22 -0
  14. package/dist/args/scalars.js.map +1 -0
  15. package/dist/args/validate.d.ts +23 -0
  16. package/dist/args/validate.d.ts.map +1 -0
  17. package/dist/args/validate.js +185 -0
  18. package/dist/args/validate.js.map +1 -0
  19. package/dist/args/z2.d.ts +27 -0
  20. package/dist/args/z2.d.ts.map +1 -0
  21. package/dist/args/z2.js +24 -0
  22. package/dist/args/z2.js.map +1 -0
  23. package/dist/cli/commands/api-proxy.d.ts +9 -0
  24. package/dist/cli/commands/api-proxy.d.ts.map +1 -0
  25. package/dist/cli/commands/api-proxy.js +292 -0
  26. package/dist/cli/commands/api-proxy.js.map +1 -0
  27. package/dist/cli/commands/env.d.ts +32 -0
  28. package/dist/cli/commands/env.d.ts.map +1 -0
  29. package/dist/cli/commands/env.js +157 -0
  30. package/dist/cli/commands/env.js.map +1 -0
  31. package/dist/cli/commands/gen.d.ts +2 -0
  32. package/dist/cli/commands/gen.d.ts.map +1 -0
  33. package/dist/cli/commands/gen.js +88 -0
  34. package/dist/cli/commands/gen.js.map +1 -0
  35. package/dist/cli/commands/init-project.d.ts +9 -0
  36. package/dist/cli/commands/init-project.d.ts.map +1 -0
  37. package/dist/cli/commands/init-project.js +143 -0
  38. package/dist/cli/commands/init-project.js.map +1 -0
  39. package/dist/cli/commands/jwt-cmd.d.ts +10 -0
  40. package/dist/cli/commands/jwt-cmd.d.ts.map +1 -0
  41. package/dist/cli/commands/jwt-cmd.js +33 -0
  42. package/dist/cli/commands/jwt-cmd.js.map +1 -0
  43. package/dist/cli/commands/run.d.ts +5 -0
  44. package/dist/cli/commands/run.d.ts.map +1 -0
  45. package/dist/cli/commands/run.js +31 -0
  46. package/dist/cli/commands/run.js.map +1 -0
  47. package/dist/cli/commands/serve.d.ts +2 -0
  48. package/dist/cli/commands/serve.d.ts.map +1 -0
  49. package/dist/cli/commands/serve.js +43 -0
  50. package/dist/cli/commands/serve.js.map +1 -0
  51. package/dist/cli/commands/watch.d.ts +5 -0
  52. package/dist/cli/commands/watch.d.ts.map +1 -0
  53. package/dist/cli/commands/watch.js +119 -0
  54. package/dist/cli/commands/watch.js.map +1 -0
  55. package/dist/cli/index.d.ts +11 -0
  56. package/dist/cli/index.d.ts.map +1 -0
  57. package/dist/cli/index.js +11 -0
  58. package/dist/cli/index.js.map +1 -0
  59. package/dist/cli/jwt-helper.d.ts +32 -0
  60. package/dist/cli/jwt-helper.d.ts.map +1 -0
  61. package/dist/cli/jwt-helper.js +84 -0
  62. package/dist/cli/jwt-helper.js.map +1 -0
  63. package/dist/cli/proxy.d.ts +40 -0
  64. package/dist/cli/proxy.d.ts.map +1 -0
  65. package/dist/cli/proxy.js +171 -0
  66. package/dist/cli/proxy.js.map +1 -0
  67. package/dist/cli/runtime.d.ts +17 -0
  68. package/dist/cli/runtime.d.ts.map +1 -0
  69. package/dist/cli/runtime.js +286 -0
  70. package/dist/cli/runtime.js.map +1 -0
  71. package/dist/cli/spec-source.d.ts +13 -0
  72. package/dist/cli/spec-source.d.ts.map +1 -0
  73. package/dist/cli/spec-source.js +207 -0
  74. package/dist/cli/spec-source.js.map +1 -0
  75. package/dist/cli/z2.d.ts +7 -0
  76. package/dist/cli/z2.d.ts.map +1 -0
  77. package/dist/cli/z2.js +93 -0
  78. package/dist/cli/z2.js.map +1 -0
  79. package/dist/client/file-transport-plugin.d.ts +25 -0
  80. package/dist/client/file-transport-plugin.d.ts.map +1 -0
  81. package/dist/client/file-transport-plugin.js +80 -0
  82. package/dist/client/file-transport-plugin.js.map +1 -0
  83. package/dist/client/http-transport-plugin.d.ts +27 -0
  84. package/dist/client/http-transport-plugin.d.ts.map +1 -0
  85. package/dist/client/http-transport-plugin.js +48 -0
  86. package/dist/client/http-transport-plugin.js.map +1 -0
  87. package/dist/client/index.d.ts +6 -0
  88. package/dist/client/index.d.ts.map +1 -0
  89. package/dist/client/index.js +6 -0
  90. package/dist/client/index.js.map +1 -0
  91. package/dist/client/openapi-client.d.ts +332 -0
  92. package/dist/client/openapi-client.d.ts.map +1 -0
  93. package/dist/client/openapi-client.js +879 -0
  94. package/dist/client/openapi-client.js.map +1 -0
  95. package/dist/client/proxy.d.ts +5 -0
  96. package/dist/client/proxy.d.ts.map +1 -0
  97. package/dist/client/proxy.js +353 -0
  98. package/dist/client/proxy.js.map +1 -0
  99. package/dist/client/request-builder.d.ts +4 -0
  100. package/dist/client/request-builder.d.ts.map +1 -0
  101. package/dist/client/request-builder.js +88 -0
  102. package/dist/client/request-builder.js.map +1 -0
  103. package/dist/client/rpc-transport-plugin.d.ts +17 -0
  104. package/dist/client/rpc-transport-plugin.d.ts.map +1 -0
  105. package/dist/client/rpc-transport-plugin.js +69 -0
  106. package/dist/client/rpc-transport-plugin.js.map +1 -0
  107. package/dist/client/tools.d.ts +69 -0
  108. package/dist/client/tools.d.ts.map +1 -0
  109. package/dist/client/tools.js +122 -0
  110. package/dist/client/tools.js.map +1 -0
  111. package/dist/client/transport-plugin.d.ts +62 -0
  112. package/dist/client/transport-plugin.d.ts.map +1 -0
  113. package/dist/client/transport-plugin.js +40 -0
  114. package/dist/client/transport-plugin.js.map +1 -0
  115. package/dist/index.d.ts +9 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +8 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/logging.d.ts +9 -0
  120. package/dist/logging.d.ts.map +1 -0
  121. package/dist/logging.js +29 -0
  122. package/dist/logging.js.map +1 -0
  123. package/dist/rpc.d.ts +39 -0
  124. package/dist/rpc.d.ts.map +1 -0
  125. package/dist/rpc.js +2 -0
  126. package/dist/rpc.js.map +1 -0
  127. package/dist/server/auth/index.d.ts +4 -0
  128. package/dist/server/auth/index.d.ts.map +1 -0
  129. package/dist/server/auth/index.js +2 -0
  130. package/dist/server/auth/index.js.map +1 -0
  131. package/dist/server/auth/jwt.d.ts +27 -0
  132. package/dist/server/auth/jwt.d.ts.map +1 -0
  133. package/dist/server/auth/jwt.js +95 -0
  134. package/dist/server/auth/jwt.js.map +1 -0
  135. package/dist/server/cache/index.d.ts +2 -0
  136. package/dist/server/cache/index.d.ts.map +1 -0
  137. package/dist/server/cache/index.js +2 -0
  138. package/dist/server/cache/index.js.map +1 -0
  139. package/dist/server/cache/utils.d.ts +30 -0
  140. package/dist/server/cache/utils.d.ts.map +1 -0
  141. package/dist/server/cache/utils.js +116 -0
  142. package/dist/server/cache/utils.js.map +1 -0
  143. package/dist/server/call-sessions.d.ts +50 -0
  144. package/dist/server/call-sessions.d.ts.map +1 -0
  145. package/dist/server/call-sessions.js +90 -0
  146. package/dist/server/call-sessions.js.map +1 -0
  147. package/dist/server/config/bucket.d.ts +9 -0
  148. package/dist/server/config/bucket.d.ts.map +1 -0
  149. package/dist/server/config/bucket.js +57 -0
  150. package/dist/server/config/bucket.js.map +1 -0
  151. package/dist/server/config/defaults.d.ts +3 -0
  152. package/dist/server/config/defaults.d.ts.map +1 -0
  153. package/dist/server/config/defaults.js +29 -0
  154. package/dist/server/config/defaults.js.map +1 -0
  155. package/dist/server/config/index.d.ts +5 -0
  156. package/dist/server/config/index.d.ts.map +1 -0
  157. package/dist/server/config/index.js +5 -0
  158. package/dist/server/config/index.js.map +1 -0
  159. package/dist/server/config/logger.d.ts +3 -0
  160. package/dist/server/config/logger.d.ts.map +1 -0
  161. package/dist/server/config/logger.js +2 -0
  162. package/dist/server/config/logger.js.map +1 -0
  163. package/dist/server/config/types.d.ts +198 -0
  164. package/dist/server/config/types.d.ts.map +1 -0
  165. package/dist/server/config/types.js +2 -0
  166. package/dist/server/config/types.js.map +1 -0
  167. package/dist/server/core.d.ts +42 -0
  168. package/dist/server/core.d.ts.map +1 -0
  169. package/dist/server/core.js +202 -0
  170. package/dist/server/core.js.map +1 -0
  171. package/dist/server/env.d.ts +39 -0
  172. package/dist/server/env.d.ts.map +1 -0
  173. package/dist/server/env.js +66 -0
  174. package/dist/server/env.js.map +1 -0
  175. package/dist/server/file-queue-protocol-plugin.d.ts +7 -0
  176. package/dist/server/file-queue-protocol-plugin.d.ts.map +1 -0
  177. package/dist/server/file-queue-protocol-plugin.js +150 -0
  178. package/dist/server/file-queue-protocol-plugin.js.map +1 -0
  179. package/dist/server/file-queue.d.ts +25 -0
  180. package/dist/server/file-queue.d.ts.map +1 -0
  181. package/dist/server/file-queue.js +2 -0
  182. package/dist/server/file-queue.js.map +1 -0
  183. package/dist/server/help.d.ts +19 -0
  184. package/dist/server/help.d.ts.map +1 -0
  185. package/dist/server/help.js +91 -0
  186. package/dist/server/help.js.map +1 -0
  187. package/dist/server/index.d.ts +29 -0
  188. package/dist/server/index.d.ts.map +1 -0
  189. package/dist/server/index.js +29 -0
  190. package/dist/server/index.js.map +1 -0
  191. package/dist/server/operation-registry.d.ts +9 -0
  192. package/dist/server/operation-registry.d.ts.map +1 -0
  193. package/dist/server/operation-registry.js +16 -0
  194. package/dist/server/operation-registry.js.map +1 -0
  195. package/dist/server/param-aliases.d.ts +40 -0
  196. package/dist/server/param-aliases.d.ts.map +1 -0
  197. package/dist/server/param-aliases.js +112 -0
  198. package/dist/server/param-aliases.js.map +1 -0
  199. package/dist/server/protocol-plugin.d.ts +72 -0
  200. package/dist/server/protocol-plugin.d.ts.map +1 -0
  201. package/dist/server/protocol-plugin.js +2 -0
  202. package/dist/server/protocol-plugin.js.map +1 -0
  203. package/dist/server/rate-limit/index.d.ts +2 -0
  204. package/dist/server/rate-limit/index.d.ts.map +1 -0
  205. package/dist/server/rate-limit/index.js +2 -0
  206. package/dist/server/rate-limit/index.js.map +1 -0
  207. package/dist/server/rate-limit/utils.d.ts +27 -0
  208. package/dist/server/rate-limit/utils.d.ts.map +1 -0
  209. package/dist/server/rate-limit/utils.js +126 -0
  210. package/dist/server/rate-limit/utils.js.map +1 -0
  211. package/dist/server/routing.d.ts +39 -0
  212. package/dist/server/routing.d.ts.map +1 -0
  213. package/dist/server/routing.js +70 -0
  214. package/dist/server/routing.js.map +1 -0
  215. package/dist/server/rpc-protocol-plugin.d.ts +12 -0
  216. package/dist/server/rpc-protocol-plugin.d.ts.map +1 -0
  217. package/dist/server/rpc-protocol-plugin.js +134 -0
  218. package/dist/server/rpc-protocol-plugin.js.map +1 -0
  219. package/dist/server/server.d.ts +143 -0
  220. package/dist/server/server.d.ts.map +1 -0
  221. package/dist/server/server.js +1161 -0
  222. package/dist/server/server.js.map +1 -0
  223. package/dist/server/token-limit/index.d.ts +2 -0
  224. package/dist/server/token-limit/index.d.ts.map +1 -0
  225. package/dist/server/token-limit/index.js +2 -0
  226. package/dist/server/token-limit/index.js.map +1 -0
  227. package/dist/server/token-limit/utils.d.ts +44 -0
  228. package/dist/server/token-limit/utils.d.ts.map +1 -0
  229. package/dist/server/token-limit/utils.js +260 -0
  230. package/dist/server/token-limit/utils.js.map +1 -0
  231. package/dist/server/tools.d.ts +51 -0
  232. package/dist/server/tools.d.ts.map +1 -0
  233. package/dist/server/tools.js +225 -0
  234. package/dist/server/tools.js.map +1 -0
  235. package/dist/server/transports.d.ts +25 -0
  236. package/dist/server/transports.d.ts.map +1 -0
  237. package/dist/server/transports.js +2 -0
  238. package/dist/server/transports.js.map +1 -0
  239. package/dist/spec/decorators.d.ts +41 -0
  240. package/dist/spec/decorators.d.ts.map +1 -0
  241. package/dist/spec/decorators.js +93 -0
  242. package/dist/spec/decorators.js.map +1 -0
  243. package/dist/spec/index.d.ts +3 -0
  244. package/dist/spec/index.d.ts.map +1 -0
  245. package/dist/spec/index.js +3 -0
  246. package/dist/spec/index.js.map +1 -0
  247. package/dist/spec/metadata.d.ts +5 -0
  248. package/dist/spec/metadata.d.ts.map +1 -0
  249. package/dist/spec/metadata.js +37 -0
  250. package/dist/spec/metadata.js.map +1 -0
  251. package/dist/types/client.d.ts +81 -0
  252. package/dist/types/client.d.ts.map +1 -0
  253. package/dist/types/client.js +14 -0
  254. package/dist/types/client.js.map +1 -0
  255. package/dist/types/endpoints.d.ts +76 -0
  256. package/dist/types/endpoints.d.ts.map +1 -0
  257. package/dist/types/endpoints.js +2 -0
  258. package/dist/types/endpoints.js.map +1 -0
  259. package/dist/types/errors.d.ts +86 -0
  260. package/dist/types/errors.d.ts.map +1 -0
  261. package/dist/types/errors.js +153 -0
  262. package/dist/types/errors.js.map +1 -0
  263. package/dist/types/http.d.ts +80 -0
  264. package/dist/types/http.d.ts.map +1 -0
  265. package/dist/types/http.js +61 -0
  266. package/dist/types/http.js.map +1 -0
  267. package/dist/types/index.d.ts +9 -0
  268. package/dist/types/index.d.ts.map +1 -0
  269. package/dist/types/index.js +9 -0
  270. package/dist/types/index.js.map +1 -0
  271. package/dist/types/openapi.d.ts +220 -0
  272. package/dist/types/openapi.d.ts.map +1 -0
  273. package/dist/types/openapi.js +11 -0
  274. package/dist/types/openapi.js.map +1 -0
  275. package/dist/types/opts.d.ts +46 -0
  276. package/dist/types/opts.d.ts.map +1 -0
  277. package/dist/types/opts.js +2 -0
  278. package/dist/types/opts.js.map +1 -0
  279. package/dist/types/plugins.d.ts +93 -0
  280. package/dist/types/plugins.d.ts.map +1 -0
  281. package/dist/types/plugins.js +5 -0
  282. package/dist/types/plugins.js.map +1 -0
  283. package/dist/types/tools.d.ts +52 -0
  284. package/dist/types/tools.d.ts.map +1 -0
  285. package/dist/types/tools.js +2 -0
  286. package/dist/types/tools.js.map +1 -0
  287. package/package.json +84 -0
package/README.md ADDED
@@ -0,0 +1,574 @@
1
+ # 🦫 plat (aka "platypus")
2
+ > **"call it like you're there"**
3
+
4
+ plat is a **Protocol and Language Agnostic Tooling Yielding Proxy-like Universal Semantics**. In short:
5
+ - just write your methods...
6
+ - ...then call them
7
+
8
+ #### It doesn't matter what you are designing
9
+ - Standard REST API
10
+ - MCP Server
11
+ - A CLI tool
12
+ - Database CRUD
13
+ - AI Tool Calls
14
+ - Inter Process Communication
15
+ - Worker Queues
16
+ - Client-to-client Chat Room
17
+
18
+ > At the end of the day, all there is is **handlers and callers**.
19
+
20
+ #### Yes there is an API layer there, but it isn't really your concern.
21
+ - auth
22
+ - exceptions
23
+ - headers
24
+ - serialization/deserialization/type coercion
25
+ - param validation
26
+ - route building
27
+ - response building
28
+ - rate limiting
29
+ - caching
30
+ - client-side retry logic
31
+ - client-side param validation
32
+
33
+ > All of that can be **handled easily behind the scenes** with easily standardizable **middleware plugins**.
34
+
35
+ #### In fact, the transport method itself doesn't even matter:
36
+ - HTTP
37
+ - WS
38
+ - File Queues
39
+ - DB triggers
40
+ - Zapier Integrations
41
+ - External APIs
42
+ - USPS mail delivery
43
+
44
+ > It's all important, but your function handler **does not need to know** about it, and **neither does your call site**.
45
+
46
+
47
+ #### Okay, seriously ... what are we even talking about here?
48
+ Fair... try this
49
+
50
+ ---
51
+ # Quickstart
52
+
53
+ ## 1. Install
54
+ ```bash
55
+ npm i modularizer-plat
56
+ ```
57
+
58
+ ---
59
+
60
+ ## 2. Make a server
61
+
62
+ ```ts
63
+ import { Controller, POST, createServer, type RouteContext } from "plat"
64
+
65
+ @Controller()
66
+ class OrdersApi {
67
+ @POST()
68
+ async createOrder(
69
+ input: { itemId: string; qty: number },
70
+ ctx: RouteContext,
71
+ ): Promise<{ orderId: string; status: string }> {
72
+ return { orderId: "ord_123", status: "pending" }
73
+ }
74
+ }
75
+
76
+ const server = createServer({ port: 3000 }, OrdersApi)
77
+ server.listen()
78
+ ```
79
+
80
+ ## 3. Serve it with the CLI
81
+
82
+ ```bash
83
+ plat serve
84
+ ```
85
+
86
+ ## 4. See the docs
87
+
88
+ ```bash
89
+ open http://localhost:3000/
90
+ ```
91
+
92
+ ## 5. Make a client
93
+
94
+ ```bash
95
+ plat gen client http://localhost:3000/ --dst client.ts
96
+ ```
97
+
98
+ ```ts
99
+ import { createClient } from "./client"
100
+
101
+
102
+ const client = createClient("http://localhost:3000")
103
+ const order = await client.createOrder({ itemId: "sku_123", qty: 2 })
104
+ console.log(order)
105
+ ```
106
+
107
+ ## 6. Make a CLI
108
+
109
+ ```bash
110
+ plat gen cli http://localhost:3000/ --dst cli.ts
111
+ ```
112
+
113
+ ```bash
114
+ npx tsx cli.ts createOrder --itemId=sku_123 --qty=2
115
+ ```
116
+
117
+ ## 7. Let your AI loose
118
+
119
+ ```ts
120
+ import { OpenAPIClient } from "plat"
121
+
122
+
123
+ const spec = await fetch("http://localhost:3000/openapi.json").then((r) => r.json())
124
+ const client = new OpenAPIClient(spec, { baseUrl: "http://localhost:3000" })
125
+
126
+ const tools = client.tools
127
+ // hand `tools` to your AI provider
128
+ // then call back into `client.createOrder(...)` when it selects a tool
129
+ ```
130
+
131
+ ## Use it
132
+
133
+ - Client call: `client.createOrder({ itemId: "...", qty: 2 })`
134
+ - Generated CLI call: `plat createOrder --itemId=... --qty=2`
135
+ - AI tool call: an LLM can see `createOrder` as a tool with a name, input shape, and result shape
136
+ - Documentation: generated `openapi.json`, plus docs/tool metadata derived from the same method
137
+
138
+
139
+ ---
140
+
141
+
142
+ ## 🎯 Why plat exists
143
+
144
+ Most API frameworks make you think about:
145
+
146
+ - routes
147
+ - methods
148
+ - headers
149
+ - authentication
150
+ - serialization/deserialization/coercion
151
+ - sending responses
152
+ - REST hierarchies
153
+ - request shapes
154
+ - wire protocols
155
+ - client generation drift
156
+ - transport details
157
+
158
+ plat tries to make most of that disappear.
159
+
160
+ What you should be thinking about is:
161
+
162
+ - what methods exist
163
+ - what input each method accepts
164
+ - what result each method returns
165
+
166
+ That’s the part plat treats as sacred.
167
+
168
+ > It feels like you are adding a new class, and behind the scenes an API is born
169
+
170
+ One of the biggest reasons plat exists is to make it easy to use **any AI provider**:
171
+
172
+ - on the client side
173
+ - on the server side
174
+ - as the initiator of your tasks
175
+ - or as the doer of your tasks
176
+ - or both
177
+
178
+ Everything below is in service of that same promise: define useful methods once, then let clients, CLIs, docs, and AI tools all see the same surface.
179
+
180
+ ### Diagram
181
+
182
+ ```
183
+ ┌───────────────────────────────────────────────────────┐
184
+ │ Tool Definitions │
185
+ │ (controllers + decorated methods) │
186
+ │ │
187
+ │ TypeScript (plain types) Python (type hints) │
188
+ │ class Orders { @Controller() │
189
+ │ @Post() class Orders: │
190
+ │ create(input, ctx) {} @POST() │
191
+ │ @Get() def create(self): ... │
192
+ │ list(input, ctx) {} @GET() │
193
+ │ } def list(self): ... │
194
+ └──────────────────────────┬────────────────────────────┘
195
+ │
196
+ ┌──────────┴──────────┐
197
+ │ Operation Registry │
198
+ │ │
199
+ │ operationId ─────► bound handler
200
+ │ method+path ─────► bound handler
201
+ └──────────┬──────────┘
202
+ │
203
+ server protocol plugins (how tool calls arrive)
204
+ │
205
+ ┌───────┬────────┬─────┴───┬────────┬───────┬───────┐
206
+ │ │ │ │ │ │ │
207
+ ┌───┴──┐┌───┴──┐┌────┴───┐┌────┴───┐┌───┴──┐┌───┴──┐┌───┴──┐
208
+ │ HTTP ││ WS ││ File ││ WebRTC ││ DB ││BullMQ││ MQTT │
209
+ │ REST ││ RPC ││ Queue ││ Data ││ Poll ││ Redis││Pub/ │
210
+ │ ││ ││ ││ Chan ││ Rows ││Queue ││ Sub │
211
+ └──────┘└──────┘└────────┘└────────┘└──────┘└──────┘└──────┘
212
+ ...
213
+ literally anything that can carry a JSON envelope
214
+ ...
215
+ ┌──────┐┌──────┐┌────────┐┌────────┐┌──────┐┌──────┐┌──────┐
216
+ │ HTTP ││ WS ││ File ││ WebRTC ││ POST ││ eBay ││ FB │
217
+ │ fetch││ RPC ││ IO ││ Peer ││to ext││ list ││ Msg │
218
+ │ ││ ││ ││ Conn ││ API ││ poll ││ poll │
219
+ └───┬──┘└───┬──┘└────┬───┘└────┬───┘└───┬──┘└───┬──┘└───┬──┘
220
+ │ │ │ │ │ │ │
221
+ └───────┴────────┴────┬────┴────────┴───────┴───────┘
222
+ │
223
+ client transport plugins (how tool calls are sent)
224
+ │
225
+ ┌──────────┴──────────┐
226
+ │ OpenAPI Client │
227
+ │ (typed proxy) │
228
+ └──────────┬──────────┘
229
+ │
230
+ ┌─────────┬────────┼────────┬───────────┐
231
+ │ │ │ │ │
232
+ ┌────┴───┐┌────┴───┐┌───┴───┐┌───┴────┐┌─────┴─────┐
233
+ │ TS ││ Python ││ CLI ││ curl ││ LLM Agent │
234
+ │ ││ ││ ││ bash ││ │
235
+ │ node ││ sync ││ plat do ││ write ││ Claude │
236
+ │ bun ││ async ││plat poll││ JSON ││ ChatGPT │
237
+ │ browser││ promise││ ││to inbox││ Gemini │
238
+ └────────┘└────────┘└───────┘└────────┘└───────────┘
239
+ ```
240
+
241
+ The transport protocol, serialization, deserialization, queueing, and delivery mechanics are intentionally pushed out of your way.
242
+
243
+ That is especially powerful for AI-heavy systems, because you can keep swapping providers and execution patterns while preserving the same tool-shaped surface.
244
+
245
+ ## 🎭 What the user experience should feel like
246
+
247
+ It should feel like this:
248
+
249
+ ```ts
250
+ const order = await client.createOrder({ itemId: "sku_123", qty: 2 })
251
+ ```
252
+
253
+ Not like this:
254
+
255
+ - choosing between totally different client libraries
256
+ - hand-authoring RPC envelopes
257
+ - thinking about HTTP vs WS every time you call a method
258
+ - manually syncing method names, routes, SDK methods, and OpenAPI operation IDs
259
+ - re-implementing error handling, retries, and auth every time you make a request
260
+ - refactoring if you change languages or protocols
261
+ - endless boilerplate
262
+
263
+ It's like an SDK except you don't have to write it. It just comes for free with every `openapi.json`.
264
+
265
+ ## 🦫 Flat by design
266
+
267
+ plat is intentionally opinionated about the API shape.
268
+
269
+ <details open>
270
+ <summary><strong>The rules</strong></summary>
271
+
272
+ - Method names are globally unique
273
+ - Method names are the canonical route names
274
+ - Input comes in as one object
275
+ - Return values matter as first-class API types
276
+ - Controllers organize code and docs, not URL hierarchies
277
+ - The API surface stays flat and easy to call
278
+
279
+ </details>
280
+
281
+ ### Example
282
+
283
+ ```ts
284
+ import { Controller, GET, POST, type RouteContext } from "plat"
285
+
286
+ type GetOrderInput = { id: string }
287
+ type CreateOrderInput = { itemId: string; qty: number }
288
+ type Order = { id: string; status: string }
289
+
290
+ @Controller()
291
+ export class OrdersApi {
292
+ @GET()
293
+ async getOrder(input: GetOrderInput, ctx: RouteContext): Promise<Order> {
294
+ return { id: input.id, status: "pending" }
295
+ }
296
+
297
+ @POST()
298
+ async createOrder(input: CreateOrderInput, ctx: RouteContext): Promise<Order> {
299
+ return { id: "ord_123", status: "pending" }
300
+ }
301
+ }
302
+ ```
303
+
304
+ Canonical routes:
305
+
306
+ - `GET /getOrder`
307
+ - `POST /createOrder`
308
+
309
+ Canonical client calls:
310
+
311
+ ```ts
312
+ await client.getOrder({ id: "ord_123" })
313
+ await client.createOrder({ itemId: "sku_123", qty: 2 })
314
+ ```
315
+
316
+ That flatness matters because it makes the generated and dynamic clients obvious:
317
+
318
+ - easy for humans to remember
319
+ - easy for CLIs to expose
320
+ - easy for AI agents to understand
321
+ - easy for generated clients to mirror exactly
322
+ - easy to hand to any AI provider as tool definitions
323
+
324
+ ## ⏳ Long-running calls without changing the mental model
325
+
326
+ Sometimes a method is fast:
327
+
328
+ ```ts
329
+ await client.createOrder({ itemId: "sku_123", qty: 2 })
330
+ ```
331
+
332
+ Sometimes a method is slow, and you want visibility:
333
+
334
+ ```ts
335
+ await client.importCatalog(
336
+ { source: "s3://bucket/catalog.csv" },
337
+ {
338
+ onRpcEvent(event) {
339
+ console.log(event.event, event.data)
340
+ },
341
+ },
342
+ )
343
+ ```
344
+
345
+ Or you want deferred execution:
346
+
347
+ ```ts
348
+ const handle = await client.importCatalog(
349
+ { source: "s3://bucket/catalog.csv" },
350
+ { execution: "deferred" },
351
+ )
352
+
353
+ const result = await handle.wait()
354
+ ```
355
+
356
+ The important part is that it is still the same method.
357
+
358
+ As a bonus, in the right mode you can get:
359
+
360
+ - progress updates
361
+ - logs
362
+ - chunks/messages
363
+ - cancellation
364
+
365
+ That is what most users actually care about. The carrier and plugin details are for transport authors.
366
+
367
+ ## 🐍 Python support
368
+
369
+ plat supports Python servers and clients too.
370
+
371
+ You can:
372
+
373
+ - write Python controllers with plat decorators
374
+ - generate OpenAPI from `*.api.py`
375
+ - generate Python clients from OpenAPI
376
+ - use sync, async, and promise-style Python clients
377
+
378
+ <details>
379
+ <summary><strong>Python highlights</strong></summary>
380
+
381
+ - Sync clients
382
+ - Async clients
383
+ - Promise-style clients
384
+ - Deferred call handles
385
+ - Automatic input coercion
386
+ - Automatic output serialization
387
+ - First-class HTTP exception types
388
+
389
+ </details>
390
+
391
+ ## 🔌 One client, many transports
392
+
393
+ The same method call should stay usable even when transport changes.
394
+
395
+ ```ts
396
+ const httpClient = createClient("http://localhost:3000")
397
+ const rpcClient = createClient("ws://localhost:3000")
398
+ const fileClient = createClient("file:///tmp/plat-queue")
399
+
400
+ await httpClient.createOrder({ itemId: "sku_123", qty: 2 })
401
+ await rpcClient.createOrder({ itemId: "sku_123", qty: 2 })
402
+ await fileClient.createOrder({ itemId: "sku_123", qty: 2 })
403
+ ```
404
+
405
+ Same tool call. Different carrier.
406
+
407
+ ### Diagram
408
+
409
+ ```txt
410
+ createOrder({ itemId, qty })
411
+ │
412
+ ┌───────┼────────┐
413
+ │ │ │
414
+ ▼ ▼ ▼
415
+ HTTP WS File
416
+ │ │ │
417
+ └───────┼────────┘
418
+ ▼
419
+ same type-aware method call
420
+ ```
421
+
422
+ ## 🤖 AI tool calling
423
+
424
+ plat is a natural fit for LLM tools because the API shape is already tool-shaped.
425
+
426
+ Every operation has:
427
+
428
+ - a stable name
429
+ - one input object
430
+ - one result
431
+ - generated schema
432
+
433
+ That means you can use AI providers in whichever role you want:
434
+
435
+ - as the caller deciding what tools to use
436
+ - as the worker fulfilling part of a task
437
+ - as interchangeable providers inside the same larger workflow
438
+ - on the client side or the server side
439
+
440
+ That makes the same API useful to:
441
+
442
+ - normal app code
443
+ - a CLI
444
+ - generated SDKs
445
+ - an LLM agent
446
+
447
+ ## 🧰 Dynamic clients and generated clients
448
+
449
+ plat supports both styles.
450
+
451
+ <details>
452
+ <summary><strong>Dynamic clients</strong></summary>
453
+
454
+ The OpenAPI client can work directly from an OpenAPI document and a runtime proxy.
455
+
456
+ Best when you want:
457
+
458
+ - low ceremony
459
+ - transport flexibility
460
+ - no generated wrapper code
461
+
462
+ </details>
463
+
464
+ <details>
465
+ <summary><strong>Generated clients</strong></summary>
466
+
467
+ plat can also generate clients that materialize types and methods.
468
+
469
+ Especially useful in Python, where explicit generated models and wrappers help more than in TypeScript.
470
+
471
+ </details>
472
+
473
+ ## 🖥️ CLI
474
+
475
+ plat includes a spec-first CLI.
476
+
477
+ ```bash
478
+ plat gen openapi
479
+ plat gen client
480
+ plat gen cli
481
+ plat run openapi.json
482
+ plat serve
483
+ ```
484
+
485
+ The CLI is available from both Node and Python packaging surfaces, with capability moving toward parity.
486
+
487
+ ## 🧩 Plugin architecture
488
+
489
+ The plugin architecture matters, but mostly as an implementation and extension story.
490
+
491
+ For normal plat users, the important thing is:
492
+
493
+ - methods stay flat
494
+ - typing stays strong
495
+ - clients feel direct
496
+ - transport details stay hidden
497
+ - provider complexity stays hidden too
498
+
499
+ For plugin developers, plat provides the escape hatch.
500
+
501
+ ### Client-side transport plugins
502
+
503
+ Transport plugins follow a generic lifecycle:
504
+
505
+ - connect
506
+ - send request
507
+ - receive updates
508
+ - receive result
509
+ - disconnect
510
+
511
+ ### Server-side protocol plugins
512
+
513
+ Protocol plugins are how tool calls arrive and how updates/results leave.
514
+
515
+ The goal is for the core method/typing/invocation story to be independent from:
516
+
517
+ - HTTP
518
+ - WebSockets
519
+ - Node
520
+ - any specific host process
521
+
522
+ <details>
523
+ <summary><strong>Why this matters</strong></summary>
524
+
525
+ That is what enables ideas like:
526
+
527
+ - a browser-side server
528
+ - a mobile-hosted server
529
+ - a worker-hosted server
530
+ - IndexedDB-backed local APIs
531
+ - WebRTC-based peer-to-peer tools
532
+ - custom carriers like DB polling or Redis streams
533
+
534
+ Most users should not have to think about any of this unless they are building a transport.
535
+
536
+ </details>
537
+
538
+ ## 🌍 What makes plat different
539
+
540
+ Most systems force you to choose:
541
+
542
+ - REST or RPC
543
+ - server or client
544
+ - app integration or AI tool integration
545
+ - HTTP or "something custom"
546
+
547
+ plat is trying to collapse those choices into one model:
548
+
549
+ 1. Define useful tools
550
+ 2. Expose them everywhere
551
+ 3. Change carriers without changing the API itself
552
+
553
+ That makes plat especially interesting for:
554
+
555
+ - internal tools
556
+ - AI agents
557
+ - automation systems
558
+ - offline-first systems
559
+ - browser-hosted local APIs
560
+ - weird protocol experiments
561
+
562
+ ## 🛣️ Direction
563
+
564
+ plat is actively moving toward:
565
+
566
+ - deeper transport neutrality
567
+ - stronger portable server core extraction
568
+ - easier custom protocol plugins
569
+ - stronger generated clients and CLIs
570
+ - better cross-language symmetry
571
+
572
+ The north star is simple:
573
+
574
+ > **Define tools once. Call them from anywhere. Carry them over anything.**
package/bin/z2.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import('../dist/cli/plat.js').catch(err => {
3
+ console.error('Failed to load plat CLI:', err.message)
4
+ process.exit(1)
5
+ })
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Type coercion for incoming data (query strings, form data, etc.)
3
+ *
4
+ * Handles: string→number, string→boolean, string→Date, trim strings.
5
+ * Called before validation so that "42" passes an integer schema.
6
+ */
7
+ export declare function parseRelativeDate(input: string, now?: Date): Date | null;
8
+ export declare function parseDateish(value: unknown, now?: Date, dateOnly?: boolean): Date | null;
9
+ export interface JsonSchema {
10
+ type?: string;
11
+ format?: string;
12
+ coerce?: string | boolean;
13
+ properties?: Record<string, JsonSchema>;
14
+ items?: JsonSchema;
15
+ [key: string]: unknown;
16
+ }
17
+ export type CoerceFn = (value: unknown) => unknown;
18
+ /** Register a custom coercion function, usable via `// coerce: name` in types. */
19
+ export declare function registerCoerce(name: string, fn: CoerceFn): void;
20
+ /**
21
+ * Decorator that registers a function as a custom coercer.
22
+ * The function name becomes the coerce key.
23
+ *
24
+ * @Coerce
25
+ * function slug(v: unknown) {
26
+ * return String(v).trim().toLowerCase().replace(/\s+/g, '-')
27
+ * }
28
+ *
29
+ * Then use in types:
30
+ * type Post = { url: string } // coerce: slug
31
+ */
32
+ export declare function Coerce(target: CoerceFn, context?: {
33
+ name?: string;
34
+ }): void;
35
+ export declare function Coerce(target: any, propertyKey?: string, descriptor?: PropertyDescriptor): void;
36
+ /**
37
+ * Coerce a value to match the expected schema type.
38
+ * Mutates objects in-place for performance.
39
+ *
40
+ * Coercion modes (set via comment or generic: coerce: <mode>):
41
+ * false — no coercion
42
+ * trim — trim whitespace (default for strings)
43
+ * lower — trim + lowercase
44
+ * upper — trim + uppercase
45
+ * number — string → number (default for number/integer types)
46
+ * int — string → integer (floors)
47
+ * bool — string/number → boolean (default for boolean type)
48
+ * date — string/number → ISO date string
49
+ * datetime — string/number → ISO datetime string
50
+ * json — JSON.parse string → object/array
51
+ * split — comma-separated string → array
52
+ */
53
+ export declare function coerce(value: unknown, schema: JsonSchema): unknown;
54
+ //# sourceMappingURL=coerce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coerce.d.ts","sourceRoot":"","sources":["../../src/args/coerce.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AAkBH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAa,GAAG,IAAI,GAAG,IAAI,CA2B9E;AAED,wBAAgB,YAAY,CACxB,KAAK,EAAE,OAAO,EACd,GAAG,OAAa,EAChB,QAAQ,UAAQ,GACjB,IAAI,GAAG,IAAI,CAiBb;AAID,MAAM,WAAW,UAAU;IACvB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACvC,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACzB;AAID,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAA;AAIlD,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,QAExD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAC3E,wBAAgB,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAA;AAahG;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAyBlE"}