@portel/photon 1.9.0 → 1.10.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.
- package/README.md +163 -210
- package/dist/async/dedup-map.d.ts +40 -0
- package/dist/async/dedup-map.d.ts.map +1 -0
- package/dist/async/dedup-map.js +80 -0
- package/dist/async/dedup-map.js.map +1 -0
- package/dist/async/index.d.ts +11 -0
- package/dist/async/index.d.ts.map +1 -0
- package/dist/async/index.js +11 -0
- package/dist/async/index.js.map +1 -0
- package/dist/async/loading-gate.d.ts +27 -0
- package/dist/async/loading-gate.d.ts.map +1 -0
- package/dist/async/loading-gate.js +48 -0
- package/dist/async/loading-gate.js.map +1 -0
- package/dist/async/with-timeout.d.ts +6 -0
- package/dist/async/with-timeout.d.ts.map +1 -0
- package/dist/async/with-timeout.js +17 -0
- package/dist/async/with-timeout.js.map +1 -0
- package/dist/auto-ui/beam/class-metadata.d.ts +52 -0
- package/dist/auto-ui/beam/class-metadata.d.ts.map +1 -0
- package/dist/auto-ui/beam/class-metadata.js +133 -0
- package/dist/auto-ui/beam/class-metadata.js.map +1 -0
- package/dist/auto-ui/beam/config.d.ts +13 -0
- package/dist/auto-ui/beam/config.d.ts.map +1 -0
- package/dist/auto-ui/beam/config.js +52 -0
- package/dist/auto-ui/beam/config.js.map +1 -0
- package/dist/auto-ui/beam/external-mcp.d.ts +37 -0
- package/dist/auto-ui/beam/external-mcp.d.ts.map +1 -0
- package/dist/auto-ui/beam/external-mcp.js +311 -0
- package/dist/auto-ui/beam/external-mcp.js.map +1 -0
- package/dist/auto-ui/beam/photon-management.d.ts +51 -0
- package/dist/auto-ui/beam/photon-management.d.ts.map +1 -0
- package/dist/auto-ui/beam/photon-management.js +310 -0
- package/dist/auto-ui/beam/photon-management.js.map +1 -0
- package/dist/auto-ui/beam/routes/api-browse.d.ts +17 -0
- package/dist/auto-ui/beam/routes/api-browse.d.ts.map +1 -0
- package/dist/auto-ui/beam/routes/api-browse.js +531 -0
- package/dist/auto-ui/beam/routes/api-browse.js.map +1 -0
- package/dist/auto-ui/beam/routes/api-config.d.ts +9 -0
- package/dist/auto-ui/beam/routes/api-config.d.ts.map +1 -0
- package/dist/auto-ui/beam/routes/api-config.js +494 -0
- package/dist/auto-ui/beam/routes/api-config.js.map +1 -0
- package/dist/auto-ui/beam/routes/api-marketplace.d.ts +8 -0
- package/dist/auto-ui/beam/routes/api-marketplace.d.ts.map +1 -0
- package/dist/auto-ui/beam/routes/api-marketplace.js +490 -0
- package/dist/auto-ui/beam/routes/api-marketplace.js.map +1 -0
- package/dist/auto-ui/beam/startup.d.ts +41 -0
- package/dist/auto-ui/beam/startup.d.ts.map +1 -0
- package/dist/auto-ui/beam/startup.js +98 -0
- package/dist/auto-ui/beam/startup.js.map +1 -0
- package/dist/auto-ui/beam/subscription.d.ts +35 -0
- package/dist/auto-ui/beam/subscription.d.ts.map +1 -0
- package/dist/auto-ui/beam/subscription.js +151 -0
- package/dist/auto-ui/beam/subscription.js.map +1 -0
- package/dist/auto-ui/beam/types.d.ts +103 -0
- package/dist/auto-ui/beam/types.d.ts.map +1 -0
- package/dist/auto-ui/beam/types.js +8 -0
- package/dist/auto-ui/beam/types.js.map +1 -0
- package/dist/auto-ui/beam.d.ts +2 -0
- package/dist/auto-ui/beam.d.ts.map +1 -1
- package/dist/auto-ui/beam.js +729 -2596
- package/dist/auto-ui/beam.js.map +1 -1
- package/dist/auto-ui/bridge/index.d.ts.map +1 -1
- package/dist/auto-ui/bridge/index.js +10 -2
- package/dist/auto-ui/bridge/index.js.map +1 -1
- package/dist/auto-ui/components/card.d.ts.map +1 -1
- package/dist/auto-ui/components/card.js +3 -1
- package/dist/auto-ui/components/card.js.map +1 -1
- package/dist/auto-ui/components/progress.d.ts.map +1 -1
- package/dist/auto-ui/components/progress.js.map +1 -1
- package/dist/auto-ui/daemon-tools.d.ts +1 -1
- package/dist/auto-ui/daemon-tools.d.ts.map +1 -1
- package/dist/auto-ui/daemon-tools.js +4 -3
- package/dist/auto-ui/daemon-tools.js.map +1 -1
- package/dist/auto-ui/photon-bridge.d.ts +6 -2
- package/dist/auto-ui/photon-bridge.d.ts.map +1 -1
- package/dist/auto-ui/photon-bridge.js +20 -8
- package/dist/auto-ui/photon-bridge.js.map +1 -1
- package/dist/auto-ui/platform-compat.d.ts.map +1 -1
- package/dist/auto-ui/platform-compat.js +4 -0
- package/dist/auto-ui/platform-compat.js.map +1 -1
- package/dist/auto-ui/streamable-http-transport.d.ts +4 -2
- package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
- package/dist/auto-ui/streamable-http-transport.js +120 -30
- package/dist/auto-ui/streamable-http-transport.js.map +1 -1
- package/dist/auto-ui/types.d.ts +4 -2
- package/dist/auto-ui/types.d.ts.map +1 -1
- package/dist/auto-ui/types.js.map +1 -1
- package/dist/beam.bundle.js +8225 -3999
- package/dist/beam.bundle.js.map +4 -4
- package/dist/cli/commands/alias.d.ts +14 -0
- package/dist/cli/commands/alias.d.ts.map +1 -0
- package/dist/cli/commands/alias.js +41 -0
- package/dist/cli/commands/alias.js.map +1 -0
- package/dist/cli/commands/audit.d.ts +9 -0
- package/dist/cli/commands/audit.d.ts.map +1 -0
- package/dist/cli/commands/audit.js +377 -0
- package/dist/cli/commands/audit.js.map +1 -0
- package/dist/cli/commands/beam.d.ts +20 -0
- package/dist/cli/commands/beam.d.ts.map +1 -0
- package/dist/cli/commands/beam.js +256 -0
- package/dist/cli/commands/beam.js.map +1 -0
- package/dist/cli/commands/config.d.ts +14 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +165 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/daemon.d.ts +11 -0
- package/dist/cli/commands/daemon.d.ts.map +1 -0
- package/dist/cli/commands/daemon.js +108 -0
- package/dist/cli/commands/daemon.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +14 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +257 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/host.d.ts +11 -0
- package/dist/cli/commands/host.d.ts.map +1 -0
- package/dist/cli/commands/host.js +96 -0
- package/dist/cli/commands/host.js.map +1 -0
- package/dist/cli/commands/info.d.ts +1 -1
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +16 -15
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/init.d.ts +20 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +774 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/maker.d.ts +12 -0
- package/dist/cli/commands/maker.d.ts.map +1 -0
- package/dist/cli/commands/maker.js +605 -0
- package/dist/cli/commands/maker.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +27 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +390 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/package-app.d.ts +1 -1
- package/dist/cli/commands/package-app.d.ts.map +1 -1
- package/dist/cli/commands/package-app.js +5 -4
- package/dist/cli/commands/package-app.js.map +1 -1
- package/dist/cli/commands/package.d.ts +1 -1
- package/dist/cli/commands/package.d.ts.map +1 -1
- package/dist/cli/commands/package.js +134 -32
- package/dist/cli/commands/package.js.map +1 -1
- package/dist/cli/commands/run.d.ts +34 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +334 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/search.d.ts +11 -0
- package/dist/cli/commands/search.d.ts.map +1 -0
- package/dist/cli/commands/search.js +60 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +11 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +138 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/test.d.ts +14 -0
- package/dist/cli/commands/test.d.ts.map +1 -0
- package/dist/cli/commands/test.js +51 -0
- package/dist/cli/commands/test.js.map +1 -0
- package/dist/cli/commands/update.d.ts +11 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/update.js +72 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +139 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli-alias.js +2 -2
- package/dist/cli-alias.js.map +1 -1
- package/dist/cli.d.ts +3 -16
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -2725
- package/dist/cli.js.map +1 -1
- package/dist/context-store.d.ts +13 -12
- package/dist/context-store.d.ts.map +1 -1
- package/dist/context-store.js +47 -23
- package/dist/context-store.js.map +1 -1
- package/dist/context.d.ts +35 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +38 -0
- package/dist/context.js.map +1 -0
- package/dist/daemon/client.d.ts +25 -13
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +183 -135
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/manager.d.ts +58 -26
- package/dist/daemon/manager.d.ts.map +1 -1
- package/dist/daemon/manager.js +348 -157
- package/dist/daemon/manager.js.map +1 -1
- package/dist/daemon/protocol.d.ts +9 -3
- package/dist/daemon/protocol.d.ts.map +1 -1
- package/dist/daemon/protocol.js +2 -0
- package/dist/daemon/protocol.js.map +1 -1
- package/dist/daemon/server.js +850 -200
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/session-manager.d.ts +16 -2
- package/dist/daemon/session-manager.d.ts.map +1 -1
- package/dist/daemon/session-manager.js +65 -7
- package/dist/daemon/session-manager.js.map +1 -1
- package/dist/daemon/state-machine.d.ts +22 -0
- package/dist/daemon/state-machine.d.ts.map +1 -0
- package/dist/daemon/state-machine.js +48 -0
- package/dist/daemon/state-machine.js.map +1 -0
- package/dist/deploy/cloudflare.d.ts.map +1 -1
- package/dist/deploy/cloudflare.js +5 -5
- package/dist/deploy/cloudflare.js.map +1 -1
- package/dist/loader.d.ts +65 -7
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +587 -63
- package/dist/loader.js.map +1 -1
- package/dist/marketplace-manager.d.ts +84 -12
- package/dist/marketplace-manager.d.ts.map +1 -1
- package/dist/marketplace-manager.js +470 -26
- package/dist/marketplace-manager.js.map +1 -1
- package/dist/path-resolver.d.ts +3 -1
- package/dist/path-resolver.d.ts.map +1 -1
- package/dist/path-resolver.js +4 -3
- package/dist/path-resolver.js.map +1 -1
- package/dist/photon-cli-runner.d.ts +1 -1
- package/dist/photon-cli-runner.d.ts.map +1 -1
- package/dist/photon-cli-runner.js +34 -44
- package/dist/photon-cli-runner.js.map +1 -1
- package/dist/photon-doc-extractor.d.ts +1 -0
- package/dist/photon-doc-extractor.d.ts.map +1 -1
- package/dist/photon-doc-extractor.js +33 -12
- package/dist/photon-doc-extractor.js.map +1 -1
- package/dist/photons/maker.photon.d.ts.map +1 -1
- package/dist/photons/maker.photon.js +4 -4
- package/dist/photons/maker.photon.js.map +1 -1
- package/dist/photons/maker.photon.ts +4 -3
- package/dist/photons/marketplace.photon.d.ts.map +1 -1
- package/dist/photons/marketplace.photon.js +10 -27
- package/dist/photons/marketplace.photon.js.map +1 -1
- package/dist/photons/marketplace.photon.ts +14 -33
- package/dist/photons/tunnel.photon.d.ts.map +1 -1
- package/dist/photons/tunnel.photon.js +4 -8
- package/dist/photons/tunnel.photon.js.map +1 -1
- package/dist/photons/tunnel.photon.ts +4 -7
- package/dist/serv/session/kv-store.d.ts +1 -1
- package/dist/serv/session/kv-store.d.ts.map +1 -1
- package/dist/serv/session/store.d.ts.map +1 -1
- package/dist/serv/session/store.js +16 -14
- package/dist/serv/session/store.js.map +1 -1
- package/dist/serv/vault/token-vault.js +1 -1
- package/dist/serv/vault/token-vault.js.map +1 -1
- package/dist/server.d.ts +34 -12
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +364 -313
- package/dist/server.js.map +1 -1
- package/dist/shared/audit.d.ts +30 -0
- package/dist/shared/audit.d.ts.map +1 -0
- package/dist/shared/audit.js +89 -0
- package/dist/shared/audit.js.map +1 -0
- package/dist/shared/cli-sections.d.ts +0 -4
- package/dist/shared/cli-sections.d.ts.map +1 -1
- package/dist/shared/cli-sections.js +0 -6
- package/dist/shared/cli-sections.js.map +1 -1
- package/dist/shared/cli-utils.d.ts +2 -56
- package/dist/shared/cli-utils.d.ts.map +1 -1
- package/dist/shared/cli-utils.js +1 -87
- package/dist/shared/cli-utils.js.map +1 -1
- package/dist/shared/error-handler.d.ts +6 -72
- package/dist/shared/error-handler.d.ts.map +1 -1
- package/dist/shared/error-handler.js +22 -213
- package/dist/shared/error-handler.js.map +1 -1
- package/dist/shared/security.d.ts +0 -9
- package/dist/shared/security.d.ts.map +1 -1
- package/dist/shared/security.js +0 -30
- package/dist/shared/security.js.map +1 -1
- package/dist/shared-utils.d.ts +0 -26
- package/dist/shared-utils.d.ts.map +1 -1
- package/dist/shared-utils.js +0 -44
- package/dist/shared-utils.js.map +1 -1
- package/dist/shell-completions.d.ts +1 -1
- package/dist/shell-completions.d.ts.map +1 -1
- package/dist/shell-completions.js +5 -5
- package/dist/shell-completions.js.map +1 -1
- package/dist/template-manager.d.ts.map +1 -1
- package/dist/template-manager.js +14 -1
- package/dist/template-manager.js.map +1 -1
- package/dist/test-runner.d.ts +0 -12
- package/dist/test-runner.d.ts.map +1 -1
- package/dist/test-runner.js +4 -39
- package/dist/test-runner.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/dist/version-checker.d.ts +4 -4
- package/dist/version-checker.d.ts.map +1 -1
- package/dist/version-checker.js +33 -4
- package/dist/version-checker.js.map +1 -1
- package/dist/watcher.d.ts.map +1 -1
- package/dist/watcher.js +14 -12
- package/dist/watcher.js.map +1 -1
- package/package.json +24 -17
package/README.md
CHANGED
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/photon-logo.png" alt="Photon" width="500">
|
|
5
5
|
|
|
6
|
-
**
|
|
7
|
-
|
|
8
|
-
A framework, runtime, and ecosystem. Batteries included.
|
|
6
|
+
**Software for humans and AI. Written once.**
|
|
9
7
|
|
|
10
8
|
[](https://www.npmjs.com/package/@portel/photon)
|
|
11
9
|
[](https://www.npmjs.com/package/@portel/photon)
|
|
@@ -14,140 +12,146 @@ A framework, runtime, and ecosystem. Batteries included.
|
|
|
14
12
|
[](https://nodejs.org)
|
|
15
13
|
[](https://modelcontextprotocol.io)
|
|
16
14
|
|
|
17
|
-
[Quick Start](#quick-start) · [Why Photon](#why-did-we-build-this) · [Beam UI](#beam) · [How It Works](#how-it-works) · [Docs](#documentation)
|
|
18
|
-
|
|
19
|
-
[](https://www.youtube.com/watch?v=FI0M8s6ZKv4)
|
|
20
|
-
|
|
21
15
|
</div>
|
|
22
16
|
|
|
23
17
|
---
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
So, here is the situation. You write a single TypeScript file. Just one. And somehow, through some dark magic I don't fully understand either, you get three things at once:
|
|
19
|
+
Your tools have two kinds of consumers now. Humans who open a dashboard and explore. AI agents that call your methods through a protocol. Until now, you've been building for one or the other, or building everything twice.
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
2. **A CLI tool** (so you can run it from the terminal like a normal human).
|
|
31
|
-
3. **A web application** (a visual dashboard called "Beam" that makes forms for you).
|
|
21
|
+
Photon is built around a different premise: **write what you mean, and let both consumers figure it out from that**.
|
|
32
22
|
|
|
33
|
-
|
|
23
|
+
You write a TypeScript class. Methods are your capabilities. Types describe what's valid. Comments explain the intent. That's it. Photon reads all of it and generates a web UI for human exploration, a CLI for scripting, and an MCP server for AI agents. Same logic. Same validation. Same data. Three interfaces from one file.
|
|
34
24
|
|
|
35
25
|
```
|
|
36
|
-
|
|
26
|
+
analytics.photon.ts → Web UI (Beam) · CLI · MCP Server for AI
|
|
37
27
|
```
|
|
38
28
|
|
|
39
|
-
|
|
29
|
+
The code stays simple, almost embarrassingly simple, because the complexity isn't in what you write. It's in what Photon derives from it.
|
|
40
30
|
|
|
41
|
-
|
|
31
|
+
<div align="center">
|
|
42
32
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
<a href="https://www.youtube.com/watch?v=FI0M8s6ZKv4">
|
|
34
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/video-preview.png" alt="Watch: Why Photon? (2 min)" width="100%">
|
|
35
|
+
</a>
|
|
46
36
|
|
|
47
|
-
|
|
37
|
+
</div>
|
|
48
38
|
|
|
49
39
|
---
|
|
50
40
|
|
|
51
|
-
##
|
|
41
|
+
## Quick Start
|
|
52
42
|
|
|
53
|
-
|
|
43
|
+
```bash
|
|
44
|
+
npm install -g @portel/photon
|
|
45
|
+
photon maker new my-tool # Create a photon
|
|
46
|
+
photon # Open Beam, the web UI
|
|
47
|
+
```
|
|
54
48
|
|
|
55
|
-
|
|
49
|
+
Or without installing:
|
|
56
50
|
|
|
57
|
-
|
|
51
|
+
```bash
|
|
52
|
+
npx @portel/photon maker new my-tool
|
|
53
|
+
npx @portel/photon
|
|
54
|
+
```
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
> Requires [Node.js 18+](https://nodejs.org). TypeScript is compiled internally; no `tsconfig.json` needed.
|
|
60
57
|
|
|
61
58
|
---
|
|
62
59
|
|
|
63
|
-
##
|
|
60
|
+
## What You Actually Write
|
|
64
61
|
|
|
65
|
-
|
|
62
|
+
Here is a complete, working photon:
|
|
66
63
|
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
```typescript
|
|
65
|
+
export default class Analytics {
|
|
66
|
+
async report(params: { period: string }) {
|
|
67
|
+
return await db.query(`SELECT * FROM events WHERE period = $1`, [params.period]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
71
70
|
```
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
From this, Photon generates:
|
|
73
|
+
- A web form in Beam with a `period` text input
|
|
74
|
+
- `photon cli analytics report --period 2024-Q4`
|
|
75
|
+
- An MCP tool that Claude or Cursor can invoke
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
npx @portel/photon maker new my-tool
|
|
77
|
-
npx @portel/photon
|
|
78
|
-
```
|
|
77
|
+
No decorators. No registration. No server boilerplate. You wrote the logic. Photon derived the rest.
|
|
79
78
|
|
|
80
|
-
|
|
79
|
+
The more you express, the more Photon understands.
|
|
81
80
|
|
|
82
81
|
---
|
|
83
82
|
|
|
84
|
-
##
|
|
83
|
+
## Everything You Add Becomes Something Useful
|
|
84
|
+
|
|
85
|
+
Photon reads your TypeScript as **intent**. Every construct you'd write anyway carries meaning it can act on.
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
| What you write | What Photon derives |
|
|
88
|
+
|---|---|
|
|
89
|
+
| Method signatures | Tool definitions: names, inputs, outputs |
|
|
90
|
+
| Type annotations | Input validation rules, UI field types |
|
|
91
|
+
| JSDoc comments | Documentation for AI clients and human users |
|
|
92
|
+
| Constructor parameters | Config UI, environment variable mapping |
|
|
93
|
+
| `@tags` | Validation, formatting, scheduling, webhooks |
|
|
87
94
|
|
|
88
|
-
|
|
95
|
+
So when you add a `@param city {@pattern ^[a-zA-Z\s]+$}` annotation you were going to write anyway, Beam automatically validates it in the form, the CLI validates it before running, and the MCP schema enforces it for the AI. One annotation. Three consumers.
|
|
89
96
|
|
|
90
97
|
<div align="center">
|
|
91
|
-
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/
|
|
98
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/photon-ecosystem.png" alt="Photon: one file, three surfaces" width="100%">
|
|
92
99
|
</div>
|
|
93
100
|
|
|
94
101
|
---
|
|
95
102
|
|
|
96
|
-
##
|
|
103
|
+
## Beam: Human Exploration
|
|
104
|
+
|
|
105
|
+
Beam is the web dashboard. Every photon becomes an interactive form. Run `photon`. That's the whole command.
|
|
97
106
|
|
|
98
|
-
|
|
107
|
+
<div align="center">
|
|
108
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/beam-dashboard.png" alt="Beam Dashboard" width="100%">
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
The UI is **fully auto-generated** from your method signatures: field types, validation, defaults, layouts. You never write frontend code. When you add a `{@choice a,b,c}` tag to a parameter, Beam renders a dropdown. When you mark a string as `{@format email}`, the field validates email format. The UI evolves as your code does.
|
|
112
|
+
|
|
113
|
+
When forms aren't the right interface for what you're building, you can replace Beam's auto-generated view with your own HTML. The custom UI receives tool results via `window.photon.onResult()`, a thin bridge with no framework required.
|
|
114
|
+
|
|
115
|
+
> Custom UIs follow the [MCP Apps Extension (SEP-1865)](https://github.com/nicolo-ribaudo/modelcontextprotocol/blob/nicolo/sep-1865/docs/specification/draft/extensions/apps.mdx) standard and work across compatible hosts. See the [Custom UI Guide](./docs/guides/CUSTOM-UI.md).
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## AI Agents: Machine Invocation
|
|
99
120
|
|
|
100
121
|
```bash
|
|
101
|
-
photon info
|
|
122
|
+
photon info analytics --mcp
|
|
102
123
|
```
|
|
103
124
|
|
|
104
|
-
It spits out some JSON:
|
|
105
|
-
|
|
106
125
|
```json
|
|
107
126
|
{
|
|
108
127
|
"mcpServers": {
|
|
109
|
-
"
|
|
128
|
+
"analytics": {
|
|
110
129
|
"command": "photon",
|
|
111
|
-
"args": ["mcp", "
|
|
130
|
+
"args": ["mcp", "analytics"]
|
|
112
131
|
}
|
|
113
132
|
}
|
|
114
133
|
}
|
|
115
134
|
```
|
|
116
135
|
|
|
117
|
-
|
|
136
|
+
Paste into your AI client's config. Your photon is now an MCP server. Claude can call your methods. Cursor can call your methods. Any MCP-compatible host can call your methods.
|
|
118
137
|
|
|
119
|
-
|
|
138
|
+
The AI sees the same thing a human sees in Beam: the method names, the parameter descriptions from your JSDoc, the validation rules from your types. The JSDoc comment you wrote to document the tool for yourself is what Claude reads to decide when and how to call it.
|
|
120
139
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
## Marketplace
|
|
140
|
+
The MCP tools themselves work with [Claude Desktop](https://claude.ai/download), [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Cursor](https://cursor.com), and any MCP-compatible client.
|
|
124
141
|
|
|
125
|
-
|
|
142
|
+
When your photon has a custom UI, clients that support the [MCP Apps Extension](https://github.com/nicolo-ribaudo/modelcontextprotocol/blob/nicolo/sep-1865/docs/specification/draft/extensions/apps.mdx) render it natively, no separate app needed. The photon below is running inside Claude Desktop, same UI, same data as Beam.
|
|
126
143
|
|
|
127
144
|
<div align="center">
|
|
128
|
-
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/
|
|
145
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/claude-desktop.png" alt="Photon running as an MCP App with custom UI inside Claude Desktop" width="100%">
|
|
129
146
|
</div>
|
|
130
147
|
|
|
131
|
-
```bash
|
|
132
|
-
photon search postgres
|
|
133
|
-
photon add postgres
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Browse the full catalog and documentation in the [official photons repository](https://github.com/portel-dev/photons).
|
|
137
|
-
|
|
138
|
-
You can also make a private marketplace for your team, so internal tools stay off the public internet.
|
|
139
|
-
|
|
140
148
|
---
|
|
141
149
|
|
|
142
|
-
##
|
|
150
|
+
## The Progression
|
|
143
151
|
|
|
144
|
-
|
|
152
|
+
Here is how a photon grows. Each step adds one thing and gets multiple capabilities from it.
|
|
145
153
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
### Step 1: The Bare Minimum
|
|
149
|
-
|
|
150
|
-
Here is a class with one method. This is a valid photon.
|
|
154
|
+
### Bare method: three interfaces from five lines
|
|
151
155
|
|
|
152
156
|
```typescript
|
|
153
157
|
export default class Weather {
|
|
@@ -157,47 +161,34 @@ export default class Weather {
|
|
|
157
161
|
}
|
|
158
162
|
```
|
|
159
163
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
**What you get:**
|
|
163
|
-
* `photon mcp weather` (The server for Claude)
|
|
164
|
-
* `photon cli weather forecast --city Paris` (The command line tool)
|
|
165
|
-
* `photon` (The web UI)
|
|
164
|
+
A text input in Beam. A `--city` flag in the CLI. An MCP input schema. From five lines.
|
|
166
165
|
|
|
167
166
|
<div align="center">
|
|
168
|
-
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/readme-step-1.png" alt="Step 1
|
|
167
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/readme-step-1.png" alt="Step 1" width="100%">
|
|
169
168
|
</div>
|
|
170
169
|
|
|
171
|
-
###
|
|
172
|
-
|
|
173
|
-
If you add JSDoc comments, they show up as descriptions.
|
|
170
|
+
### Add comments: AI understands your intent
|
|
174
171
|
|
|
175
172
|
```typescript
|
|
176
173
|
/**
|
|
177
174
|
* Weather - Check weather forecasts worldwide
|
|
178
|
-
*
|
|
179
|
-
* Provides current conditions.
|
|
180
175
|
*/
|
|
181
176
|
export default class Weather {
|
|
182
177
|
/**
|
|
183
|
-
* Get the weather forecast
|
|
178
|
+
* Get the weather forecast for a city
|
|
184
179
|
* @param city City name (e.g., "London")
|
|
185
180
|
*/
|
|
186
|
-
async forecast(params: { city: string }) {
|
|
187
|
-
return `Weather for ${params.city}: Sunny, 72°F`;
|
|
188
|
-
}
|
|
181
|
+
async forecast(params: { city: string }) { ... }
|
|
189
182
|
}
|
|
190
183
|
```
|
|
191
184
|
|
|
192
|
-
|
|
185
|
+
The class description becomes how AI clients introduce the tool to users. The `@param` description is what the AI reads before deciding what value to pass. Same comments. Human help text and AI contract at once.
|
|
193
186
|
|
|
194
187
|
<div align="center">
|
|
195
|
-
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/readme-step-2.png" alt="Step 2
|
|
188
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/readme-step-2.png" alt="Step 2" width="100%">
|
|
196
189
|
</div>
|
|
197
190
|
|
|
198
|
-
###
|
|
199
|
-
|
|
200
|
-
If you need an API key, put it in the constructor.
|
|
191
|
+
### Add a constructor: configuration appears
|
|
201
192
|
|
|
202
193
|
```typescript
|
|
203
194
|
export default class Weather {
|
|
@@ -207,163 +198,137 @@ export default class Weather {
|
|
|
207
198
|
) {}
|
|
208
199
|
|
|
209
200
|
async forecast(params: { city: string }) {
|
|
210
|
-
const res = await fetch(
|
|
211
|
-
`https://api.openweathermap.org/data/2.5/weather?q=${params.city}&appid=${this.apiKey}&units=${this.units}`
|
|
212
|
-
);
|
|
201
|
+
const res = await fetch(`...?appid=${this.apiKey}&units=${this.units}`);
|
|
213
202
|
return await res.json();
|
|
214
203
|
}
|
|
215
204
|
}
|
|
216
205
|
```
|
|
217
206
|
|
|
218
|
-
|
|
207
|
+
`apiKey` becomes a password field in the Beam settings panel and maps to the `WEATHER_API_KEY` environment variable. `units` gets a text input with `'metric'` pre-filled. You declared what you need. Photon built the configuration surface.
|
|
219
208
|
|
|
220
209
|
<div align="center">
|
|
221
|
-
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/readme-step-3.png" alt="Step 3
|
|
210
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/readme-step-3.png" alt="Step 3" width="100%">
|
|
222
211
|
</div>
|
|
223
212
|
|
|
224
|
-
###
|
|
225
|
-
|
|
226
|
-
You can add tags to valid inputs.
|
|
213
|
+
### Add tags: behavior extends across all surfaces
|
|
227
214
|
|
|
228
215
|
```typescript
|
|
229
216
|
/**
|
|
230
|
-
* Weather - Check weather forecasts worldwide
|
|
231
217
|
* @dependencies node-fetch@^3.0.0
|
|
232
218
|
*/
|
|
233
219
|
export default class Weather {
|
|
234
|
-
constructor(
|
|
235
|
-
private apiKey: string,
|
|
236
|
-
private units: string = 'metric'
|
|
237
|
-
) {}
|
|
238
|
-
|
|
239
220
|
/**
|
|
240
|
-
* Get the weather forecast for a city
|
|
241
221
|
* @param city City name {@example London} {@pattern ^[a-zA-Z\s]+$}
|
|
242
222
|
* @param days Number of days {@min 1} {@max 7}
|
|
243
223
|
* @format table
|
|
244
224
|
*/
|
|
245
|
-
async forecast(params: { city: string; days?: number }) {
|
|
246
|
-
// fetch and return forecast data...
|
|
247
|
-
}
|
|
225
|
+
async forecast(params: { city: string; days?: number }) { ... }
|
|
248
226
|
}
|
|
249
227
|
```
|
|
250
228
|
|
|
251
|
-
|
|
252
|
-
* The `city` input validates the regex.
|
|
253
|
-
* The `days` input becomes a number spinner (1-7).
|
|
254
|
-
* The result is formatted as a table.
|
|
255
|
-
* `@dependencies` makes Photon install `node-fetch` automatically. You don't even run `npm install`.
|
|
229
|
+
`@dependencies` installs `node-fetch` automatically on first run, no `npm install` needed. The `{@pattern}` validates in the form, the CLI, and the MCP schema simultaneously. `days` becomes a number spinner with bounds. `@format table` renders the result as a table in Beam. One annotation, three surfaces.
|
|
256
230
|
|
|
257
|
-
|
|
231
|
+
<div align="center">
|
|
232
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/readme-step-4.png" alt="Step 4" width="100%">
|
|
233
|
+
</div>
|
|
258
234
|
|
|
259
|
-
|
|
235
|
+
### System CLI dependencies
|
|
236
|
+
|
|
237
|
+
If your photon wraps a command-line tool, declare it and Photon enforces it at load time:
|
|
260
238
|
|
|
261
239
|
```typescript
|
|
262
240
|
/**
|
|
263
|
-
* Video processor
|
|
264
241
|
* @cli ffmpeg - https://ffmpeg.org/download.html
|
|
265
|
-
* @cli imagemagick - https://imagemagick.org/script/download.php
|
|
266
242
|
*/
|
|
267
243
|
export default class VideoProcessor {
|
|
268
244
|
async convert({ input, format }: { input: string; format: string }) {
|
|
269
|
-
// ffmpeg is guaranteed to exist
|
|
245
|
+
// ffmpeg is guaranteed to exist when this runs
|
|
270
246
|
}
|
|
271
247
|
}
|
|
272
248
|
```
|
|
273
249
|
|
|
274
|
-
|
|
250
|
+
<div align="center">
|
|
251
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/readme-step-5.png" alt="Step 5" width="100%">
|
|
252
|
+
</div>
|
|
275
253
|
|
|
276
|
-
|
|
277
|
-
VideoProcessor requires the following CLI tools to be installed:
|
|
278
|
-
- ffmpeg: Install from https://ffmpeg.org/download.html
|
|
279
|
-
```
|
|
254
|
+
---
|
|
280
255
|
|
|
281
|
-
|
|
256
|
+
## What Comes for Free
|
|
282
257
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
258
|
+
Things you don't build because Photon handles them:
|
|
259
|
+
|
|
260
|
+
| | |
|
|
261
|
+
|---|---|
|
|
262
|
+
| **Auto-UI** | Forms, field types, validation, layouts generated from your signatures |
|
|
263
|
+
| **Stateful instances** | Multiple named instances of the same photon, each with isolated state |
|
|
264
|
+
| **Persistent memory** | `this.memory` gives your photon per-instance key-value storage, no database needed |
|
|
265
|
+
| **Scheduled execution** | `@scheduled` runs any method on a cron schedule |
|
|
266
|
+
| **Webhooks** | `@webhook` exposes any method as an HTTP endpoint |
|
|
267
|
+
| **OAuth** | Built-in OAuth 2.0 flows for Google, GitHub, Microsoft |
|
|
268
|
+
| **Distributed locks** | `@locked` serializes access: one caller at a time, across processes |
|
|
269
|
+
| **Cross-photon calls** | `this.call()` invokes another photon's methods |
|
|
270
|
+
| **Real-time events** | `this.emit()` fires named events to the browser UI with zero wiring |
|
|
271
|
+
| **Dependency management** | `@dependencies` auto-installs npm packages on first run |
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Coordination: Locks + Events
|
|
286
276
|
|
|
287
|
-
|
|
277
|
+
Two primitives. Together they unlock a class of things that are surprisingly hard to build today.
|
|
288
278
|
|
|
289
|
-
|
|
279
|
+
**Locks** serialize access. When a method is marked `@locked`, only one caller can execute at a time, whether that caller is a human in Beam, a CLI script, or an AI agent. Everyone else waits their turn.
|
|
280
|
+
|
|
281
|
+
**Events** push state changes to any browser UI in real time. `this.emit('name', data)` in your method fires `window.photon.on('name', handler)` in your custom UI. No WebSockets to configure. No polling. The data marshalling and delivery is handled by the system.
|
|
282
|
+
|
|
283
|
+
Together: **turn-based coordination with live state**.
|
|
290
284
|
|
|
291
285
|
```typescript
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
export default class Weather {
|
|
298
|
-
constructor(private apiKey: string, private units: string = 'metric') {}
|
|
286
|
+
export default class Chess {
|
|
287
|
+
/** Make a move. Locks ensure human and AI alternate turns. */
|
|
288
|
+
/** @locked */
|
|
289
|
+
async move(params: { from: string; to: string }) {
|
|
290
|
+
const result = await this.applyMove(params.from, params.to);
|
|
299
291
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
async forecast(params: { city: string; days?: number }) {
|
|
306
|
-
// returns structured weather data
|
|
292
|
+
// Browser UI updates instantly, no polling needed
|
|
293
|
+
this.emit('board-updated', result.board);
|
|
294
|
+
this.emit('turn-changed', { next: result.nextPlayer });
|
|
295
|
+
|
|
296
|
+
return result;
|
|
307
297
|
}
|
|
308
298
|
}
|
|
309
299
|
```
|
|
310
300
|
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
</div>
|
|
316
|
-
<script>
|
|
317
|
-
window.photon.onResult(data => {
|
|
318
|
-
document.getElementById('forecast').innerHTML = renderWeather(data);
|
|
319
|
-
});
|
|
320
|
-
</script>
|
|
301
|
+
```javascript
|
|
302
|
+
// In your custom UI (ui/chess.html)
|
|
303
|
+
window.photon.on('board-updated', board => renderBoard(board));
|
|
304
|
+
window.photon.on('turn-changed', ({ next }) => showTurn(next));
|
|
321
305
|
```
|
|
322
306
|
|
|
323
|
-
|
|
307
|
+
A human moves through Beam. Claude is configured with the MCP server. The lock ensures they truly alternate. Events keep the board live on both sides. That's a fully functional turn-based chess game, human vs AI, in about 50 lines of application logic.
|
|
324
308
|
|
|
325
|
-
|
|
309
|
+
The same pattern applies beyond games: approval workflows where a human reviews before AI continues, collaborative tools where edits from any source appear instantly, simulations where steps must execute in strict sequence, any system where **who acts next matters**.
|
|
326
310
|
|
|
327
|
-
|
|
328
|
-
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/readme-step-5.png" alt="Step 5 — Custom UI result in Beam" width="100%">
|
|
329
|
-
</div>
|
|
311
|
+
---
|
|
330
312
|
|
|
331
|
-
|
|
313
|
+
## Marketplace
|
|
332
314
|
|
|
333
|
-
|
|
334
|
-
|------|---------|-----------------|
|
|
335
|
-
| **1. Methods** | A function | Tools, CLI commands, Forms |
|
|
336
|
-
| **2. JSDoc** | Comments | Descriptions for AI and Humans |
|
|
337
|
-
| **3. Constructor** | Arguments | Config UI, Env vars |
|
|
338
|
-
| **4. Tags** | `@tags` | Validation, Installers, Formatting |
|
|
339
|
-
| **5. Custom UI** | HTML | A custom app |
|
|
315
|
+
32 photons ready to install: databases, APIs, developer tools, and more.
|
|
340
316
|
|
|
341
317
|
<div align="center">
|
|
342
|
-
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/
|
|
318
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/beam-marketplace.png" alt="Marketplace" width="100%">
|
|
343
319
|
</div>
|
|
344
320
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
If you are just skimming, here is what you need to know:
|
|
321
|
+
```bash
|
|
322
|
+
photon search postgres
|
|
323
|
+
photon add postgres
|
|
324
|
+
```
|
|
350
325
|
|
|
351
|
-
|
|
352
|
-
|---------|-----------|------------|
|
|
353
|
-
| **MCP** | A way for AI to use your tools. It's a standard. | [modelcontextprotocol.io](https://modelcontextprotocol.io/introduction) |
|
|
354
|
-
| **Photon file** | A `.photon.ts` file. You define tools as methods in a class. | [Guide](./docs/GUIDE.md) |
|
|
355
|
-
| **Beam** | A web dashboard. It shows your tools as forms. | [Beam UI](#beam) |
|
|
356
|
-
| **Marketplace** | A way to get other people's photons. | [Marketplace](#marketplace) |
|
|
357
|
-
| **Daemon** | A background thing that handles messages and jobs. | [Daemon Pub/Sub](./docs/core/DAEMON-PUBSUB.md) |
|
|
358
|
-
| **Tags** | JSDoc comments that tell Photon what to do. | [Tag Reference](./docs/reference/DOCBLOCK-TAGS.md) |
|
|
359
|
-
| **Custom UI** | When the auto-generated forms aren't enough. | [Custom UI Guide](./docs/guides/CUSTOM-UI.md) |
|
|
326
|
+
Browse the full catalog in the [official photons repository](https://github.com/portel-dev/photons). You can also host a private marketplace for your team: internal tools that stay off the public internet.
|
|
360
327
|
|
|
361
328
|
---
|
|
362
329
|
|
|
363
330
|
## Commands
|
|
364
331
|
|
|
365
|
-
A few commands you might use:
|
|
366
|
-
|
|
367
332
|
```bash
|
|
368
333
|
# Run
|
|
369
334
|
photon # Open Beam UI
|
|
@@ -376,7 +341,7 @@ photon maker new <name> # Scaffold a new photon
|
|
|
376
341
|
|
|
377
342
|
# Manage
|
|
378
343
|
photon info # List all photons
|
|
379
|
-
photon info <name> --mcp # Get MCP client config
|
|
344
|
+
photon info <name> --mcp # Get MCP client config
|
|
380
345
|
photon maker validate <name> # Check for errors
|
|
381
346
|
|
|
382
347
|
# Marketplace
|
|
@@ -391,15 +356,13 @@ photon test # Run tests
|
|
|
391
356
|
|
|
392
357
|
---
|
|
393
358
|
|
|
394
|
-
## Tag Reference
|
|
395
|
-
|
|
396
|
-
Tags are JSDoc annotations that control how Photon processes your code. Here are the most commonly used ones:
|
|
359
|
+
## Tag Reference
|
|
397
360
|
|
|
398
361
|
| Tag | Where | What it does |
|
|
399
|
-
|
|
362
|
+
|---|---|---|
|
|
400
363
|
| `@dependencies` | Class | Auto-install npm packages on first run |
|
|
401
|
-
| `@cli` | Class | Declare system CLI
|
|
402
|
-
| `@format` | Method |
|
|
364
|
+
| `@cli` | Class | Declare system CLI dependencies, checked at load time |
|
|
365
|
+
| `@format` | Method | Result rendering (table, list, markdown, code, etc.) |
|
|
403
366
|
| `@param ... {@choice a,b,c}` | Param | Dropdown selection in Beam |
|
|
404
367
|
| `@param ... {@format email}` | Param | Input validation and field type |
|
|
405
368
|
| `@param ... {@min N} {@max N}` | Param | Numeric range constraints |
|
|
@@ -411,7 +374,7 @@ Tags are JSDoc annotations that control how Photon processes your code. Here are
|
|
|
411
374
|
| `@mcp` | Class | Inject another MCP server as a dependency |
|
|
412
375
|
| `@icon` | Class/Method | Set emoji icon |
|
|
413
376
|
|
|
414
|
-
>
|
|
377
|
+
> See the full [Tag Reference](./docs/reference/DOCBLOCK-TAGS.md) for all 30+ tags with examples.
|
|
415
378
|
|
|
416
379
|
---
|
|
417
380
|
|
|
@@ -420,7 +383,7 @@ Tags are JSDoc annotations that control how Photon processes your code. Here are
|
|
|
420
383
|
**Start here:**
|
|
421
384
|
|
|
422
385
|
| Guide | |
|
|
423
|
-
|
|
386
|
+
|---|---|
|
|
424
387
|
| [Getting Started](./docs/GUIDE.md) | Create your first photon, step by step |
|
|
425
388
|
| [Tag Reference](./docs/reference/DOCBLOCK-TAGS.md) | Complete JSDoc tag reference with examples |
|
|
426
389
|
| [Naming Conventions](./docs/guides/NAMING-CONVENTIONS.md) | How to name methods so they read naturally as CLI commands |
|
|
@@ -429,9 +392,9 @@ Tags are JSDoc annotations that control how Photon processes your code. Here are
|
|
|
429
392
|
**Build more:**
|
|
430
393
|
|
|
431
394
|
| Topic | |
|
|
432
|
-
|
|
395
|
+
|---|---|
|
|
433
396
|
| [Custom UI](./docs/guides/CUSTOM-UI.md) | Build rich interactive interfaces with `window.photon` |
|
|
434
|
-
| [OAuth](./docs/guides/AUTH.md) | Built-in OAuth 2.
|
|
397
|
+
| [OAuth](./docs/guides/AUTH.md) | Built-in OAuth 2.0 with Google, GitHub, Microsoft |
|
|
435
398
|
| [Daemon Pub/Sub](./docs/core/DAEMON-PUBSUB.md) | Real-time cross-process messaging |
|
|
436
399
|
| [Webhooks](./docs/reference/WEBHOOKS.md) | HTTP endpoints for external services |
|
|
437
400
|
| [Locks](./docs/reference/LOCKS.md) | Distributed locks for exclusive access |
|
|
@@ -441,7 +404,7 @@ Tags are JSDoc annotations that control how Photon processes your code. Here are
|
|
|
441
404
|
**Operate:**
|
|
442
405
|
|
|
443
406
|
| Topic | |
|
|
444
|
-
|
|
407
|
+
|---|---|
|
|
445
408
|
| [Security](./SECURITY.md) | Best practices and audit checklist |
|
|
446
409
|
| [Marketplace Publishing](./docs/guides/MARKETPLACE-PUBLISHING.md) | Create and share team marketplaces |
|
|
447
410
|
| [Best Practices](./docs/guides/BEST-PRACTICES.md) | Patterns for production photons |
|
|
@@ -453,18 +416,8 @@ Tags are JSDoc annotations that control how Photon processes your code. Here are
|
|
|
453
416
|
|
|
454
417
|
## Contributing
|
|
455
418
|
|
|
456
|
-
|
|
419
|
+
Open an issue or a PR. See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
457
420
|
|
|
458
421
|
## License
|
|
459
422
|
|
|
460
|
-
[MIT](./LICENSE).
|
|
461
|
-
|
|
462
|
-
---
|
|
463
|
-
|
|
464
|
-
<div align="center">
|
|
465
|
-
|
|
466
|
-
*Singular focus. Precise target.*
|
|
467
|
-
|
|
468
|
-
Made by [Portel](https://github.com/portel-dev)
|
|
469
|
-
|
|
470
|
-
</div>
|
|
423
|
+
[MIT](./LICENSE).
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DedupMap — Map with deduplication for concurrent async creation.
|
|
3
|
+
*
|
|
4
|
+
* When multiple callers request the same key concurrently, only one
|
|
5
|
+
* factory call runs — all callers share the same inflight promise.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const map = new DedupMap<string, Connection>();
|
|
9
|
+
* const conn = await map.getOrCreate('redis', () => connect('redis://...'));
|
|
10
|
+
*/
|
|
11
|
+
export declare class DedupMap<K, V> {
|
|
12
|
+
private _values;
|
|
13
|
+
private _inflight;
|
|
14
|
+
/** Number of resolved entries. */
|
|
15
|
+
get size(): number;
|
|
16
|
+
/** Check if a resolved value exists for key. */
|
|
17
|
+
has(key: K): boolean;
|
|
18
|
+
/** Get a resolved value (undefined if not yet created). */
|
|
19
|
+
get(key: K): V | undefined;
|
|
20
|
+
/** Set a value directly (bypasses factory). */
|
|
21
|
+
set(key: K, value: V): void;
|
|
22
|
+
/**
|
|
23
|
+
* Get existing value or create one. Concurrent calls for the same
|
|
24
|
+
* key join the same inflight promise instead of spawning duplicates.
|
|
25
|
+
*/
|
|
26
|
+
getOrCreate(key: K, factory: () => Promise<V>): Promise<V>;
|
|
27
|
+
/** Delete a key (cancels inflight if pending). */
|
|
28
|
+
delete(key: K): boolean;
|
|
29
|
+
/** Clear all entries and inflight promises. */
|
|
30
|
+
clear(): void;
|
|
31
|
+
/** Iterate over resolved entries. Returns a snapshot (safe across await). */
|
|
32
|
+
entries(): [K, V][];
|
|
33
|
+
/** Iterate over resolved keys. Returns a snapshot. */
|
|
34
|
+
keys(): K[];
|
|
35
|
+
/** Iterate over resolved values. Returns a snapshot. */
|
|
36
|
+
values(): V[];
|
|
37
|
+
/** Iterate resolved entries via for...of. Returns snapshot array. */
|
|
38
|
+
[Symbol.iterator](): IterableIterator<[K, V]>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=dedup-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup-map.d.ts","sourceRoot":"","sources":["../../src/async/dedup-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,qBAAa,QAAQ,CAAC,CAAC,EAAE,CAAC;IACxB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,SAAS,CAA4B;IAE7C,kCAAkC;IAClC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,gDAAgD;IAChD,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIpB,2DAA2D;IAC3D,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAI1B,+CAA+C;IAC/C,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAK3B;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAuBhE,kDAAkD;IAClD,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAKvB,+CAA+C;IAC/C,KAAK,IAAI,IAAI;IAKb,6EAA6E;IAC7E,OAAO,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;IAInB,sDAAsD;IACtD,IAAI,IAAI,CAAC,EAAE;IAIX,wDAAwD;IACxD,MAAM,IAAI,CAAC,EAAE;IAIb,qEAAqE;IACrE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAG9C"}
|