@nebulr-group/bridge-cli 0.6.5 → 1.0.0-alpha.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 (293) hide show
  1. package/README.md +93 -460
  2. package/dist/api/accounts.d.ts +69 -0
  3. package/dist/api/accounts.d.ts.map +1 -0
  4. package/dist/api/accounts.js +42 -0
  5. package/dist/api/accounts.js.map +1 -0
  6. package/dist/api/apps.d.ts +50 -0
  7. package/dist/api/apps.d.ts.map +1 -0
  8. package/dist/api/apps.js +62 -0
  9. package/dist/api/apps.js.map +1 -0
  10. package/dist/api/billing.d.ts +131 -0
  11. package/dist/api/billing.d.ts.map +1 -0
  12. package/dist/api/billing.js +42 -0
  13. package/dist/api/billing.js.map +1 -0
  14. package/dist/api/flags.d.ts +73 -0
  15. package/dist/api/flags.d.ts.map +1 -0
  16. package/dist/api/flags.js +108 -0
  17. package/dist/api/flags.js.map +1 -0
  18. package/dist/api/keys.d.ts +48 -0
  19. package/dist/api/keys.d.ts.map +1 -0
  20. package/dist/api/keys.js +87 -0
  21. package/dist/api/keys.js.map +1 -0
  22. package/dist/auth/browser-open.d.ts +21 -4
  23. package/dist/auth/browser-open.d.ts.map +1 -1
  24. package/dist/auth/browser-open.js +45 -65
  25. package/dist/auth/browser-open.js.map +1 -1
  26. package/dist/auth/lock.d.ts +8 -0
  27. package/dist/auth/lock.d.ts.map +1 -0
  28. package/dist/auth/lock.js +43 -0
  29. package/dist/auth/lock.js.map +1 -0
  30. package/dist/auth/loopback.d.ts +8 -12
  31. package/dist/auth/loopback.d.ts.map +1 -1
  32. package/dist/auth/loopback.js +40 -45
  33. package/dist/auth/loopback.js.map +1 -1
  34. package/dist/auth/session.d.ts +61 -0
  35. package/dist/auth/session.d.ts.map +1 -0
  36. package/dist/auth/session.js +142 -0
  37. package/dist/auth/session.js.map +1 -0
  38. package/dist/auth/sign-in.d.ts +67 -0
  39. package/dist/auth/sign-in.d.ts.map +1 -0
  40. package/dist/auth/sign-in.js +113 -0
  41. package/dist/auth/sign-in.js.map +1 -0
  42. package/dist/auth/store.d.ts +89 -0
  43. package/dist/auth/store.d.ts.map +1 -0
  44. package/dist/auth/store.js +214 -0
  45. package/dist/auth/store.js.map +1 -0
  46. package/dist/bin.js +0 -2
  47. package/dist/bin.js.map +1 -1
  48. package/dist/cli.d.ts +1 -2
  49. package/dist/cli.d.ts.map +1 -1
  50. package/dist/cli.js +13 -17
  51. package/dist/cli.js.map +1 -1
  52. package/dist/client.d.ts +31 -0
  53. package/dist/client.d.ts.map +1 -0
  54. package/dist/client.js +67 -0
  55. package/dist/client.js.map +1 -0
  56. package/dist/commands/accounts.command.d.ts +4 -0
  57. package/dist/commands/accounts.command.d.ts.map +1 -0
  58. package/dist/commands/accounts.command.js +116 -0
  59. package/dist/commands/accounts.command.js.map +1 -0
  60. package/dist/commands/app.command.d.ts +3 -2
  61. package/dist/commands/app.command.d.ts.map +1 -1
  62. package/dist/commands/app.command.js +132 -233
  63. package/dist/commands/app.command.js.map +1 -1
  64. package/dist/commands/auth.command.d.ts +2 -12
  65. package/dist/commands/auth.command.d.ts.map +1 -1
  66. package/dist/commands/auth.command.js +147 -20
  67. package/dist/commands/auth.command.js.map +1 -1
  68. package/dist/commands/billing.command.d.ts +4 -0
  69. package/dist/commands/billing.command.d.ts.map +1 -0
  70. package/dist/commands/billing.command.js +185 -0
  71. package/dist/commands/billing.command.js.map +1 -0
  72. package/dist/commands/check.command.d.ts +2 -1
  73. package/dist/commands/check.command.d.ts.map +1 -1
  74. package/dist/commands/check.command.js +11 -14
  75. package/dist/commands/check.command.js.map +1 -1
  76. package/dist/commands/context.d.ts +58 -0
  77. package/dist/commands/context.d.ts.map +1 -0
  78. package/dist/commands/context.js +78 -0
  79. package/dist/commands/context.js.map +1 -0
  80. package/dist/commands/flag.command.d.ts +3 -62
  81. package/dist/commands/flag.command.d.ts.map +1 -1
  82. package/dist/commands/flag.command.js +181 -864
  83. package/dist/commands/flag.command.js.map +1 -1
  84. package/dist/commands/key.command.d.ts +4 -0
  85. package/dist/commands/key.command.d.ts.map +1 -0
  86. package/dist/commands/key.command.js +44 -0
  87. package/dist/commands/key.command.js.map +1 -0
  88. package/dist/commands/plan.command.d.ts +3 -203
  89. package/dist/commands/plan.command.d.ts.map +1 -1
  90. package/dist/commands/plan.command.js +200 -759
  91. package/dist/commands/plan.command.js.map +1 -1
  92. package/dist/config.d.ts +19 -101
  93. package/dist/config.d.ts.map +1 -1
  94. package/dist/config.js +51 -364
  95. package/dist/config.js.map +1 -1
  96. package/dist/core/discovery.d.ts +27 -0
  97. package/dist/core/discovery.d.ts.map +1 -0
  98. package/dist/core/discovery.js +97 -0
  99. package/dist/core/discovery.js.map +1 -0
  100. package/dist/core/errors.d.ts +26 -0
  101. package/dist/core/errors.d.ts.map +1 -0
  102. package/dist/core/errors.js +33 -0
  103. package/dist/core/errors.js.map +1 -0
  104. package/dist/core/http.d.ts +23 -0
  105. package/dist/core/http.d.ts.map +1 -0
  106. package/dist/core/http.js +153 -0
  107. package/dist/core/http.js.map +1 -0
  108. package/dist/core/private-files.d.ts +6 -0
  109. package/dist/core/private-files.d.ts.map +1 -0
  110. package/dist/core/private-files.js +96 -0
  111. package/dist/core/private-files.js.map +1 -0
  112. package/dist/core/routes.d.ts +94 -0
  113. package/dist/core/routes.d.ts.map +1 -0
  114. package/dist/core/routes.js +91 -0
  115. package/dist/core/routes.js.map +1 -0
  116. package/dist/core/urls.d.ts +15 -0
  117. package/dist/core/urls.d.ts.map +1 -0
  118. package/dist/core/urls.js +55 -0
  119. package/dist/core/urls.js.map +1 -0
  120. package/dist/gate-rules.d.ts +1 -2
  121. package/dist/gate-rules.d.ts.map +1 -1
  122. package/dist/gate-rules.js +4 -5
  123. package/dist/gate-rules.js.map +1 -1
  124. package/dist/index.d.ts +23 -3
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +18 -3
  127. package/dist/index.js.map +1 -1
  128. package/dist/io.d.ts +21 -0
  129. package/dist/io.d.ts.map +1 -0
  130. package/dist/io.js +59 -0
  131. package/dist/io.js.map +1 -0
  132. package/dist/money.d.ts +42 -0
  133. package/dist/money.d.ts.map +1 -0
  134. package/dist/money.js +112 -0
  135. package/dist/money.js.map +1 -0
  136. package/dist/program.d.ts +5 -24
  137. package/dist/program.d.ts.map +1 -1
  138. package/dist/program.js +25 -113
  139. package/dist/program.js.map +1 -1
  140. package/package.json +7 -8
  141. package/dist/auth/api-client.d.ts +0 -50
  142. package/dist/auth/api-client.d.ts.map +0 -1
  143. package/dist/auth/api-client.js +0 -109
  144. package/dist/auth/api-client.js.map +0 -1
  145. package/dist/auth/relative-time.d.ts +0 -2
  146. package/dist/auth/relative-time.d.ts.map +0 -1
  147. package/dist/auth/relative-time.js +0 -27
  148. package/dist/auth/relative-time.js.map +0 -1
  149. package/dist/auth/scopes.d.ts +0 -30
  150. package/dist/auth/scopes.d.ts.map +0 -1
  151. package/dist/auth/scopes.js +0 -39
  152. package/dist/auth/scopes.js.map +0 -1
  153. package/dist/auth/workspace-client.d.ts +0 -70
  154. package/dist/auth/workspace-client.d.ts.map +0 -1
  155. package/dist/auth/workspace-client.js +0 -105
  156. package/dist/auth/workspace-client.js.map +0 -1
  157. package/dist/commands/auth/config.command.d.ts +0 -8
  158. package/dist/commands/auth/config.command.d.ts.map +0 -1
  159. package/dist/commands/auth/config.command.js +0 -60
  160. package/dist/commands/auth/config.command.js.map +0 -1
  161. package/dist/commands/auth/login.command.d.ts +0 -46
  162. package/dist/commands/auth/login.command.d.ts.map +0 -1
  163. package/dist/commands/auth/login.command.js +0 -166
  164. package/dist/commands/auth/login.command.js.map +0 -1
  165. package/dist/commands/auth/logout.command.d.ts +0 -21
  166. package/dist/commands/auth/logout.command.d.ts.map +0 -1
  167. package/dist/commands/auth/logout.command.js +0 -97
  168. package/dist/commands/auth/logout.command.js.map +0 -1
  169. package/dist/commands/auth/methods.command.d.ts +0 -68
  170. package/dist/commands/auth/methods.command.d.ts.map +0 -1
  171. package/dist/commands/auth/methods.command.js +0 -91
  172. package/dist/commands/auth/methods.command.js.map +0 -1
  173. package/dist/commands/auth/status.command.d.ts +0 -18
  174. package/dist/commands/auth/status.command.d.ts.map +0 -1
  175. package/dist/commands/auth/status.command.js +0 -104
  176. package/dist/commands/auth/status.command.js.map +0 -1
  177. package/dist/commands/auth/use.command.d.ts +0 -17
  178. package/dist/commands/auth/use.command.d.ts.map +0 -1
  179. package/dist/commands/auth/use.command.js +0 -47
  180. package/dist/commands/auth/use.command.js.map +0 -1
  181. package/dist/commands/branding.command.d.ts +0 -3
  182. package/dist/commands/branding.command.d.ts.map +0 -1
  183. package/dist/commands/branding.command.js +0 -42
  184. package/dist/commands/branding.command.js.map +0 -1
  185. package/dist/commands/event.command.d.ts +0 -3
  186. package/dist/commands/event.command.d.ts.map +0 -1
  187. package/dist/commands/event.command.js +0 -27
  188. package/dist/commands/event.command.js.map +0 -1
  189. package/dist/commands/flag-init.command.d.ts +0 -13
  190. package/dist/commands/flag-init.command.d.ts.map +0 -1
  191. package/dist/commands/flag-init.command.js +0 -463
  192. package/dist/commands/flag-init.command.js.map +0 -1
  193. package/dist/commands/guide.command.d.ts +0 -61
  194. package/dist/commands/guide.command.d.ts.map +0 -1
  195. package/dist/commands/guide.command.js +0 -655
  196. package/dist/commands/guide.command.js.map +0 -1
  197. package/dist/commands/info.command.d.ts +0 -3
  198. package/dist/commands/info.command.d.ts.map +0 -1
  199. package/dist/commands/info.command.js +0 -70
  200. package/dist/commands/info.command.js.map +0 -1
  201. package/dist/commands/integration.command.d.ts +0 -35
  202. package/dist/commands/integration.command.d.ts.map +0 -1
  203. package/dist/commands/integration.command.js +0 -290
  204. package/dist/commands/integration.command.js.map +0 -1
  205. package/dist/commands/journeys.d.ts +0 -43
  206. package/dist/commands/journeys.d.ts.map +0 -1
  207. package/dist/commands/journeys.js +0 -221
  208. package/dist/commands/journeys.js.map +0 -1
  209. package/dist/commands/ops.command.d.ts +0 -17
  210. package/dist/commands/ops.command.d.ts.map +0 -1
  211. package/dist/commands/ops.command.js +0 -129
  212. package/dist/commands/ops.command.js.map +0 -1
  213. package/dist/commands/privilege.command.d.ts +0 -16
  214. package/dist/commands/privilege.command.d.ts.map +0 -1
  215. package/dist/commands/privilege.command.js +0 -86
  216. package/dist/commands/privilege.command.js.map +0 -1
  217. package/dist/commands/role.command.d.ts +0 -3
  218. package/dist/commands/role.command.d.ts.map +0 -1
  219. package/dist/commands/role.command.js +0 -101
  220. package/dist/commands/role.command.js.map +0 -1
  221. package/dist/commands/runtime-dir.d.ts +0 -16
  222. package/dist/commands/runtime-dir.d.ts.map +0 -1
  223. package/dist/commands/runtime-dir.js +0 -18
  224. package/dist/commands/runtime-dir.js.map +0 -1
  225. package/dist/commands/setup.command.d.ts +0 -63
  226. package/dist/commands/setup.command.d.ts.map +0 -1
  227. package/dist/commands/setup.command.js +0 -148
  228. package/dist/commands/setup.command.js.map +0 -1
  229. package/dist/commands/stripe.command.d.ts +0 -29
  230. package/dist/commands/stripe.command.d.ts.map +0 -1
  231. package/dist/commands/stripe.command.js +0 -99
  232. package/dist/commands/stripe.command.js.map +0 -1
  233. package/dist/commands/tenant.command.d.ts +0 -3
  234. package/dist/commands/tenant.command.d.ts.map +0 -1
  235. package/dist/commands/tenant.command.js +0 -135
  236. package/dist/commands/tenant.command.js.map +0 -1
  237. package/dist/commands/token.command.d.ts +0 -3
  238. package/dist/commands/token.command.d.ts.map +0 -1
  239. package/dist/commands/token.command.js +0 -54
  240. package/dist/commands/token.command.js.map +0 -1
  241. package/dist/commands/user.command.d.ts +0 -3
  242. package/dist/commands/user.command.d.ts.map +0 -1
  243. package/dist/commands/user.command.js +0 -117
  244. package/dist/commands/user.command.js.map +0 -1
  245. package/dist/credentials.d.ts +0 -141
  246. package/dist/credentials.d.ts.map +0 -1
  247. package/dist/credentials.js +0 -348
  248. package/dist/credentials.js.map +0 -1
  249. package/dist/flag-coherence.d.ts +0 -42
  250. package/dist/flag-coherence.d.ts.map +0 -1
  251. package/dist/flag-coherence.js +0 -103
  252. package/dist/flag-coherence.js.map +0 -1
  253. package/dist/output.d.ts +0 -17
  254. package/dist/output.d.ts.map +0 -1
  255. package/dist/output.js +0 -107
  256. package/dist/output.js.map +0 -1
  257. package/dist/preview.d.ts +0 -9
  258. package/dist/preview.d.ts.map +0 -1
  259. package/dist/preview.js +0 -63
  260. package/dist/preview.js.map +0 -1
  261. package/dist/product-decisions.d.ts +0 -40
  262. package/dist/product-decisions.d.ts.map +0 -1
  263. package/dist/product-decisions.js +0 -52
  264. package/dist/product-decisions.js.map +0 -1
  265. package/dist/prompt-urls.d.ts +0 -31
  266. package/dist/prompt-urls.d.ts.map +0 -1
  267. package/dist/prompt-urls.js +0 -35
  268. package/dist/prompt-urls.js.map +0 -1
  269. package/dist/prompts/auth-master-integration-prompt.md +0 -255
  270. package/dist/prompts/billing/master.md +0 -332
  271. package/dist/prompts/decisions/feature-control.md +0 -131
  272. package/dist/prompts/decisions/going-live.md +0 -60
  273. package/dist/prompts/decisions/login.md +0 -71
  274. package/dist/prompts/decisions/look-and-feel.md +0 -68
  275. package/dist/prompts/decisions/payments.md +0 -97
  276. package/dist/prompts/decisions/roles.md +0 -78
  277. package/dist/prompts/decisions/teams.md +0 -61
  278. package/dist/prompts/fit-together.md +0 -97
  279. package/dist/prompts/flags/master.md +0 -220
  280. package/dist/prompts/guide-coverage.json +0 -17
  281. package/dist/prompts/integration-success.md +0 -106
  282. package/dist/prompts/mcp-tools.json +0 -90
  283. package/dist/prompts/mechanisms.md +0 -298
  284. package/dist/prompts/orientation.md +0 -42
  285. package/dist/prompts/product-decisions.json +0 -338
  286. package/dist/resolve.d.ts +0 -110
  287. package/dist/resolve.d.ts.map +0 -1
  288. package/dist/resolve.js +0 -260
  289. package/dist/resolve.js.map +0 -1
  290. package/dist/shape.d.ts +0 -25
  291. package/dist/shape.d.ts.map +0 -1
  292. package/dist/shape.js +0 -74
  293. package/dist/shape.js.map +0 -1
