@adonis-agora/flags 0.0.0-stage → 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 (322) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +15 -2
  4. package/dist/assets/spa/assets/index-DdmFo7xr.js +12 -0
  5. package/dist/assets/spa/assets/index-DdmFo7xr.js.map +1 -0
  6. package/dist/assets/spa/assets/index-DhkcP3Cw.css +2 -0
  7. package/dist/assets/spa/index.html +26 -0
  8. package/dist/commands/flags_list.d.ts +9 -0
  9. package/dist/commands/flags_list.d.ts.map +1 -0
  10. package/dist/commands/flags_list.js +51 -0
  11. package/dist/commands/flags_list.js.map +1 -0
  12. package/dist/commands/flags_ops.d.ts +60 -0
  13. package/dist/commands/flags_ops.d.ts.map +1 -0
  14. package/dist/commands/flags_ops.js +253 -0
  15. package/dist/commands/flags_ops.js.map +1 -0
  16. package/dist/commands/flags_refs.d.ts +15 -0
  17. package/dist/commands/flags_refs.d.ts.map +1 -0
  18. package/dist/commands/flags_refs.js +90 -0
  19. package/dist/commands/flags_refs.js.map +1 -0
  20. package/dist/commands/flags_set.d.ts +19 -0
  21. package/dist/commands/flags_set.d.ts.map +1 -0
  22. package/dist/commands/flags_set.js +97 -0
  23. package/dist/commands/flags_set.js.map +1 -0
  24. package/dist/commands/flags_types.d.ts +18 -0
  25. package/dist/commands/flags_types.d.ts.map +1 -0
  26. package/dist/commands/flags_types.js +94 -0
  27. package/dist/commands/flags_types.js.map +1 -0
  28. package/dist/commands/flags_work.d.ts +16 -0
  29. package/dist/commands/flags_work.d.ts.map +1 -0
  30. package/dist/commands/flags_work.js +71 -0
  31. package/dist/commands/flags_work.js.map +1 -0
  32. package/dist/commands/main.d.ts +15 -0
  33. package/dist/commands/main.d.ts.map +1 -0
  34. package/dist/commands/main.js +30 -0
  35. package/dist/commands/main.js.map +1 -0
  36. package/dist/commands/shared.d.ts +21 -0
  37. package/dist/commands/shared.d.ts.map +1 -0
  38. package/dist/commands/shared.js +70 -0
  39. package/dist/commands/shared.js.map +1 -0
  40. package/dist/configure.d.ts +11 -0
  41. package/dist/configure.d.ts.map +1 -0
  42. package/dist/configure.js +32 -0
  43. package/dist/configure.js.map +1 -0
  44. package/dist/providers/dashboard_provider.d.ts +65 -0
  45. package/dist/providers/dashboard_provider.d.ts.map +1 -0
  46. package/dist/providers/dashboard_provider.js +340 -0
  47. package/dist/providers/dashboard_provider.js.map +1 -0
  48. package/dist/providers/flags_provider.d.ts +30 -0
  49. package/dist/providers/flags_provider.d.ts.map +1 -0
  50. package/dist/providers/flags_provider.js +184 -0
  51. package/dist/providers/flags_provider.js.map +1 -0
  52. package/dist/services/main.d.ts +17 -0
  53. package/dist/services/main.d.ts.map +1 -0
  54. package/dist/services/main.js +20 -0
  55. package/dist/services/main.js.map +1 -0
  56. package/dist/src/admin.d.ts +189 -0
  57. package/dist/src/admin.d.ts.map +1 -0
  58. package/dist/src/admin.js +539 -0
  59. package/dist/src/admin.js.map +1 -0
  60. package/dist/src/analytics/backend.d.ts +18 -0
  61. package/dist/src/analytics/backend.d.ts.map +1 -0
  62. package/dist/src/analytics/backend.js +25 -0
  63. package/dist/src/analytics/backend.js.map +1 -0
  64. package/dist/src/analytics/clickhouse_backend.d.ts +16 -0
  65. package/dist/src/analytics/clickhouse_backend.d.ts.map +1 -0
  66. package/dist/src/analytics/clickhouse_backend.js +48 -0
  67. package/dist/src/analytics/clickhouse_backend.js.map +1 -0
  68. package/dist/src/analytics/duckdb.d.ts +39 -0
  69. package/dist/src/analytics/duckdb.d.ts.map +1 -0
  70. package/dist/src/analytics/duckdb.js +117 -0
  71. package/dist/src/analytics/duckdb.js.map +1 -0
  72. package/dist/src/analytics/lucid_backend.d.ts +16 -0
  73. package/dist/src/analytics/lucid_backend.d.ts.map +1 -0
  74. package/dist/src/analytics/lucid_backend.js +45 -0
  75. package/dist/src/analytics/lucid_backend.js.map +1 -0
  76. package/dist/src/analytics/metrics.d.ts +50 -0
  77. package/dist/src/analytics/metrics.d.ts.map +1 -0
  78. package/dist/src/analytics/metrics.js +42 -0
  79. package/dist/src/analytics/metrics.js.map +1 -0
  80. package/dist/src/analytics/results.d.ts +80 -0
  81. package/dist/src/analytics/results.d.ts.map +1 -0
  82. package/dist/src/analytics/results.js +143 -0
  83. package/dist/src/analytics/results.js.map +1 -0
  84. package/dist/src/dashboard/access_denied_page.d.ts +100 -0
  85. package/dist/src/dashboard/access_denied_page.d.ts.map +1 -0
  86. package/dist/src/dashboard/access_denied_page.js +262 -0
  87. package/dist/src/dashboard/access_denied_page.js.map +1 -0
  88. package/dist/src/dashboard/actor.d.ts +12 -0
  89. package/dist/src/dashboard/actor.d.ts.map +1 -0
  90. package/dist/src/dashboard/actor.js +33 -0
  91. package/dist/src/dashboard/actor.js.map +1 -0
  92. package/dist/src/dashboard/authorize_decision.d.ts +48 -0
  93. package/dist/src/dashboard/authorize_decision.d.ts.map +1 -0
  94. package/dist/src/dashboard/authorize_decision.js +44 -0
  95. package/dist/src/dashboard/authorize_decision.js.map +1 -0
  96. package/dist/src/dashboard/console_authorize.d.ts +77 -0
  97. package/dist/src/dashboard/console_authorize.d.ts.map +1 -0
  98. package/dist/src/dashboard/console_authorize.js +104 -0
  99. package/dist/src/dashboard/console_authorize.js.map +1 -0
  100. package/dist/src/dashboard/define_config.d.ts +71 -0
  101. package/dist/src/dashboard/define_config.d.ts.map +1 -0
  102. package/dist/src/dashboard/define_config.js +18 -0
  103. package/dist/src/dashboard/define_config.js.map +1 -0
  104. package/dist/src/dashboard/handlers.d.ts +172 -0
  105. package/dist/src/dashboard/handlers.d.ts.map +1 -0
  106. package/dist/src/dashboard/handlers.js +949 -0
  107. package/dist/src/dashboard/handlers.js.map +1 -0
  108. package/dist/src/dashboard/index.d.ts +15 -0
  109. package/dist/src/dashboard/index.d.ts.map +1 -0
  110. package/dist/src/dashboard/index.js +10 -0
  111. package/dist/src/dashboard/index.js.map +1 -0
  112. package/dist/src/dashboard/shape.d.ts +12 -0
  113. package/dist/src/dashboard/shape.d.ts.map +1 -0
  114. package/dist/src/dashboard/shape.js +232 -0
  115. package/dist/src/dashboard/shape.js.map +1 -0
  116. package/dist/src/dashboard/spa.d.ts +34 -0
  117. package/dist/src/dashboard/spa.d.ts.map +1 -0
  118. package/dist/src/dashboard/spa.js +50 -0
  119. package/dist/src/dashboard/spa.js.map +1 -0
  120. package/dist/src/declare.d.ts +85 -0
  121. package/dist/src/declare.d.ts.map +1 -0
  122. package/dist/src/declare.js +62 -0
  123. package/dist/src/declare.js.map +1 -0
  124. package/dist/src/define_config.d.ts +256 -0
  125. package/dist/src/define_config.d.ts.map +1 -0
  126. package/dist/src/define_config.js +90 -0
  127. package/dist/src/define_config.js.map +1 -0
  128. package/dist/src/durable.d.ts +29 -0
  129. package/dist/src/durable.d.ts.map +1 -0
  130. package/dist/src/durable.js +55 -0
  131. package/dist/src/durable.js.map +1 -0
  132. package/dist/src/enrichers.d.ts +51 -0
  133. package/dist/src/enrichers.d.ts.map +1 -0
  134. package/dist/src/enrichers.js +105 -0
  135. package/dist/src/enrichers.js.map +1 -0
  136. package/dist/src/events/buffer.d.ts +38 -0
  137. package/dist/src/events/buffer.d.ts.map +1 -0
  138. package/dist/src/events/buffer.js +86 -0
  139. package/dist/src/events/buffer.js.map +1 -0
  140. package/dist/src/events/event.d.ts +42 -0
  141. package/dist/src/events/event.d.ts.map +1 -0
  142. package/dist/src/events/event.js +82 -0
  143. package/dist/src/events/event.js.map +1 -0
  144. package/dist/src/http/browser.d.ts +33 -0
  145. package/dist/src/http/browser.d.ts.map +1 -0
  146. package/dist/src/http/browser.js +81 -0
  147. package/dist/src/http/browser.js.map +1 -0
  148. package/dist/src/http/browser_api.d.ts +48 -0
  149. package/dist/src/http/browser_api.d.ts.map +1 -0
  150. package/dist/src/http/browser_api.js +273 -0
  151. package/dist/src/http/browser_api.js.map +1 -0
  152. package/dist/src/http/browser_tokens.d.ts +27 -0
  153. package/dist/src/http/browser_tokens.d.ts.map +1 -0
  154. package/dist/src/http/browser_tokens.js +48 -0
  155. package/dist/src/http/browser_tokens.js.map +1 -0
  156. package/dist/src/http/control_plane.d.ts +58 -0
  157. package/dist/src/http/control_plane.d.ts.map +1 -0
  158. package/dist/src/http/control_plane.js +318 -0
  159. package/dist/src/http/control_plane.js.map +1 -0
  160. package/dist/src/http/csrf.d.ts +22 -0
  161. package/dist/src/http/csrf.d.ts.map +1 -0
  162. package/dist/src/http/csrf.js +24 -0
  163. package/dist/src/http/csrf.js.map +1 -0
  164. package/dist/src/http/edge_tag.d.ts +55 -0
  165. package/dist/src/http/edge_tag.d.ts.map +1 -0
  166. package/dist/src/http/edge_tag.js +45 -0
  167. package/dist/src/http/edge_tag.js.map +1 -0
  168. package/dist/src/http/flag_middleware.d.ts +27 -0
  169. package/dist/src/http/flag_middleware.d.ts.map +1 -0
  170. package/dist/src/http/flag_middleware.js +30 -0
  171. package/dist/src/http/flag_middleware.js.map +1 -0
  172. package/dist/src/http/flags_middleware.d.ts +20 -0
  173. package/dist/src/http/flags_middleware.d.ts.map +1 -0
  174. package/dist/src/http/flags_middleware.js +20 -0
  175. package/dist/src/http/flags_middleware.js.map +1 -0
  176. package/dist/src/http/subject_resolver.d.ts +45 -0
  177. package/dist/src/http/subject_resolver.d.ts.map +1 -0
  178. package/dist/src/http/subject_resolver.js +93 -0
  179. package/dist/src/http/subject_resolver.js.map +1 -0
  180. package/dist/src/identity.d.ts +82 -0
  181. package/dist/src/identity.d.ts.map +1 -0
  182. package/dist/src/identity.js +100 -0
  183. package/dist/src/identity.js.map +1 -0
  184. package/dist/src/index.d.ts +41 -0
  185. package/dist/src/index.d.ts.map +1 -0
  186. package/dist/src/index.js +34 -0
  187. package/dist/src/index.js.map +1 -0
  188. package/dist/src/manager.d.ts +154 -0
  189. package/dist/src/manager.d.ts.map +1 -0
  190. package/dist/src/manager.js +333 -0
  191. package/dist/src/manager.js.map +1 -0
  192. package/dist/src/openfeature/provider.d.ts +36 -0
  193. package/dist/src/openfeature/provider.d.ts.map +1 -0
  194. package/dist/src/openfeature/provider.js +82 -0
  195. package/dist/src/openfeature/provider.js.map +1 -0
  196. package/dist/src/relay/relay.d.ts +36 -0
  197. package/dist/src/relay/relay.d.ts.map +1 -0
  198. package/dist/src/relay/relay.js +73 -0
  199. package/dist/src/relay/relay.js.map +1 -0
  200. package/dist/src/relay/results_cache.d.ts +20 -0
  201. package/dist/src/relay/results_cache.d.ts.map +1 -0
  202. package/dist/src/relay/results_cache.js +31 -0
  203. package/dist/src/relay/results_cache.js.map +1 -0
  204. package/dist/src/relay/worker.d.ts +66 -0
  205. package/dist/src/relay/worker.d.ts.map +1 -0
  206. package/dist/src/relay/worker.js +177 -0
  207. package/dist/src/relay/worker.js.map +1 -0
  208. package/dist/src/ruleset_source.d.ts +73 -0
  209. package/dist/src/ruleset_source.d.ts.map +1 -0
  210. package/dist/src/ruleset_source.js +128 -0
  211. package/dist/src/ruleset_source.js.map +1 -0
  212. package/dist/src/runtime.d.ts +70 -0
  213. package/dist/src/runtime.d.ts.map +1 -0
  214. package/dist/src/runtime.js +301 -0
  215. package/dist/src/runtime.js.map +1 -0
  216. package/dist/src/schema.d.ts +39 -0
  217. package/dist/src/schema.d.ts.map +1 -0
  218. package/dist/src/schema.js +311 -0
  219. package/dist/src/schema.js.map +1 -0
  220. package/dist/src/services/accessor.d.ts +5 -0
  221. package/dist/src/services/accessor.d.ts.map +1 -0
  222. package/dist/src/services/accessor.js +20 -0
  223. package/dist/src/services/accessor.js.map +1 -0
  224. package/dist/src/sinks/clickhouse.d.ts +41 -0
  225. package/dist/src/sinks/clickhouse.d.ts.map +1 -0
  226. package/dist/src/sinks/clickhouse.js +117 -0
  227. package/dist/src/sinks/clickhouse.js.map +1 -0
  228. package/dist/src/sinks/lucid_sink.d.ts +25 -0
  229. package/dist/src/sinks/lucid_sink.d.ts.map +1 -0
  230. package/dist/src/sinks/lucid_sink.js +87 -0
  231. package/dist/src/sinks/lucid_sink.js.map +1 -0
  232. package/dist/src/sinks/parquet_sink.d.ts +41 -0
  233. package/dist/src/sinks/parquet_sink.d.ts.map +1 -0
  234. package/dist/src/sinks/parquet_sink.js +140 -0
  235. package/dist/src/sinks/parquet_sink.js.map +1 -0
  236. package/dist/src/sinks/posthog_sink.d.ts +29 -0
  237. package/dist/src/sinks/posthog_sink.d.ts.map +1 -0
  238. package/dist/src/sinks/posthog_sink.js +65 -0
  239. package/dist/src/sinks/posthog_sink.js.map +1 -0
  240. package/dist/src/sinks/sink.d.ts +36 -0
  241. package/dist/src/sinks/sink.d.ts.map +1 -0
  242. package/dist/src/sinks/sink.js +36 -0
  243. package/dist/src/sinks/sink.js.map +1 -0
  244. package/dist/src/sql.d.ts +33 -0
  245. package/dist/src/sql.d.ts.map +1 -0
  246. package/dist/src/sql.js +66 -0
  247. package/dist/src/sql.js.map +1 -0
  248. package/dist/src/stores/lucid_store.d.ts +55 -0
  249. package/dist/src/stores/lucid_store.d.ts.map +1 -0
  250. package/dist/src/stores/lucid_store.js +412 -0
  251. package/dist/src/stores/lucid_store.js.map +1 -0
  252. package/dist/src/stores/memory_store.d.ts +38 -0
  253. package/dist/src/stores/memory_store.d.ts.map +1 -0
  254. package/dist/src/stores/memory_store.js +166 -0
  255. package/dist/src/stores/memory_store.js.map +1 -0
  256. package/dist/src/stores/store.d.ts +166 -0
  257. package/dist/src/stores/store.d.ts.map +1 -0
  258. package/dist/src/stores/store.js +67 -0
  259. package/dist/src/stores/store.js.map +1 -0
  260. package/dist/src/subject.d.ts +25 -0
  261. package/dist/src/subject.d.ts.map +1 -0
  262. package/dist/src/subject.js +30 -0
  263. package/dist/src/subject.js.map +1 -0
  264. package/dist/src/testing/index.d.ts +31 -0
  265. package/dist/src/testing/index.d.ts.map +1 -0
  266. package/dist/src/testing/index.js +60 -0
  267. package/dist/src/testing/index.js.map +1 -0
  268. package/dist/src/topology/browser_token.d.ts +48 -0
  269. package/dist/src/topology/browser_token.d.ts.map +1 -0
  270. package/dist/src/topology/browser_token.js +128 -0
  271. package/dist/src/topology/browser_token.js.map +1 -0
  272. package/dist/src/topology/remote_source.d.ts +39 -0
  273. package/dist/src/topology/remote_source.d.ts.map +1 -0
  274. package/dist/src/topology/remote_source.js +184 -0
  275. package/dist/src/topology/remote_source.js.map +1 -0
  276. package/dist/src/topology/token.d.ts +36 -0
  277. package/dist/src/topology/token.d.ts.map +1 -0
  278. package/dist/src/topology/token.js +68 -0
  279. package/dist/src/topology/token.js.map +1 -0
  280. package/dist/src/topology/verify.d.ts +88 -0
  281. package/dist/src/topology/verify.d.ts.map +1 -0
  282. package/dist/src/topology/verify.js +128 -0
  283. package/dist/src/topology/verify.js.map +1 -0
  284. package/dist/src/transports/db_transport.d.ts +55 -0
  285. package/dist/src/transports/db_transport.d.ts.map +1 -0
  286. package/dist/src/transports/db_transport.js +202 -0
  287. package/dist/src/transports/db_transport.js.map +1 -0
  288. package/dist/src/transports/http_transport.d.ts +30 -0
  289. package/dist/src/transports/http_transport.d.ts.map +1 -0
  290. package/dist/src/transports/http_transport.js +51 -0
  291. package/dist/src/transports/http_transport.js.map +1 -0
  292. package/dist/src/transports/memory_transport.d.ts +29 -0
  293. package/dist/src/transports/memory_transport.d.ts.map +1 -0
  294. package/dist/src/transports/memory_transport.js +91 -0
  295. package/dist/src/transports/memory_transport.js.map +1 -0
  296. package/dist/src/transports/redis_transport.d.ts +50 -0
  297. package/dist/src/transports/redis_transport.d.ts.map +1 -0
  298. package/dist/src/transports/redis_transport.js +107 -0
  299. package/dist/src/transports/redis_transport.js.map +1 -0
  300. package/dist/src/transports/transport.d.ts +62 -0
  301. package/dist/src/transports/transport.d.ts.map +1 -0
  302. package/dist/src/transports/transport.js +2 -0
  303. package/dist/src/transports/transport.js.map +1 -0
  304. package/dist/src/types.d.ts +30 -0
  305. package/dist/src/types.d.ts.map +1 -0
  306. package/dist/src/types.js +2 -0
  307. package/dist/src/types.js.map +1 -0
  308. package/dist/src/usage.d.ts +18 -0
  309. package/dist/src/usage.d.ts.map +1 -0
  310. package/dist/src/usage.js +52 -0
  311. package/dist/src/usage.js.map +1 -0
  312. package/dist/stubs/config/flags.stub +50 -0
  313. package/dist/stubs/config/flags_dashboard.stub +35 -0
  314. package/dist/stubs/database/migrations/create_flags_tables.stub +27 -0
  315. package/dist/stubs/main.d.ts +3 -0
  316. package/dist/stubs/main.d.ts.map +1 -0
  317. package/dist/stubs/main.js +9 -0
  318. package/dist/stubs/main.js.map +1 -0
  319. package/package.json +178 -3
  320. package/skills/flags-basics/SKILL.md +142 -0
  321. package/skills/flags-deployment/SKILL.md +125 -0
  322. package/skills/flags-experiments/SKILL.md +99 -0