@@ -1,97 +0,0 @@
1
- # How roles, plans, limits and flags fit together
2
-
3
- Read this before any decision guide that touches who gets what. Every other guide builds on it. MCP: resource `bridge://guides/fit-together`. CLI: `bridge guide fit-together`.
4
-
5
- ## The one rule for app code
6
-
7
- **Every gate in app code is a flag.** A link, a page, a button, an endpoint: if some people get it and others do not, the code asks a flag, and the flag's rule says why: a privilege (`privileges contains "USER_WRITE"`), a plan feature (`bridge:billing.entitlement.analytics eq true`) or a rollout. App code never reads a role, a privilege list, the plan or a plan feature to decide what someone may see or do.
8
-
9
- Two things are not gates and are not flags: **numbers** are plan limits (`<QuotaGate>` in the browser, `@RequireQuota` on the backend), and **permission on one specific record** stays in app code ("only the author edits their post").
10
-
11
- Before calling the work done, run `npx @nebulr-group/bridge-cli check gates` in the project. It lists every direct role, privilege, plan or plan-feature check with the flag to use instead. Fix each one.
12
-
13
- - **SvelteKit:** a piece of UI or a nav link is `<FeatureFlag key="…">`; a page is a route rule `{ match: '/admin/*', featureFlag: 'admin-area', redirectTo: '/' }`.
14
- - **NestJS:** an endpoint is `@RequireFeatureFlag('…')` on the handler.
15
-
16
- ## Why a flag
17
-
18
- Features should be controlled with feature flags. A flag can switch a route, an API endpoint, a feature or a single piece of code; its rules change without a release and update live when someone's role or plan changes. If a plan sells the feature, include it on the plan as well. This is valuable because the plan is where the customer sees what they are buying: the pricing table, the upgrade dialog and the "not on your plan" reason all read the plan's list, and the flag's rule points at that same list, so changing what Pro includes is one edit in one place.
19
-
20
- ## One question, one tool
21
-
22
- | The question | The tool | How |
23
- |---|---|---|
24
- | Is this route, API endpoint, feature or piece of code on for them? | a **flag** | The code asks for the flag by key; the flag's rule decides |
25
- | Does their plan sell it? | the plan's **features list**, read by the flag's rule | `bridge plan feature add pro analytics`, then the rule `bridge:billing.entitlement.analytics eq true` |
26
- | How many do they get (projects, exports, seats)? | a **plan limit** | `bridge plan quota set pro --metric exports --limit 100 --policy hard` |
27
- | How many people fit in a workspace? | a **plan limit** counted from membership (seats) | `bridge plan quota set pro --metric seats --limit 10 --policy hard --kind gauge --source membership` |
28
- | Who is this person, and may they do this kind of thing? | a flag rule on a **privilege** (preferred) or a **role** | `privileges contains "REPORTS_VIEW"`, or `user.role eq "ADMIN"` |
29
- | May this person change this one record? | the app's own code | "Only the author edits their post" |
30
-
31
- The flag's rule never names plans. It points at `bridge:billing.entitlement.<feature>`, so when Pro gains or loses a feature, the plan changes and no rule has to follow.
32
-
33
- ## Where a flag is not the tool
34
-
35
- - **Numbers are plan limits.** "100 exports a month" or "10 projects" is a quota on the plan, not a flag. Bridge refuses at the limit and the upgrade dialog explains it.
36
- - **Seats are a plan limit too, counted from membership.** Bridge counts the workspace's active members; the check sits where invites happen (the team page's seat-limit setting, or `@RequireQuota('seats')` on the app's own invite handler), never in a flag or an entitlement.
37
- - **Permission on one specific record stays in app code.** Whether this person may edit *this* invoice depends on data only the app has (who wrote it, which team owns it). The flag decides whether invoice editing exists for them at all; the app decides the record.
38
-
39
- ## Access by who someone is
40
-
41
- Control it with a flag rule, never with a role check written into the app's code.
42
-
43
- - **Prefer a privilege rule** (`privileges contains "REPORTS_VIEW"`) over a role rule (`user.role eq "ADMIN"`). A privilege rule keeps working when roles are renamed or reshuffled. `contains` on `privileges` means the person holds exactly that key: `REPORTS_VIEW` does not match `REPORTS_VIEW_ALL`.
44
- - **A role rule is fine when the developer means the role itself.** Use the role key exactly as `list_roles` shows it; `ADMIN` and `admin` are different.
45
- - **An admin-only page is a flag route rule, not a check on the page.** In SvelteKit: `{ match: '/admin/*', featureFlag: 'admin-area', redirectTo: '/' }`, with the flag `admin-area` ruled on a privilege. On the NestJS backend: `@RequireFeatureFlag('admin-area')` on the handler. Both read the same rule.
46
- - **The team page is the same.** The team link and the team route are gated by a flag ruled on the privilege that manages people (for example `privileges contains "USER_WRITE"`, after `bridge role list`), never by a list of role names.
47
- - **Roles are the app's own.** What a role can do is only ever "in the default setup". Read the app's real roles and privileges with `list_roles` / `bridge role list` before writing any rule, and never assume what a role grants from its name. In the default setup a new app has Owner, Admin and Member, and Member is the default for everyone after the first person in a workspace.
48
-
49
- ## The backend sees the same rule
50
-
51
- A flag rule on a role, a privilege, the plan or a plan feature gives the same answer in the NestJS backend as in the browser, with no wiring: the plugin fills those in from the verified sign-in and from Bridge's own billing records. Nothing the client sends is used for them. Plan-limit numbers (`bridge:billing.quota.*`) are the one thing a server-side rule cannot see; limits are enforced by the plan-limit decorator anyway.
52
-
53
- ## Limits: count once, where the action happens
54
-
55
- Ask the developer first: **"Does this action call your server?"** Then:
56
-
57
- - **Yes, it calls the backend:** the backend counts it. One check on the handler (`@RequireQuota('exports')` in NestJS, `bridge.requireQuota('exports')` in Express) refuses at the limit with `402` and records the use after a successful request. The frontend only shows the count and the upgrade dialog; it does not report the same metric.
58
- - **No, it happens in the browser** (local-first, data on the device, no server of the developer's own): the browser counts it. `bridge.usage.report('exports')` for something that happened, `bridge.usage.set('projects', n)` for how many exist now, and `<QuotaGate metric="exports">` around the button so it stops at the limit. This is a complete, first-class way to run limits.
59
-
60
- Never count one metric in both places: it is counted twice. Never describe browser counting as a lesser or temporary option; ask the question above and follow the answer.
61
-
62
- Counter or gauge: if deleting it frees room, it's a gauge and your app counts it; if it happened, it's a counter and Bridge counts it.
63
-
64
- ## What people see when something is off
65
-
66
- A flag that is off says why, and the app shows each reason differently:
67
-
68
- | Why it is off | In the browser | From the backend |
69
- |---|---|---|
70
- | Not on their plan | The upgrade dialog, naming the plans that include it | `402 FEATURE_NOT_IN_PLAN` |
71
- | Their role or privileges | Hidden, or a line telling them to ask an admin | `403 FEATURE_NOT_PERMITTED` |
72
- | Switched off | Hidden | `403 FEATURE_OFF` |
73
-
74
- No upgrade dialog opens by itself. It opens when someone opens a gated route, clicks something gated (SvelteKit: `<FeatureFlag key="analytics" upgrade>` renders an "Upgrade to use this" button), or when the backend refuses with a `402`. A page that only renders a hidden feature opens nothing.
75
-
76
- ## Worked example
77
-
78
- "Analytics on Pro. Only people who manage the workspace see settings. 100 exports a month on Pro."
79
-
80
- 1. `bridge plan feature add pro analytics --name "Analytics"`, and a flag `analytics` with the rule `bridge:billing.entitlement.analytics eq true`.
81
- 2. `bridge role list` first. Then a flag `workspace-settings` with a privilege rule, e.g. `privileges contains "TENANT_WRITE"` if that is the privilege the app's roles use for it, and a route rule on `/settings/*`.
82
- 3. Ask whether exporting calls the developer's server. If it does: `bridge plan quota set pro --metric exports --limit 100 --policy hard` and `@RequireQuota('exports')` on the export handler. If it does not: the same quota, `bridge.usage.report('exports')` in the browser and `<QuotaGate metric="exports">` around the button.
83
- 4. `npx @nebulr-group/bridge-cli check gates`: nothing left that reads a role, a privilege, the plan or a plan feature directly.
84
-
85
- ## Exceptions
86
-
87
- **Checking a plan feature without a flag is the exception.** `@RequireEntitlement('analytics')` on the backend and `<Entitled to="analytics">` or `$entitlements.can('analytics')` in the UI read the plan's list directly. They are for the rare case where the developer explicitly asks for no flag, and they print a one-time note in development. Mark each such line `// bridge-gate-exception: <reason>` so `check gates` leaves it. Use a flag by default: it can also carry a rollout, a trial group or a one-customer exception later, without a code change.
88
-
89
- ## Where each piece is decided
90
-
91
- | Area | Decision guide |
92
- |---|---|
93
- | Flags and their rules | `bridge guide decision feature-control` |
94
- | Roles and privileges | `bridge guide decision roles` |
95
- | Plans, features and limits | `bridge guide decision payments` |
96
- | Who is in a workspace | `bridge guide decision teams` |
97
- | How limits, the upgrade dialog and page customisation work | `bridge guide mechanisms` |
@@ -1,220 +0,0 @@
1
- # Bridge Feature Flags — Master Integration Prompt
2
-
3
- You are integrating **Bridge Feature Flags** into a user's application — the flags-only path. No auth scaffolding, no billing components. If the user also wants auth or billing, run `bridge guide` (auth master) or `bridge guide billing` afterwards.
4
-
5
- > **The one rule for app code: every gate is a flag.** A link, a page, a button, an endpoint: if some people get it and others do not, the code asks a flag, and the flag's rule says why (a privilege, a plan feature, a rollout). App code never reads a role, a privilege list, the plan or a plan feature to decide what someone may see or do. Numbers are plan limits, and permission on one specific record stays in app code. `bridge guide fit-together` has the whole rule; the last verification step runs `npx @nebulr-group/bridge-cli check gates`.
6
-
7
- Flags are the standard way to control a route, an API endpoint, a feature or a piece of code. A feature a plan sells is listed on the plan and the flag's rule points at `bridge:billing.entitlement.<feature>`, never at plan names; access by who someone is is a rule on a privilege (preferred) or a role, never a role check in code. `bridge guide fit-together` has the whole rule; `bridge guide decision feature-control` has the questions to ask.
8
-
9
- This master prompt orchestrates discovery, confirmation, and verification. The **actual install commands, provider shapes, and runnable code snippets live in the per-framework prompts** that ship in each plugin repo's `mcp/` folder. This master does the planning; the per-framework prompts do the wiring.
10
-
11
- ## Where per-framework prompts live
12
-
13
- Each plugin repo owns its own framework-specific prompt under `mcp/feature-flags-prompt.md`. The CLI fetches them on demand:
14
-
15
- | Framework | Plugin repo | Fetched by |
16
- |-----------|-------------|------------|
17
- | SvelteKit | `bridge-svelte/mcp/feature-flags-prompt.md` | `bridge guide flags --framework svelte` |
18
- | React | `bridge-react/mcp/feature-flags-prompt.md` | `bridge guide flags --framework react` |
19
- | Next.js | `bridge-nextjs/mcp/feature-flags-prompt.md` | `bridge guide flags --framework nextjs` |
20
- | Angular | `bridge-angular/mcp/feature-flags-prompt.md` | `bridge guide flags --framework angular` |
21
- | NestJS | `bridge-nestjs/mcp/feature-flags-prompt.md` | `bridge guide flags --framework nestjs` |
22
- | Express | `bridge-express/mcp/feature-flags-prompt.md` | `bridge guide flags --framework express` |
23
-
24
- `bridge guide <framework> flags` is an equivalent route — both call the same plugin-repo prompt.
25
-
26
- If the CLI returns `HTTP 404 — file may not exist yet in the plugin repo`, the plugin hasn't published its flags guide yet. Tell the user that's the blocker and fall back to `@nebulr-group/bridge-auth-core` directly (it's framework-agnostic and stable). Do NOT improvise an inline integration in this master prompt — the plugin guide is the canonical handover.
27
-
28
- ## Step 0 — Authenticate
29
-
30
- Run `bridge auth login` and wait for it to print "Logged in as <email>".
31
-
32
- If the user is using flags **without Bridge Auth** (bring-your-own identity), they still need to be logged into the CLI to fetch app config and manage flags. Login is not the same as runtime identity.
33
-
34
- ---
35
-
36
- **At the end of this integration you will output a success message:**
37
-
38
- ```
39
- ██████╗ ██████╗ ██╗██████╗ ██████╗ ███████╗
40
- ██╔══██╗██╔══██╗██║██╔══██╗██╔════╝ ██╔════╝
41
- ██████╔╝██████╔╝██║██║ ██║██║ ███╗█████╗
42
- ██╔══██╗██╔══██╗██║██║ ██║██║ ██║██╔══╝
43
- ██████╔╝██║ ██║██║██████╔╝╚██████╔╝███████╗
44
- ╚═════╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚═════╝ ╚══════╝
45
-
46
- ──────────────────────────────────────────────
47
- Feature flags are live in [project-name].
48
-
49
- ✅ Flags read from the bulk-evaluate endpoint at boot
50
- ✅ Eval context wired: [identity-source]
51
- ✅ Telemetry batching enabled
52
- ✅ Realtime updates enabled (optional)
53
- ──────────────────────────────────────────────
54
- Here is what I actually did:
55
- [what-i-actually-did]
56
-
57
- And here is what I changed:
58
- [what-i-changed]
59
- ──────────────────────────────────────────────
60
- ```
61
-
62
- Track as you work:
63
- - **`[project-name]`** — folder name and/or `package.json` name for each project
64
- - **`[identity-source]`** — how `identity` flows in (Bridge Auth `user.id`, your own session, anonymous bucket ID, etc.)
65
- - **`[what-i-actually-did]`** — high-level outcomes
66
- - **`[what-i-changed]`** — every package installed, file modified, env var set
67
-
68
- ## Mental model (1-minute version)
69
-
70
- A flag has:
71
-
72
- | Field | Possible values |
73
- |---|---|
74
- | `state` | `off` · `on` · `on-with-rule` |
75
- | `valueType` | `boolean` · `string` · `number` · `json` |
76
- | `offValue` | typed per `valueType` (returned when state=off) |
77
- | `onValue` | typed per `valueType` (returned when state=on) |
78
- | `rule` | branches[] + otherwiseValue + rolloutPct (only when state=on-with-rule) |
79
-
80
- Branches are first-match-wins; conditions within a branch AND together; `rolloutPct` is rule-level (0-100) and requires `identity` on the eval context.
81
-
82
- Every plugin wraps the same evaluation, and the return type is inferred from the default value, so no casts. On SvelteKit it is `useFlag(key, defaultValue)` in script and `<FeatureFlag key defaultValue>` in markup, both from `@nebulr-group/bridge-svelte/flags`; a whole route is gated by a `featureFlag` rule in `bridgeBootstrap()`.
83
-
84
- ## Step 1 — Discover projects
85
-
86
- Scan the current directory and its immediate subdirectories for `package.json` files. For each one:
87
-
88
- 1. **Package manager** — detect from lock file:
89
- - `bun.lock` / `bun.lockb` → `bun`
90
- - `pnpm-lock.yaml` → `pnpm`
91
- - `yarn.lock` → `yarn`
92
- - `package-lock.json` or none → `npm`
93
-
94
- 2. **Framework** (from `dependencies` + `devDependencies`):
95
- - `svelte` or `@sveltejs/kit` → **SvelteKit**
96
- - `react` + `next` → **Next.js**
97
- - `react` (without next) → **React**
98
- - `@angular/core` → **Angular**
99
- - `@nestjs/core` → **NestJS** (backend)
100
- - `express` (without `@nestjs/core`) → **Express** (backend)
101
-
102
- 3. **Existing Bridge plugin?** Check for `@nebulr-group/bridge-<framework>` — if present, the project may already have auth or other Bridge surfaces. Flags can co-exist with auth in the same plugin; the per-framework guide will tell you whether its bootstrap is already in place. On SvelteKit, an app that already calls `bridgeBootstrap()` and renders `<BridgeBootstrap>` has flags running — there is nothing to install or initialise.
103
-
104
- 4. **Existing flag system?** Look for `launchdarkly-*`, `@unleash/*`, `posthog-js`, `growthbook-*` — warn the user that Bridge flags will run in parallel unless they migrate.
105
-
106
- 5. **Record** for each project: path, type (frontend/backend), framework, package manager, existing flag system (if any).
107
-
108
- ## Step 2 — Present findings and confirm
109
-
110
- ```
111
- I detected the following projects:
112
-
113
- 1. ./my-app-ui — SvelteKit 5 (frontend, bun)
114
- Existing flags: none
115
-
116
- 2. ./my-app-api — NestJS 11 (backend, bun)
117
- Existing flags: none
118
-
119
- Which projects should I wire flags into? (all / select by number)
120
- ```
121
-
122
- Wait for confirmation. If specific projects are selected, only integrate those.
123
-
124
- **Identity source (frontend + backend both).** Ask:
125
-
126
- ```
127
- How should flags identify users?
128
-
129
- - Bridge Auth (default if already integrated) — uses the authenticated user's id
130
- - Your own user model — you pass identity yourself per call / per request
131
- - Anonymous — every visitor gets a stable random bucket id (no targeting beyond rolloutPct)
132
- ```
133
-
134
- Note the answer — Step 4 wires the eval context based on it.
135
-
136
- ## Step 3 — Get Bridge app context
137
-
138
- Run:
139
-
140
- ```bash
141
- bridge app get
142
- bridge info flags
143
- ```
144
-
145
- Extract and note:
146
- - `appId` — needed by every project
147
- - Existing flags in the app (if any) — useful to show the user what they can already read
148
-
149
- If `bridge` CLI is not available or not configured, ask the user for the `appId` directly.
150
-
151
- ## Step 4 — Fetch the per-framework prompt and follow it
152
-
153
- For each confirmed project, fetch the framework-specific flags prompt and follow it verbatim:
154
-
155
- ```bash
156
- bridge guide flags --framework <name>
157
- ```
158
-
159
- Where `<name>` is one of: `svelte`, `react`, `nextjs`, `angular`, `nestjs`, `express`.
160
-
161
- | Framework | Command |
162
- |-----------|---------|
163
- | SvelteKit | `bridge guide flags --framework svelte` |
164
- | React | `bridge guide flags --framework react` |
165
- | Next.js | `bridge guide flags --framework nextjs` |
166
- | Angular | `bridge guide flags --framework angular` |
167
- | NestJS | `bridge guide flags --framework nestjs` |
168
- | Express | `bridge guide flags --framework express` |
169
-
170
- `bridge guide <framework> flags` is an equivalent route — both call the same plugin-repo prompt.
171
-
172
- **Every per-framework guide covers this contract:**
173
-
174
- - **Install** — the right package + version + import path (when the plugin is already installed for auth, nothing more to install)
175
- - **Bootstrap** — provider / module / factory setup where the framework needs one (SvelteKit with Bridge Auth needs none: `<BridgeBootstrap>` starts flags, reading the app id from `.env`)
176
- - **Eval context** — idiomatic placement for `identity` and `attributes`, including how to wire the `identitySource` chosen in Step 2
177
- - **Reading a flag** — the framework wrapper (`useFlag`, `FeatureFlag`, `signal`, decorator) and the synchronous `evaluateFlag` for non-reactive contexts
178
- - **Advanced attribute wiring** — the unified `bridge.attributes` write surface and realtime event subscription via `bridge.events.handle`
179
- - **Telemetry + realtime** — both on by default; how to disable
180
- - **Troubleshooting** — the most common first-time failures
181
-
182
- **Pass these values from this master to the per-framework prompt:**
183
- - `appId` — from Step 3
184
- - `baseUrl` — production by default; a stage, local or self-hosted app must set it (SvelteKit reads `VITE_BRIDGE_API_BASE_URL` itself)
185
- - `packageManager` — from Step 1
186
- - `mode` — `frontend` (browser projects) or `backend` (NestJS, Express, Next.js server)
187
- - `identitySource` — from Step 2
188
-
189
- **Order:** Frontend first, then backend.
190
-
191
- If the CLI returns `HTTP 404`, the plugin hasn't published its flags guide yet. Tell the user that's the blocker and fall back to `@nebulr-group/bridge-auth-core` directly — `new BridgeFlags({ appId, baseUrl, mode })` + `bridge.setContext({...})` + `bridge.flag(...)`. The SDK shape is identical across frameworks; only the bindings differ.
192
-
193
- ## Step 5 — Verify
194
-
195
- For each integrated project:
196
-
197
- 1. **Build check** — run the project's build command (from `package.json` scripts)
198
- 2. **Eval check** — read one flag (existing or freshly created) and confirm it returns the expected value
199
- 3. **Context check** — confirm `identity` is set when the project uses any rolled-out rule
200
- 4. **Gate check** — `npx @nebulr-group/bridge-cli check gates` in the project: every direct role, privilege, plan or plan-feature check it lists becomes a flag. Run it again until it is clean
201
-
202
- If anything fails, diagnose and fix before moving on.
203
-
204
- ## Step 6 — Tell the developer what they just got
205
-
206
- Output the success message at the top of this prompt, personalised for what you actually did. Same rules as the auth master:
207
-
208
- - Fill every `[placeholder]` with real values
209
- - Strip any agent-only notes
210
- - Group by project if both frontend and backend were integrated
211
- - **Do not** write any text before the banner — start with the `█` character
212
- - If a build is broken or flag reads return defaults forever, prepend a single "Heads up:" line before the banner
213
-
214
- After delivering the message, the flags integration is complete.
215
-
216
- ## Reference notes
217
-
218
- **Frontend vs backend:** The frontend SDK evaluates flags from a local cache hydrated at boot. The backend SDK (`mode: 'backend'`) evaluates per-request and requires an explicit `identity` for any rule using `rolloutPct < 100` — no anonymous bucketing.
219
-
220
- **Flags without Bridge Auth:** Flags work standalone. Pass your own `identity` and `attributes` when `identitySource` is "your own user model". If Bridge Auth is already installed, `user.role`, `privileges`, `tenant.plan` and the `bridge:billing.*` attributes (including `bridge:billing.entitlement.<feature>`) fill the eval context automatically, in the browser and on the NestJS backend alike.
@@ -1,17 +0,0 @@
1
- {
2
- "_comment": "Which per-framework guides exist, keyed by the feature names `bridge guide <framework> <feature>` takes (the file stems under mcp/ in each plugin repo). The one copy: `bridge guide list` reads it from the package, and the Bridge MCP server reads it from GitHub (bridge-cli/main/bridge-cli/prompts/guide-coverage.json). Update it when a plugin adds or removes an mcp/*-prompt.md. `topics` lists the framework-agnostic guides bundled here (`bridge guide <topic>`).",
3
- "aliases": { "flags": "feature-flags" },
4
- "topics": {
5
- "orientation": "What Bridge does: a short map to relay to the developer (bridge guide orientation)",
6
- "fit-together": "How roles, plans, limits and flags fit together: the rule every decision guide builds on (bridge guide fit-together)",
7
- "mechanisms": "How limits, the upgrade dialog and page customisation work (bridge guide mechanisms)"
8
- },
9
- "frameworks": {
10
- "svelte": ["integration", "sdk-auth", "feature-flags", "billing", "team", "branding"],
11
- "react": ["integration", "sdk-auth", "feature-flags", "billing", "team", "branding"],
12
- "angular": ["integration", "sdk-auth", "feature-flags", "billing", "team", "branding"],
13
- "nextjs": ["integration", "sdk-auth", "feature-flags", "billing", "team"],
14
- "nestjs": ["integration", "auth", "feature-flags", "billing", "team"],
15
- "express": ["integration", "auth", "feature-flags", "billing", "team"]
16
- }
17
- }
@@ -1,106 +0,0 @@
1
- ██████╗ ██████╗ ██╗██████╗ ██████╗ ███████╗
2
- ██╔══██╗██╔══██╗██║██╔══██╗██╔════╝ ██╔════╝
3
- ██████╔╝██████╔╝██║██║ ██║██║ ███╗█████╗
4
- ██╔══██╗██╔══██╗██║██║ ██║██║ ██║██╔══╝
5
- ██████╔╝██║ ██║██║██████╔╝╚██████╔╝███████╗
6
- ╚═════╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚═════╝ ╚══════╝
7
-
8
- ──────────────────────────────────────────────
9
- Congratulations — [project-name] now has superpowers.
10
-
11
- ✅ Authentication
12
- UI for users to signup and login. Enable more
13
- alternatives from the control center.
14
-
15
- ✅ Locked by default
16
- Every route protected. Unauth'd users
17
- bounce to login automatically.
18
-
19
- ✅ Multi-tenant ready
20
- Teams · Orgs · Workspaces · RBAC
21
- Tenant context flows through every request.
22
-
23
- ✅ You know who's calling
24
- [token-description]
25
- One token. Every auth alternative covered.
26
-
27
- ──────────────────────────────────────────────
28
- The control center gives you even more powers.
29
-
30
- › Billing Stripe plans & subscriptions → bridge guide [framework] billing
31
- › Feature flags Per-user, per-plan toggles → bridge guide flags
32
- › Team management Invites, roles, orgs → bridge guide [framework] team
33
-
34
- Run any of those when you're ready.
35
-
36
- Go build the thing that actually matters.
37
-
38
- ──────────────────────────────────────────────
39
- Here is what I actually did:
40
-
41
- [what-i-actually-did]
42
-
43
- And here is what I changed:
44
-
45
- [what-i-changed]
46
- ──────────────────────────────────────────────
47
-
48
- ---
49
- AGENT SUBSTITUTION GUIDE (strip this section before outputting)
50
-
51
- PLACEHOLDER: project-name
52
- Pick the most human-readable name available. Prefer in this order:
53
- 1. The folder name (e.g. "my-saas-app") — often more meaningful than the package name
54
- 2. The `name` field in package.json if it's descriptive
55
- If both frontend and backend were integrated, combine them:
56
- "my-saas-app + my-saas-api" or just the shared root folder name if obvious.
57
-
58
- PLACEHOLDER: framework
59
- In the "control center" next-steps lines, replace `[framework]` with the
60
- framework slug used by `bridge guide` (`svelte`, `react`, `nextjs`, `angular`,
61
- `nestjs`, `express`). Use the frontend framework when both frontend and
62
- backend were integrated. Leave `bridge guide flags` unchanged — that command
63
- auto-detects the framework from `package.json`.
64
-
65
- PLACEHOLDER: token-description
66
- One short line — the key identifiers the developer uses to access the
67
- authenticated user in their specific framework. No explanation.
68
- Examples:
69
- Svelte: profileStore · isAuthenticated · tokenStore
70
- React: useProfile() · useIsAuthenticated() · useToken()
71
- NestJS: @CurrentUser() on any endpoint
72
- Express: req.user on any protected route
73
- If both frontend and backend were integrated, combine on one line:
74
- profileStore (frontend) · @CurrentUser() (backend)
75
-
76
- PLACEHOLDER: what-i-actually-did
77
- 3–5 bullet lines drawn from what actually happened in this session.
78
- High-level outcomes in plain English — not file names.
79
- If both frontend and backend were integrated, group under headings:
80
-
81
- Frontend (my-saas-app):
82
- ✅ Auth UI live — signup, login, password reset
83
- ✅ All routes protected by default
84
-
85
- Backend (my-saas-api):
86
- ✅ All endpoints require a verified JWT
87
- ✅ Authenticated user available on every request
88
-
89
- PLACEHOLDER: what-i-changed
90
- The nitty-gritty. One line per actual change, drawn from what you did in this
91
- session. Real package names, real file paths, real URLs, real values. Omit
92
- anything that was skipped.
93
- If both frontend and backend were integrated, group under headings:
94
-
95
- Frontend (my-saas-app):
96
- ✅ @nebulr-group/bridge-svelte installed
97
- ✅ src/routes/+layout.ts — export const load = bridgeBootstrap({ rules })
98
- ✅ src/routes/+layout.svelte — app wrapped in <BridgeBootstrap>
99
- ✅ src/routes/auth/[...bridge]/+page.svelte — <BridgeAuthRoutes /> serves every sign-in page
100
- ✅ .env configured with VITE_BRIDGE_APP_ID=abc123
101
- ✅ Bridge app configured for http://localhost:5175
102
-
103
- Backend (my-saas-api):
104
- ✅ @nebulr-group/bridge-nestjs installed
105
- ✅ AppModule — BridgeModule.forRoot({ guard: { global: true } }), every route needs a signed-in user
106
- ✅ .env configured with BRIDGE_APP_ID=abc123
@@ -1,90 +0,0 @@
1
- {
2
- "_comment": [
3
- "The names of the Bridge MCP server's tools, and nothing else. The bundled guides are tested against this list: a guide that names a tool not in it fails src/__tests__/guide-decisions.test.ts.",
4
- "Source: the `bridgeTools` registry in bridge-api, microservices/account/nebulr-api/mcp/tools/register.ts.",
5
- "Generated, never edited by hand: in bridge-api run `bun scripts/generate-mcp-tools-json.ts > <bridge-cli>/bridge-cli/prompts/mcp-tools.json` (in the bridge-api container).",
6
- "The count must equal the number of entries in `bridgeTools` (79 at the source below). bridge-api's guide-currency.spec.ts fails when this file and the registry disagree."
7
- ],
8
- "source": "bridge-api feature/tbp-806-plan-delete 1c622b17",
9
- "tools": [
10
- "add_plan_feature",
11
- "add_redirect_uri",
12
- "apply_plan",
13
- "archive_plan",
14
- "clear_flag_schedule",
15
- "connect_stripe",
16
- "create_api_token",
17
- "create_app",
18
- "create_feature_flag",
19
- "create_plan",
20
- "create_privilege",
21
- "create_role",
22
- "create_tenant",
23
- "create_test_user",
24
- "debug_auth_issue",
25
- "delete_feature_flag",
26
- "delete_plan",
27
- "delete_privilege",
28
- "delete_role",
29
- "delete_tenant",
30
- "diagnose_integration",
31
- "evaluate_feature_flag",
32
- "export_feature_flags",
33
- "get_app",
34
- "get_auth_config",
35
- "get_branding",
36
- "get_environment_info",
37
- "get_flag",
38
- "get_integration_guide",
39
- "get_plan",
40
- "get_role",
41
- "get_server_info",
42
- "get_setup_status",
43
- "get_started",
44
- "get_stripe_status",
45
- "get_tenant",
46
- "get_user",
47
- "import_feature_flags",
48
- "invite_user",
49
- "list_api_tokens",
50
- "list_apps",
51
- "list_events",
52
- "list_feature_flags",
53
- "list_plan_quotas",
54
- "list_plans",
55
- "list_privileges",
56
- "list_redirect_uris",
57
- "list_roles",
58
- "list_tenants",
59
- "list_users",
60
- "remove_plan_feature",
61
- "remove_plan_price",
62
- "remove_plan_quota",
63
- "remove_redirect_uri",
64
- "remove_user",
65
- "revoke_api_token",
66
- "scaffold_flags_integration",
67
- "set_default_role",
68
- "set_flag_schedule",
69
- "set_plan_features",
70
- "set_plan_price",
71
- "set_plan_quota",
72
- "set_tenant_plan",
73
- "setup_communication",
74
- "setup_payments",
75
- "setup_sso",
76
- "toggle_feature_flag",
77
- "update_app",
78
- "update_auth_methods",
79
- "update_branding",
80
- "update_feature_flag",
81
- "update_password_policy",
82
- "update_plan",
83
- "update_privilege",
84
- "update_role",
85
- "update_tenant",
86
- "update_user",
87
- "use_app",
88
- "verify_login"
89
- ]
90
- }