@@ -0,0 +1,62 @@
1
+ import type { FlagEvent } from '../events/event.js';
2
+ import type { SqlClient } from '../sql.js';
3
+ /** What a transport guarantees. The provider warns once at boot about what it does not. */
4
+ export interface TransportCapabilities {
5
+ /**
6
+ * Can write inside the caller's database transaction, so a conversion and the business write it
7
+ * belongs to commit or roll back together. Only `db` can.
8
+ */
9
+ transactional: boolean;
10
+ /** Survives a process crash once `publish` resolved. */
11
+ durable: boolean;
12
+ }
13
+ /** A leased batch for one sink. Exactly one of `ack`/`release`/`fail` must be called. */
14
+ export interface Delivery {
15
+ events: FlagEvent[];
16
+ /** Marks the batch delivered and moves the sink's cursor past it. */
17
+ ack(): Promise<void>;
18
+ /** Gives the lease back without moving the cursor (nothing to deliver, or shutting down). */
19
+ release(): Promise<void>;
20
+ /** Records the error and gives the lease back; the same batch is retried later. */
21
+ fail(error: unknown): Promise<void>;
22
+ }
23
+ export interface SinkStatus {
24
+ sink: string;
25
+ /** Events waiting for this sink (approximate on some transports). */
26
+ lag: number;
27
+ delivered: number;
28
+ leasedBy: string | null;
29
+ lastError: string | null;
30
+ }
31
+ export interface PublishOptions {
32
+ /** A database transaction to write in (transactional transports only). */
33
+ client?: SqlClient;
34
+ }
35
+ /**
36
+ * Moves events from the process that produced them to the relay that delivers them to sinks.
37
+ * DESIGN §8: the transport is the log, and each sink reads it at its own pace through its own
38
+ * cursor.
39
+ */
40
+ export interface Transport {
41
+ readonly name: string;
42
+ readonly capabilities: TransportCapabilities;
43
+ setup?(): Promise<void>;
44
+ publish(events: FlagEvent[], options?: PublishOptions): Promise<void>;
45
+ /**
46
+ * Leases the next batch for `sink`. Resolves `null` when another process holds the lease, and a
47
+ * delivery with no events when the sink is caught up. Transports without a consumer side (`http`
48
+ * on a tenant) do not implement it.
49
+ */
50
+ consume?(sink: string, options: {
51
+ batchSize: number;
52
+ leaseMs: number;
53
+ owner: string;
54
+ }): Promise<Delivery | null>;
55
+ /** Moves a sink's cursor back to the beginning (replay / backfill a new destination). */
56
+ rewind?(sink: string): Promise<void>;
57
+ status?(sinks: string[]): Promise<SinkStatus[]>;
58
+ /** The newest id every listed sink has delivered (what is safe to prune). */
59
+ safePruneId?(sinks: string[]): Promise<number>;
60
+ close?(): Promise<void>;
61
+ }
62
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../src/transports/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,2FAA2F;AAC3F,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,aAAa,EAAE,OAAO,CAAC;IACvB,wDAAwD;IACxD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,yFAAyF;AACzF,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,qEAAqE;IACrE,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACrB,6FAA6F;IAC7F,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,mFAAmF;IACnF,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC;IAC7C,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE;;;;OAIG;IACH,OAAO,CAAC,CACN,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAC7D,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAC5B,yFAAyF;IACzF,MAAM,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAChD,6EAA6E;IAC7E,WAAW,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../src/transports/transport.ts"],"names":[],"mappings":""}
@@ -0,0 +1,30 @@
1
+ import type { JsonValue } from '@adonis-agora/flags-core';
2
+ /**
3
+ * The typed flag registry. Flags declared in `config/flags.ts` are added with
4
+ * `interface FlagsList extends InferFlags<typeof flagsConfig> {}`, and flags created in the console
5
+ * with `node ace flags:types` (it writes a `.d.ts` that augments this interface).
6
+ */
7
+ export interface FlagsList {
8
+ }
9
+ /**
10
+ * Type-level settings. `node ace flags:types` sets `strictKeys: true`: once every flag (code and
11
+ * console) is in {@link FlagsList}, an unknown key is a compile error. Without it, any string is
12
+ * accepted (flags created in the console since the last generation still compile) and declared
13
+ * keys are offered by autocomplete.
14
+ */
15
+ export interface FlagsSettings {
16
+ }
17
+ type Declared = Extract<keyof FlagsList, string>;
18
+ type Strict = FlagsSettings extends {
19
+ strictKeys: true;
20
+ } ? true : false;
21
+ /** Flag keys: the declared ones, plus any string unless `strictKeys` is on. */
22
+ export type FlagKey = keyof FlagsList extends never ? string : Strict extends true ? Declared : Declared | (string & {});
23
+ /** The value type of a flag key (any JSON for keys not in {@link FlagsList}). */
24
+ export type FlagValue<K extends string> = K extends keyof FlagsList ? FlagsList[K] : JsonValue;
25
+ /** Keys whose value is boolean (what `enabled()` accepts). */
26
+ export type BooleanFlagKey = keyof FlagsList extends never ? string : ({
27
+ [K in keyof FlagsList]: FlagsList[K] extends boolean ? K : never;
28
+ }[keyof FlagsList] & string) | (Strict extends true ? never : string & {});
29
+ export {};
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,SAAS;CAAG;AAE7B;;;;;GAKG;AACH,MAAM,WAAW,aAAa;CAAG;AAEjC,KAAK,QAAQ,GAAG,OAAO,CAAC,MAAM,SAAS,EAAE,MAAM,CAAC,CAAC;AACjD,KAAK,MAAM,GAAG,aAAa,SAAS;IAAE,UAAU,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,GAAG,KAAK,CAAC;AAExE,+EAA+E;AAC/E,MAAM,MAAM,OAAO,GAAG,MAAM,SAAS,SAAS,KAAK,GAC/C,MAAM,GACN,MAAM,SAAS,IAAI,GACjB,QAAQ,GACR,QAAQ,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAE/B,iFAAiF;AACjF,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;AAE/F,8DAA8D;AAC9D,MAAM,MAAM,cAAc,GAAG,MAAM,SAAS,SAAS,KAAK,GACtD,MAAM,GAEF,CAAC;KAAG,CAAC,IAAI,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,OAAO,GAAG,CAAC,GAAG,KAAK;CAAE,CAAC,MAAM,SAAS,CAAC,GACpF,MAAM,CAAC,GACT,CAAC,MAAM,SAAS,IAAI,GAAG,KAAK,GAAG,MAAM,GAAG,EAAE,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import type { UsageRow } from './stores/store.js';
2
+ /**
3
+ * Counts evaluations per flag in memory and hands them off periodically — the input of stale-flag
4
+ * detection ("not evaluated in 30 days"). Counting is a Map increment; nothing touches I/O on the
5
+ * evaluation path.
6
+ */
7
+ export declare class UsageCounter {
8
+ #private;
9
+ private readonly environment;
10
+ private readonly sink;
11
+ private readonly onError?;
12
+ constructor(environment: string, sink: (rows: UsageRow[]) => Promise<void>, onError?: ((error: unknown) => void) | undefined);
13
+ hit(flag: string): void;
14
+ start(intervalMs?: number): void;
15
+ flush(): Promise<void>;
16
+ close(): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD;;;;GAIG;AACH,qBAAa,YAAY;;IAKrB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IAH3B,YACmB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACzC,OAAO,CAAC,GAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,aAAA,EACjD;IAEJ,GAAG,CAAC,IAAI,EAAE,MAAM,QAEf;IAED,KAAK,CAAC,UAAU,SAAS,QAGxB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAkB3B;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAI3B;CACF"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Counts evaluations per flag in memory and hands them off periodically — the input of stale-flag
3
+ * detection ("not evaluated in 30 days"). Counting is a Map increment; nothing touches I/O on the
4
+ * evaluation path.
5
+ */
6
+ export class UsageCounter {
7
+ environment;
8
+ sink;
9
+ onError;
10
+ #counts = new Map();
11
+ #timer;
12
+ constructor(environment, sink, onError) {
13
+ this.environment = environment;
14
+ this.sink = sink;
15
+ this.onError = onError;
16
+ }
17
+ hit(flag) {
18
+ this.#counts.set(flag, (this.#counts.get(flag) ?? 0) + 1);
19
+ }
20
+ start(intervalMs = 60_000) {
21
+ this.#timer ??= setInterval(() => void this.flush(), intervalMs);
22
+ this.#timer.unref();
23
+ }
24
+ async flush() {
25
+ if (this.#counts.size === 0)
26
+ return;
27
+ const day = new Date().toISOString().slice(0, 10);
28
+ const rows = [...this.#counts].map(([flag, evaluations]) => ({
29
+ environment: this.environment,
30
+ flag,
31
+ day,
32
+ evaluations,
33
+ }));
34
+ this.#counts.clear();
35
+ try {
36
+ await this.sink(rows);
37
+ }
38
+ catch (error) {
39
+ // Put the counts back; they go out with the next flush.
40
+ for (const row of rows)
41
+ this.#counts.set(row.flag, (this.#counts.get(row.flag) ?? 0) + row.evaluations);
42
+ this.onError?.(error);
43
+ }
44
+ }
45
+ async close() {
46
+ if (this.#timer)
47
+ clearInterval(this.#timer);
48
+ this.#timer = undefined;
49
+ await this.flush();
50
+ }
51
+ }
52
+ //# sourceMappingURL=usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/usage.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,OAAO,YAAY;IAKJ,WAAW;IACX,IAAI;IACJ,OAAO;IAN1B,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,MAAM,CAA6B;IAEnC,YACmB,WAAmB,EACnB,IAAyC,EACzC,OAAkC;2BAFlC,WAAW;oBACX,IAAI;uBACJ,OAAO;IACvB,CAAC;IAEJ,GAAG,CAAC,IAAY;QACd,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,UAAU,GAAG,MAAM;QACvB,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;QACjE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QACpC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3D,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,IAAI;YACJ,GAAG;YACH,WAAW;SACZ,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,wDAAwD;YACxD,KAAK,MAAM,GAAG,IAAI,IAAI;gBACpB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;YAClF,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,MAAM;YAAE,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF"}
@@ -0,0 +1,50 @@
1
+ {{{
2
+ exports({ to: app.configPath('flags.ts') })
3
+ }}}
4
+ import env from '#start/env'
5
+ import app from '@adonisjs/core/services/app'
6
+ import { defineConfig, flag, type InferFlags } from '@adonis-agora/flags'
7
+
8
+ const flagsConfig = defineConfig({
9
+ /**
10
+ * Flags declared in code: their type and the value served when the ruleset cannot answer.
11
+ * Targeting (who gets what) is edited in the console or with \`node ace flags:set\`.
12
+ */
13
+ flags: {
14
+ example_flag: flag.boolean({
15
+ default: false,
16
+ description: 'What this flag controls',
17
+ owner: 'your-team',
18
+ // Shown by \`node ace flags:stale\` once this date passes.
19
+ expiresAt: '2027-06-30',
20
+ // Allowed to reach the browser through ctx.flags.all() / useFlag().
21
+ client: true,
22
+ }),
23
+ },
24
+
25
+ /**
26
+ * Which environment's ruleset this process reads (and the console and \`flags:*\` commands edit
27
+ * by default). Each environment has its own targeting.
28
+ */
29
+ environment: env.get('FLAGS_ENVIRONMENT', app.inProduction ? 'production' : 'development'),
30
+
31
+ /**
32
+ * Metrics experiments are judged by, computed from \`flags.track(subject, 'event', { value })\`.
33
+ * Build them with \`metric\` from '@adonis-agora/flags'.
34
+ */
35
+ metrics: {
36
+ // conversion: metric.conversion('purchase'),
37
+ // revenue: metric.sum('purchase', { cupedMs: 14 * 24 * 3600 * 1000 }),
38
+ },
39
+
40
+ /** Experiment key (the flag key) → the metrics that decide it and the ones that must not regress. */
41
+ experiments: {
42
+ // checkout_v2: { metrics: ['conversion', 'revenue'], guardrails: [] },
43
+ },
44
+ })
45
+
46
+ export default flagsConfig
47
+
48
+ declare module '@adonis-agora/flags/types' {
49
+ interface FlagsList extends InferFlags<typeof flagsConfig> {}
50
+ }
@@ -0,0 +1,35 @@
1
+ {{{
2
+ exports({ to: app.configPath('flags_dashboard.ts') })
3
+ }}}
4
+ import { defineConfig } from '@adonis-agora/flags/dashboard'
5
+
6
+ /**
7
+ * Configuration for the @adonis-agora/flags console.
8
+ *
9
+ * Without \`authorize\` the console is open OUTSIDE production and answers
10
+ * every request with 403 in production (a warning is logged at boot). Set
11
+ * \`authorize\` to choose who may open it: an inline function,
12
+ * an authorizer class (\`@inject()\` works), or a lazy import of one
13
+ * (\`() => import('#policies/console_access')\`). It answers \`true\`/\`false\`,
14
+ * \`{ allowed, reason?: 'unauthenticated' | 'forbidden' }\` or \`{ redirectTo }\`:
15
+ * \`unauthenticated\` is a 401, any other denial (or a throw) a 403. With authkit,
16
+ * \`authkitDashboardAuthorize(...)\` from @adonis-agora/authkit-client fits as-is.
17
+ * For machine access use \`authorizeByToken({ token: env.get('CONSOLE_TOKEN') })\`.
18
+ *
19
+ * An identity provider in config/flags.ts (\`identity: identities.adonisAuth({ canOpenConsole })\`,
20
+ * \`identities.authkit()\` or your own class) answers both questions for you.
21
+ *
22
+ * \`actor\` names who made a change (audit log, two-person approvals). Default:
23
+ * the signed-in user's id, else 'dashboard'.
24
+ *
25
+ * Set \`enabled: false\` to remove it entirely — no routes are registered at all.
26
+ */
27
+ export default defineConfig({
28
+ // enabled: true,
29
+ // path: '/flags',
30
+ // authorize: async (ctx) => {
31
+ // if (!(await ctx.auth.check())) return { redirectTo: '/login' }
32
+ // return ctx.auth.user?.isAdmin === true
33
+ // },
34
+ // actor: (ctx) => ctx.auth.user ? { id: String(ctx.auth.user.id), name: ctx.auth.user.email } : undefined,
35
+ })
@@ -0,0 +1,27 @@
1
+ {{{
2
+ exports({ to: app.migrationsPath(`${new Date().getTime()}_create_flags_tables.ts`) })
3
+ }}}
4
+ import { BaseSchema } from '@adonisjs/lucid/schema'
5
+ import { createFlagsTables, dropFlagsTables } from '@adonis-agora/flags'
6
+
7
+ /**
8
+ * The flags tables, for apps that run DDL explicitly.
9
+ *
10
+ * **You may not need this file.** \`@adonis-agora/flags\` creates its tables at boot
11
+ * (\`autoCreateSchema\`, on by default), like the other Agora libraries. Keep this migration if you
12
+ * want migrations to own the schema, and set \`autoCreateSchema: false\` in config/flags.ts. Both
13
+ * paths call the same \`createFlagsTables\`.
14
+ */
15
+ export default class extends BaseSchema {
16
+ async up() {
17
+ this.defer(async (db) => {
18
+ await createFlagsTables(db as never, { partitionEvents: false })
19
+ })
20
+ }
21
+
22
+ async down() {
23
+ this.defer(async (db) => {
24
+ await dropFlagsTables(db as never)
25
+ })
26
+ }
27
+ }
@@ -0,0 +1,3 @@
1
+ /** Absolute path to the published stubs directory (`dist/stubs/`), computed lazily. */
2
+ export declare function stubsRoot(): string;
3
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../stubs/main.ts"],"names":[],"mappings":"AAIA,uFAAuF;AACvF,wBAAgB,SAAS,IAAI,MAAM,CAGlC"}
@@ -0,0 +1,9 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ let cached;
3
+ /** Absolute path to the published stubs directory (`dist/stubs/`), computed lazily. */
4
+ export function stubsRoot() {
5
+ if (cached === undefined)
6
+ cached = fileURLToPath(new URL('./', import.meta.url));
7
+ return cached;
8
+ }
9
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../stubs/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,IAAI,MAA0B,CAAC;AAE/B,uFAAuF;AACvF,MAAM,UAAU,SAAS;IACvB,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACjF,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,181 @@
1
1
  {
2
2
  "name": "@adonis-agora/flags",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Feature flags, A/B experiments and their analysis for AdonisJS — typed flags in code, targeting in your database, exposures relayed to Postgres, Parquet/DuckDB, ClickHouse or PostHog. Speaks OpenFeature. Part of the Agora ecosystem.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/DavideCarvalho/adonis-agora-flags.git",
9
+ "directory": "packages/adonis"
10
+ },
11
+ "homepage": "https://davidecarvalho.github.io/agora/docs/flags",
12
+ "bugs": {
13
+ "url": "https://github.com/DavideCarvalho/adonis-agora-flags/issues"
14
+ },
15
+ "keywords": [
16
+ "adonisjs",
17
+ "adonis",
18
+ "feature-flags",
19
+ "ab-testing",
20
+ "experiments",
21
+ "openfeature",
22
+ "ofrep",
23
+ "agora",
24
+ "tanstack-intent"
25
+ ],
26
+ "author": "Davi Carvalho <davi@goflip.ai>",
27
+ "type": "module",
28
+ "main": "./dist/src/index.js",
29
+ "types": "./dist/src/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/src/index.d.ts",
33
+ "import": "./dist/src/index.js",
34
+ "default": "./dist/src/index.js"
35
+ },
36
+ "./types": {
37
+ "types": "./dist/src/types.d.ts",
38
+ "import": "./dist/src/types.js",
39
+ "default": "./dist/src/types.js"
40
+ },
41
+ "./services/main": {
42
+ "types": "./dist/services/main.d.ts",
43
+ "import": "./dist/services/main.js",
44
+ "default": "./dist/services/main.js"
45
+ },
46
+ "./flags_provider": {
47
+ "types": "./dist/providers/flags_provider.d.ts",
48
+ "import": "./dist/providers/flags_provider.js",
49
+ "default": "./dist/providers/flags_provider.js"
50
+ },
51
+ "./flags_middleware": {
52
+ "types": "./dist/src/http/flags_middleware.d.ts",
53
+ "import": "./dist/src/http/flags_middleware.js",
54
+ "default": "./dist/src/http/flags_middleware.js"
55
+ },
56
+ "./flag_middleware": {
57
+ "types": "./dist/src/http/flag_middleware.d.ts",
58
+ "import": "./dist/src/http/flag_middleware.js",
59
+ "default": "./dist/src/http/flag_middleware.js"
60
+ },
61
+ "./openfeature": {
62
+ "types": "./dist/src/openfeature/provider.d.ts",
63
+ "import": "./dist/src/openfeature/provider.js",
64
+ "default": "./dist/src/openfeature/provider.js"
65
+ },
66
+ "./testing": {
67
+ "types": "./dist/src/testing/index.d.ts",
68
+ "import": "./dist/src/testing/index.js",
69
+ "default": "./dist/src/testing/index.js"
70
+ },
71
+ "./commands": {
72
+ "types": "./dist/commands/main.d.ts",
73
+ "import": "./dist/commands/main.js",
74
+ "default": "./dist/commands/main.js"
75
+ },
76
+ "./configure": {
77
+ "types": "./dist/configure.d.ts",
78
+ "import": "./dist/configure.js",
79
+ "default": "./dist/configure.js"
80
+ },
81
+ "./dashboard": {
82
+ "types": "./dist/src/dashboard/index.d.ts",
83
+ "import": "./dist/src/dashboard/index.js",
84
+ "default": "./dist/src/dashboard/index.js"
85
+ },
86
+ "./dashboard_provider": {
87
+ "types": "./dist/providers/dashboard_provider.d.ts",
88
+ "import": "./dist/providers/dashboard_provider.js",
89
+ "default": "./dist/providers/dashboard_provider.js"
90
+ }
91
+ },
92
+ "files": [
93
+ "dist/",
94
+ "README.md",
95
+ "CHANGELOG.md",
96
+ "skills"
97
+ ],
98
+ "dependencies": {
99
+ "@adonis-agora/flags-core": "^0.1.0"
100
+ },
101
+ "peerDependencies": {
102
+ "@adonis-agora/authkit-client": ">=0.18.0 <2.0.0 || >=1.0.0-rc.0 <2.0.0",
103
+ "@adonis-agora/durable": ">=0.40.0 <2.0.0 || >=1.0.0-rc.0 <2.0.0",
104
+ "@adonisjs/core": "^7.3.0",
105
+ "@adonisjs/lucid": "^22.4.0",
106
+ "@adonisjs/redis": "^10.0.0 || ^11.0.0",
107
+ "@adonisjs/transmit": "^3.0.0",
108
+ "@duckdb/node-api": ">=1.4.0-r.0",
109
+ "@openfeature/server-sdk": "^1.17.0",
110
+ "@opentelemetry/api": "^1.9.0",
111
+ "edge.js": "^6.0.0"
112
+ },
113
+ "peerDependenciesMeta": {
114
+ "@adonis-agora/authkit-client": {
115
+ "optional": true
116
+ },
117
+ "@adonis-agora/durable": {
118
+ "optional": true
119
+ },
120
+ "@adonisjs/lucid": {
121
+ "optional": true
122
+ },
123
+ "@adonisjs/redis": {
124
+ "optional": true
125
+ },
126
+ "@adonisjs/transmit": {
127
+ "optional": true
128
+ },
129
+ "@duckdb/node-api": {
130
+ "optional": true
131
+ },
132
+ "@openfeature/server-sdk": {
133
+ "optional": true
134
+ },
135
+ "@opentelemetry/api": {
136
+ "optional": true
137
+ },
138
+ "edge.js": {
139
+ "optional": true
140
+ }
141
+ },
142
+ "devDependencies": {
143
+ "@adonis-agora/authkit-client": "1.0.0-rc.0",
144
+ "@adonis-agora/durable": "1.0.0-rc.11",
145
+ "@adonis-agora/flags-dashboard": "0.1.0",
146
+ "@adonisjs/assembler": "8.6.0",
147
+ "@adonisjs/core": "7.6.0",
148
+ "@adonisjs/lucid": "22.4.2",
149
+ "@duckdb/node-api": "1.5.6-r.2",
150
+ "@openfeature/core": "1.12.0",
151
+ "@openfeature/server-sdk": "1.23.0",
152
+ "@opentelemetry/api": "1.9.0",
153
+ "@tanstack/intent": "0.5.6",
154
+ "@testcontainers/postgresql": "12.2.1",
155
+ "@testcontainers/redis": "12.2.1",
156
+ "@types/luxon": "3.7.6",
157
+ "@types/node": "26.6.6",
158
+ "@types/pg": "8.23.1",
159
+ "better-sqlite3": "13.0.3",
160
+ "edge.js": "6.5.1",
161
+ "ioredis": "5.11.1",
162
+ "jose": "6.2.15",
163
+ "luxon": "3.7.2",
164
+ "mysql2": "3.24.6",
165
+ "pg": "8.23.1",
166
+ "typescript": "7.0.2",
167
+ "vitest": "5.1.0"
168
+ },
169
+ "engines": {
170
+ "node": ">=24"
171
+ },
172
+ "scripts": {
173
+ "build": "tsc -p tsconfig.json && pnpm run copy:stubs && pnpm run copy:spa",
174
+ "copy:stubs": "rm -rf dist/stubs/config dist/stubs/database && mkdir -p dist/stubs && cp -r stubs/config stubs/database dist/stubs/",
175
+ "test": "vitest run --passWithNoTests",
176
+ "test:integration": "vitest run -c vitest.integration.config.ts",
177
+ "test:watch": "vitest",
178
+ "typecheck": "tsc -p tsconfig.json --noEmit",
179
+ "copy:spa": "mkdir -p dist/assets && rm -rf dist/assets/spa && cp -r ../dashboard/dist/spa dist/assets/spa"
180
+ }
6
181
  }
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: flags-basics
3
+ description: >-
4
+ Set up @adonis-agora/flags in an AdonisJS 7 app and read feature flags: node
5
+ ace configure, declaring typed flags in config/flags.ts with flag.boolean /
6
+ flag.string({ variants }) / flag.number / flag.json, the FlagsList module
7
+ augmentation with InferFlags, ctx.flags.enabled / value / variant / details,
8
+ the flags service (flags.for(user)), the `flag` route-guard middleware, the
9
+ Edge @flag tag, client: true flags for the browser, flags:set (on, off,
10
+ kill, 25%, variant), and fakeFlags / createTestFlags for tests. Use for
11
+ "add a feature flag", "roll out to 10% of users", "hide a route behind a
12
+ flag", "flag is always the default", or "test code that reads a flag".
13
+ metadata:
14
+ type: core
15
+ library: "@adonis-agora/flags"
16
+ library_version: "0.1.0"
17
+ framework: adonisjs
18
+ license: MIT
19
+ sources:
20
+ - 'DavideCarvalho/adonis-agora-flags:docs/getting-started.mdx'
21
+ - 'DavideCarvalho/adonis-agora-flags:docs/declaring-flags.mdx'
22
+ - 'DavideCarvalho/adonis-agora-flags:docs/reading-flags.mdx'
23
+ - 'DavideCarvalho/adonis-agora-flags:docs/targeting.mdx'
24
+ - 'DavideCarvalho/adonis-agora-flags:docs/testing.mdx'
25
+ ---
26
+
27
+ # @adonis-agora/flags — declaring and reading flags
28
+
29
+ Flags are declared in code (key, type, default); who gets what lives in the
30
+ database and changes without a deploy. Evaluation is in memory: reading a flag
31
+ never waits on I/O and never throws — on any problem the declared default is
32
+ served.
33
+
34
+ ## Setup
35
+
36
+ ```sh
37
+ node ace add @adonis-agora/flags
38
+ ```
39
+
40
+ ```ts
41
+ // config/flags.ts
42
+ import { defineConfig, flag, type InferFlags } from '@adonis-agora/flags'
43
+
44
+ const flagsConfig = defineConfig({
45
+ flags: {
46
+ new_player: flag.boolean({ default: false, owner: 'video-team', expiresAt: '2027-03-31' }),
47
+ checkout: flag.string({ default: 'control', variants: ['control', 'one_page'] }),
48
+ max_uploads: flag.number({ default: 10 }),
49
+ },
50
+ })
51
+ export default flagsConfig
52
+
53
+ declare module '@adonis-agora/flags/types' {
54
+ interface FlagsList extends InferFlags<typeof flagsConfig> {}
55
+ }
56
+ ```
57
+
58
+ Flags are usually created in the console (`/flags` → New flag: key, type, default, variants,
59
+ owner); their declaration travels with the ruleset. `node ace flags:types` writes
60
+ `types/flags.d.ts` so console flags are typed (and turns on strict keys). Declaring in
61
+ `config/flags.ts`, as above, is the alternative; code wins when both exist.
62
+
63
+ ## Core patterns
64
+
65
+ ### Pattern 1 — read a flag in a controller
66
+
67
+ ```ts no-check
68
+ async show({ flags, inertia }: HttpContext) {
69
+ if (await flags.enabled('new_player')) return inertia.render('player/new')
70
+ return inertia.render('player/classic')
71
+ }
72
+ ```
73
+
74
+ `ctx.flags` evaluates for the authkit identity when `@adonis-agora/authkit-client`
75
+ is installed (targetingKey = `sub`; attributes `email`, `emailDomain`,
76
+ `globalRoles`, `orgId`, `orgSlug`, `orgRole`), else `ctx.auth.user`.
77
+ `identity` in the config replaces all of it with one `FlagsIdentityProvider`
78
+ (`subject`, `actor`, `authorizeConsole`, `can`): `identities.authkit()`,
79
+ `identities.adonisAuth({ guard, canOpenConsole: (user, ctx) =>
80
+ ctx.bouncer.allows(manageFlags) })`, or a class implementing the interface.
81
+ Outside HTTP:
82
+
83
+ ```ts
84
+ import flags from '@adonis-agora/flags/services/main'
85
+ await flags.for(user).value('max_uploads')
86
+ ```
87
+
88
+ ### Pattern 2 — targeting attributes from server state
89
+
90
+ ```ts no-check
91
+ // config/flags.ts
92
+ attributes: async (subject) => {
93
+ const user = await User.find(subject.id)
94
+ return { plan: user?.plan, orgId: user?.organizationId }
95
+ },
96
+ ```
97
+
98
+ ### Pattern 3 — guard a route
99
+
100
+ ```ts
101
+ router.get('/beta', [BetaController]).use(middleware.flag({ flag: 'new_player' }))
102
+ ```
103
+
104
+ Answers 404 when the flag says no.
105
+
106
+ ### Pattern 4 — change a flag
107
+
108
+ ```sh
109
+ node ace flags:set new_player 10% # rollout; raising it only adds people
110
+ node ace flags:set new_player on
111
+ node ace flags:set new_player kill # kill switch: off for everyone
112
+ ```
113
+
114
+ ### Pattern 5 — tests
115
+
116
+ ```ts
117
+ import { fakeFlags } from '@adonis-agora/flags/testing'
118
+ test('new player', async ({ client, cleanup }) => {
119
+ cleanup(fakeFlags({ new_player: true }))
120
+ })
121
+ ```
122
+
123
+ ## Common mistakes
124
+
125
+ ### HIGH — Expecting `setEnabled(true)` / `unkill` to turn a boolean flag on
126
+
127
+ `enabled` is the kill switch. Turning targeting back on does not serve `on`.
128
+ Use `flags:set <flag> on` (`admin.turnOn`).
129
+
130
+ ### HIGH — Sending flags to the browser with `ctx.flags.all()` and getting `{}`
131
+
132
+ Only flags declared with `client: true` are included, so internal names never
133
+ leak. Declare the flag with `client: true`.
134
+
135
+ ### MEDIUM — Reading targeting attributes from the request
136
+
137
+ `attributes` must come from server state. A query string or header lets users
138
+ pick their own variant.
139
+
140
+ ### MEDIUM — Forgetting the `declare module` block
141
+
142
+ Without it keys are plain strings: typos compile and values are untyped.