@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.
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +15 -2
- package/dist/assets/spa/assets/index-DdmFo7xr.js +12 -0
- package/dist/assets/spa/assets/index-DdmFo7xr.js.map +1 -0
- package/dist/assets/spa/assets/index-DhkcP3Cw.css +2 -0
- package/dist/assets/spa/index.html +26 -0
- package/dist/commands/flags_list.d.ts +9 -0
- package/dist/commands/flags_list.d.ts.map +1 -0
- package/dist/commands/flags_list.js +51 -0
- package/dist/commands/flags_list.js.map +1 -0
- package/dist/commands/flags_ops.d.ts +60 -0
- package/dist/commands/flags_ops.d.ts.map +1 -0
- package/dist/commands/flags_ops.js +253 -0
- package/dist/commands/flags_ops.js.map +1 -0
- package/dist/commands/flags_refs.d.ts +15 -0
- package/dist/commands/flags_refs.d.ts.map +1 -0
- package/dist/commands/flags_refs.js +90 -0
- package/dist/commands/flags_refs.js.map +1 -0
- package/dist/commands/flags_set.d.ts +19 -0
- package/dist/commands/flags_set.d.ts.map +1 -0
- package/dist/commands/flags_set.js +97 -0
- package/dist/commands/flags_set.js.map +1 -0
- package/dist/commands/flags_types.d.ts +18 -0
- package/dist/commands/flags_types.d.ts.map +1 -0
- package/dist/commands/flags_types.js +94 -0
- package/dist/commands/flags_types.js.map +1 -0
- package/dist/commands/flags_work.d.ts +16 -0
- package/dist/commands/flags_work.d.ts.map +1 -0
- package/dist/commands/flags_work.js +71 -0
- package/dist/commands/flags_work.js.map +1 -0
- package/dist/commands/main.d.ts +15 -0
- package/dist/commands/main.d.ts.map +1 -0
- package/dist/commands/main.js +30 -0
- package/dist/commands/main.js.map +1 -0
- package/dist/commands/shared.d.ts +21 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +70 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/configure.d.ts +11 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +32 -0
- package/dist/configure.js.map +1 -0
- package/dist/providers/dashboard_provider.d.ts +65 -0
- package/dist/providers/dashboard_provider.d.ts.map +1 -0
- package/dist/providers/dashboard_provider.js +340 -0
- package/dist/providers/dashboard_provider.js.map +1 -0
- package/dist/providers/flags_provider.d.ts +30 -0
- package/dist/providers/flags_provider.d.ts.map +1 -0
- package/dist/providers/flags_provider.js +184 -0
- package/dist/providers/flags_provider.js.map +1 -0
- package/dist/services/main.d.ts +17 -0
- package/dist/services/main.d.ts.map +1 -0
- package/dist/services/main.js +20 -0
- package/dist/services/main.js.map +1 -0
- package/dist/src/admin.d.ts +189 -0
- package/dist/src/admin.d.ts.map +1 -0
- package/dist/src/admin.js +539 -0
- package/dist/src/admin.js.map +1 -0
- package/dist/src/analytics/backend.d.ts +18 -0
- package/dist/src/analytics/backend.d.ts.map +1 -0
- package/dist/src/analytics/backend.js +25 -0
- package/dist/src/analytics/backend.js.map +1 -0
- package/dist/src/analytics/clickhouse_backend.d.ts +16 -0
- package/dist/src/analytics/clickhouse_backend.d.ts.map +1 -0
- package/dist/src/analytics/clickhouse_backend.js +48 -0
- package/dist/src/analytics/clickhouse_backend.js.map +1 -0
- package/dist/src/analytics/duckdb.d.ts +39 -0
- package/dist/src/analytics/duckdb.d.ts.map +1 -0
- package/dist/src/analytics/duckdb.js +117 -0
- package/dist/src/analytics/duckdb.js.map +1 -0
- package/dist/src/analytics/lucid_backend.d.ts +16 -0
- package/dist/src/analytics/lucid_backend.d.ts.map +1 -0
- package/dist/src/analytics/lucid_backend.js +45 -0
- package/dist/src/analytics/lucid_backend.js.map +1 -0
- package/dist/src/analytics/metrics.d.ts +50 -0
- package/dist/src/analytics/metrics.d.ts.map +1 -0
- package/dist/src/analytics/metrics.js +42 -0
- package/dist/src/analytics/metrics.js.map +1 -0
- package/dist/src/analytics/results.d.ts +80 -0
- package/dist/src/analytics/results.d.ts.map +1 -0
- package/dist/src/analytics/results.js +143 -0
- package/dist/src/analytics/results.js.map +1 -0
- package/dist/src/dashboard/access_denied_page.d.ts +100 -0
- package/dist/src/dashboard/access_denied_page.d.ts.map +1 -0
- package/dist/src/dashboard/access_denied_page.js +262 -0
- package/dist/src/dashboard/access_denied_page.js.map +1 -0
- package/dist/src/dashboard/actor.d.ts +12 -0
- package/dist/src/dashboard/actor.d.ts.map +1 -0
- package/dist/src/dashboard/actor.js +33 -0
- package/dist/src/dashboard/actor.js.map +1 -0
- package/dist/src/dashboard/authorize_decision.d.ts +48 -0
- package/dist/src/dashboard/authorize_decision.d.ts.map +1 -0
- package/dist/src/dashboard/authorize_decision.js +44 -0
- package/dist/src/dashboard/authorize_decision.js.map +1 -0
- package/dist/src/dashboard/console_authorize.d.ts +77 -0
- package/dist/src/dashboard/console_authorize.d.ts.map +1 -0
- package/dist/src/dashboard/console_authorize.js +104 -0
- package/dist/src/dashboard/console_authorize.js.map +1 -0
- package/dist/src/dashboard/define_config.d.ts +71 -0
- package/dist/src/dashboard/define_config.d.ts.map +1 -0
- package/dist/src/dashboard/define_config.js +18 -0
- package/dist/src/dashboard/define_config.js.map +1 -0
- package/dist/src/dashboard/handlers.d.ts +172 -0
- package/dist/src/dashboard/handlers.d.ts.map +1 -0
- package/dist/src/dashboard/handlers.js +949 -0
- package/dist/src/dashboard/handlers.js.map +1 -0
- package/dist/src/dashboard/index.d.ts +15 -0
- package/dist/src/dashboard/index.d.ts.map +1 -0
- package/dist/src/dashboard/index.js +10 -0
- package/dist/src/dashboard/index.js.map +1 -0
- package/dist/src/dashboard/shape.d.ts +12 -0
- package/dist/src/dashboard/shape.d.ts.map +1 -0
- package/dist/src/dashboard/shape.js +232 -0
- package/dist/src/dashboard/shape.js.map +1 -0
- package/dist/src/dashboard/spa.d.ts +34 -0
- package/dist/src/dashboard/spa.d.ts.map +1 -0
- package/dist/src/dashboard/spa.js +50 -0
- package/dist/src/dashboard/spa.js.map +1 -0
- package/dist/src/declare.d.ts +85 -0
- package/dist/src/declare.d.ts.map +1 -0
- package/dist/src/declare.js +62 -0
- package/dist/src/declare.js.map +1 -0
- package/dist/src/define_config.d.ts +256 -0
- package/dist/src/define_config.d.ts.map +1 -0
- package/dist/src/define_config.js +90 -0
- package/dist/src/define_config.js.map +1 -0
- package/dist/src/durable.d.ts +29 -0
- package/dist/src/durable.d.ts.map +1 -0
- package/dist/src/durable.js +55 -0
- package/dist/src/durable.js.map +1 -0
- package/dist/src/enrichers.d.ts +51 -0
- package/dist/src/enrichers.d.ts.map +1 -0
- package/dist/src/enrichers.js +105 -0
- package/dist/src/enrichers.js.map +1 -0
- package/dist/src/events/buffer.d.ts +38 -0
- package/dist/src/events/buffer.d.ts.map +1 -0
- package/dist/src/events/buffer.js +86 -0
- package/dist/src/events/buffer.js.map +1 -0
- package/dist/src/events/event.d.ts +42 -0
- package/dist/src/events/event.d.ts.map +1 -0
- package/dist/src/events/event.js +82 -0
- package/dist/src/events/event.js.map +1 -0
- package/dist/src/http/browser.d.ts +33 -0
- package/dist/src/http/browser.d.ts.map +1 -0
- package/dist/src/http/browser.js +81 -0
- package/dist/src/http/browser.js.map +1 -0
- package/dist/src/http/browser_api.d.ts +48 -0
- package/dist/src/http/browser_api.d.ts.map +1 -0
- package/dist/src/http/browser_api.js +273 -0
- package/dist/src/http/browser_api.js.map +1 -0
- package/dist/src/http/browser_tokens.d.ts +27 -0
- package/dist/src/http/browser_tokens.d.ts.map +1 -0
- package/dist/src/http/browser_tokens.js +48 -0
- package/dist/src/http/browser_tokens.js.map +1 -0
- package/dist/src/http/control_plane.d.ts +58 -0
- package/dist/src/http/control_plane.d.ts.map +1 -0
- package/dist/src/http/control_plane.js +318 -0
- package/dist/src/http/control_plane.js.map +1 -0
- package/dist/src/http/csrf.d.ts +22 -0
- package/dist/src/http/csrf.d.ts.map +1 -0
- package/dist/src/http/csrf.js +24 -0
- package/dist/src/http/csrf.js.map +1 -0
- package/dist/src/http/edge_tag.d.ts +55 -0
- package/dist/src/http/edge_tag.d.ts.map +1 -0
- package/dist/src/http/edge_tag.js +45 -0
- package/dist/src/http/edge_tag.js.map +1 -0
- package/dist/src/http/flag_middleware.d.ts +27 -0
- package/dist/src/http/flag_middleware.d.ts.map +1 -0
- package/dist/src/http/flag_middleware.js +30 -0
- package/dist/src/http/flag_middleware.js.map +1 -0
- package/dist/src/http/flags_middleware.d.ts +20 -0
- package/dist/src/http/flags_middleware.d.ts.map +1 -0
- package/dist/src/http/flags_middleware.js +20 -0
- package/dist/src/http/flags_middleware.js.map +1 -0
- package/dist/src/http/subject_resolver.d.ts +45 -0
- package/dist/src/http/subject_resolver.d.ts.map +1 -0
- package/dist/src/http/subject_resolver.js +93 -0
- package/dist/src/http/subject_resolver.js.map +1 -0
- package/dist/src/identity.d.ts +82 -0
- package/dist/src/identity.d.ts.map +1 -0
- package/dist/src/identity.js +100 -0
- package/dist/src/identity.js.map +1 -0
- package/dist/src/index.d.ts +41 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +34 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/manager.d.ts +154 -0
- package/dist/src/manager.d.ts.map +1 -0
- package/dist/src/manager.js +333 -0
- package/dist/src/manager.js.map +1 -0
- package/dist/src/openfeature/provider.d.ts +36 -0
- package/dist/src/openfeature/provider.d.ts.map +1 -0
- package/dist/src/openfeature/provider.js +82 -0
- package/dist/src/openfeature/provider.js.map +1 -0
- package/dist/src/relay/relay.d.ts +36 -0
- package/dist/src/relay/relay.d.ts.map +1 -0
- package/dist/src/relay/relay.js +73 -0
- package/dist/src/relay/relay.js.map +1 -0
- package/dist/src/relay/results_cache.d.ts +20 -0
- package/dist/src/relay/results_cache.d.ts.map +1 -0
- package/dist/src/relay/results_cache.js +31 -0
- package/dist/src/relay/results_cache.js.map +1 -0
- package/dist/src/relay/worker.d.ts +66 -0
- package/dist/src/relay/worker.d.ts.map +1 -0
- package/dist/src/relay/worker.js +177 -0
- package/dist/src/relay/worker.js.map +1 -0
- package/dist/src/ruleset_source.d.ts +73 -0
- package/dist/src/ruleset_source.d.ts.map +1 -0
- package/dist/src/ruleset_source.js +128 -0
- package/dist/src/ruleset_source.js.map +1 -0
- package/dist/src/runtime.d.ts +70 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +301 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/schema.d.ts +39 -0
- package/dist/src/schema.d.ts.map +1 -0
- package/dist/src/schema.js +311 -0
- package/dist/src/schema.js.map +1 -0
- package/dist/src/services/accessor.d.ts +5 -0
- package/dist/src/services/accessor.d.ts.map +1 -0
- package/dist/src/services/accessor.js +20 -0
- package/dist/src/services/accessor.js.map +1 -0
- package/dist/src/sinks/clickhouse.d.ts +41 -0
- package/dist/src/sinks/clickhouse.d.ts.map +1 -0
- package/dist/src/sinks/clickhouse.js +117 -0
- package/dist/src/sinks/clickhouse.js.map +1 -0
- package/dist/src/sinks/lucid_sink.d.ts +25 -0
- package/dist/src/sinks/lucid_sink.d.ts.map +1 -0
- package/dist/src/sinks/lucid_sink.js +87 -0
- package/dist/src/sinks/lucid_sink.js.map +1 -0
- package/dist/src/sinks/parquet_sink.d.ts +41 -0
- package/dist/src/sinks/parquet_sink.d.ts.map +1 -0
- package/dist/src/sinks/parquet_sink.js +140 -0
- package/dist/src/sinks/parquet_sink.js.map +1 -0
- package/dist/src/sinks/posthog_sink.d.ts +29 -0
- package/dist/src/sinks/posthog_sink.d.ts.map +1 -0
- package/dist/src/sinks/posthog_sink.js +65 -0
- package/dist/src/sinks/posthog_sink.js.map +1 -0
- package/dist/src/sinks/sink.d.ts +36 -0
- package/dist/src/sinks/sink.d.ts.map +1 -0
- package/dist/src/sinks/sink.js +36 -0
- package/dist/src/sinks/sink.js.map +1 -0
- package/dist/src/sql.d.ts +33 -0
- package/dist/src/sql.d.ts.map +1 -0
- package/dist/src/sql.js +66 -0
- package/dist/src/sql.js.map +1 -0
- package/dist/src/stores/lucid_store.d.ts +55 -0
- package/dist/src/stores/lucid_store.d.ts.map +1 -0
- package/dist/src/stores/lucid_store.js +412 -0
- package/dist/src/stores/lucid_store.js.map +1 -0
- package/dist/src/stores/memory_store.d.ts +38 -0
- package/dist/src/stores/memory_store.d.ts.map +1 -0
- package/dist/src/stores/memory_store.js +166 -0
- package/dist/src/stores/memory_store.js.map +1 -0
- package/dist/src/stores/store.d.ts +166 -0
- package/dist/src/stores/store.d.ts.map +1 -0
- package/dist/src/stores/store.js +67 -0
- package/dist/src/stores/store.js.map +1 -0
- package/dist/src/subject.d.ts +25 -0
- package/dist/src/subject.d.ts.map +1 -0
- package/dist/src/subject.js +30 -0
- package/dist/src/subject.js.map +1 -0
- package/dist/src/testing/index.d.ts +31 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +60 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/topology/browser_token.d.ts +48 -0
- package/dist/src/topology/browser_token.d.ts.map +1 -0
- package/dist/src/topology/browser_token.js +128 -0
- package/dist/src/topology/browser_token.js.map +1 -0
- package/dist/src/topology/remote_source.d.ts +39 -0
- package/dist/src/topology/remote_source.d.ts.map +1 -0
- package/dist/src/topology/remote_source.js +184 -0
- package/dist/src/topology/remote_source.js.map +1 -0
- package/dist/src/topology/token.d.ts +36 -0
- package/dist/src/topology/token.d.ts.map +1 -0
- package/dist/src/topology/token.js +68 -0
- package/dist/src/topology/token.js.map +1 -0
- package/dist/src/topology/verify.d.ts +88 -0
- package/dist/src/topology/verify.d.ts.map +1 -0
- package/dist/src/topology/verify.js +128 -0
- package/dist/src/topology/verify.js.map +1 -0
- package/dist/src/transports/db_transport.d.ts +55 -0
- package/dist/src/transports/db_transport.d.ts.map +1 -0
- package/dist/src/transports/db_transport.js +202 -0
- package/dist/src/transports/db_transport.js.map +1 -0
- package/dist/src/transports/http_transport.d.ts +30 -0
- package/dist/src/transports/http_transport.d.ts.map +1 -0
- package/dist/src/transports/http_transport.js +51 -0
- package/dist/src/transports/http_transport.js.map +1 -0
- package/dist/src/transports/memory_transport.d.ts +29 -0
- package/dist/src/transports/memory_transport.d.ts.map +1 -0
- package/dist/src/transports/memory_transport.js +91 -0
- package/dist/src/transports/memory_transport.js.map +1 -0
- package/dist/src/transports/redis_transport.d.ts +50 -0
- package/dist/src/transports/redis_transport.d.ts.map +1 -0
- package/dist/src/transports/redis_transport.js +107 -0
- package/dist/src/transports/redis_transport.js.map +1 -0
- package/dist/src/transports/transport.d.ts +62 -0
- package/dist/src/transports/transport.d.ts.map +1 -0
- package/dist/src/transports/transport.js +2 -0
- package/dist/src/transports/transport.js.map +1 -0
- package/dist/src/types.d.ts +30 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/usage.d.ts +18 -0
- package/dist/src/usage.d.ts.map +1 -0
- package/dist/src/usage.js +52 -0
- package/dist/src/usage.js.map +1 -0
- package/dist/stubs/config/flags.stub +50 -0
- package/dist/stubs/config/flags_dashboard.stub +35 -0
- package/dist/stubs/database/migrations/create_flags_tables.stub +27 -0
- package/dist/stubs/main.d.ts +3 -0
- package/dist/stubs/main.d.ts.map +1 -0
- package/dist/stubs/main.js +9 -0
- package/dist/stubs/main.js.map +1 -0
- package/package.json +178 -3
- package/skills/flags-basics/SKILL.md +142 -0
- package/skills/flags-deployment/SKILL.md +125 -0
- package/skills/flags-experiments/SKILL.md +99 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flags-deployment
|
|
3
|
+
description: >-
|
|
4
|
+
Deploy @adonis-agora/flags: role (all | api | worker), dispatcher (auto |
|
|
5
|
+
durable | in-process), node ace flags:work [--once], transports (db, redis)
|
|
6
|
+
and sinks, retentionDays and partitionEvents, approvals and authorize with
|
|
7
|
+
authz, the console (config/flags_dashboard.ts, authorize, actor), and the
|
|
8
|
+
central topology: topology 'control-plane' with verifyTenant
|
|
9
|
+
(verifiers.authkit / sharedSecret), topology 'tenant' with tokens.
|
|
10
|
+
clientCredentials and snapshotPath, and OFREP. Use for "run flags work in a
|
|
11
|
+
separate worker", "share flags across apps", "approval required for
|
|
12
|
+
production", or "tenant keeps serving defaults".
|
|
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/deployment.mdx'
|
|
21
|
+
- 'DavideCarvalho/adonis-agora-flags:docs/central-server.mdx'
|
|
22
|
+
- 'DavideCarvalho/adonis-agora-flags:docs/operations.mdx'
|
|
23
|
+
- 'DavideCarvalho/adonis-agora-flags:docs/console.mdx'
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# @adonis-agora/flags — deployment
|
|
27
|
+
|
|
28
|
+
## Core patterns
|
|
29
|
+
|
|
30
|
+
### Pattern 1 — background work in a worker
|
|
31
|
+
|
|
32
|
+
```ts no-check
|
|
33
|
+
// config/flags.ts
|
|
34
|
+
role: env.get('FLAGS_ROLE', 'all'),
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Web processes run with `FLAGS_ROLE=api`; one process runs `node ace flags:work`
|
|
38
|
+
(or `--once` from cron). With `@adonis-agora/durable` registered, `dispatcher:
|
|
39
|
+
'auto'` runs it as durable workflows instead.
|
|
40
|
+
|
|
41
|
+
### Pattern 2 — approvals for production
|
|
42
|
+
|
|
43
|
+
```ts no-check
|
|
44
|
+
approvals: { environments: ['production'] },
|
|
45
|
+
authorize: async (actor, action, { environment }) => /* authz check */ true,
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Pattern 3 — one flags server, many apps
|
|
49
|
+
|
|
50
|
+
```ts no-check
|
|
51
|
+
// flags server
|
|
52
|
+
topology: 'control-plane',
|
|
53
|
+
controlPlane: { verifyTenant: verifiers.authkit({ issuer: env.get('AUTHKIT_ISSUER') }) },
|
|
54
|
+
|
|
55
|
+
// each app
|
|
56
|
+
topology: 'tenant',
|
|
57
|
+
tenant: {
|
|
58
|
+
url: env.get('FLAGS_URL'),
|
|
59
|
+
partition: 'streaming',
|
|
60
|
+
token: tokens.clientCredentials({ tokenUrl, clientId, clientSecret }),
|
|
61
|
+
snapshotPath: 'storage/flags/ruleset.json',
|
|
62
|
+
},
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Frontends that do not render through Inertia (SPA, static site) read client
|
|
66
|
+
flags with `remoteFlags({ endpoint: '/_flags/token' })` from
|
|
67
|
+
`@adonis-agora/flags-react`. Set `browser: {}` in the app's config — it mounts
|
|
68
|
+
`GET /_flags/token`, which signs the same subject as `ctx.flags` (authkit
|
|
69
|
+
identity, else `subject` resolver, else anonymous) — and on the control-plane:
|
|
70
|
+
|
|
71
|
+
```ts no-check
|
|
72
|
+
// flags server
|
|
73
|
+
browser: {
|
|
74
|
+
corsOrigins: ['https://app.example.com'],
|
|
75
|
+
// optional: mobile apps holding their own authkit access token (PKCE)
|
|
76
|
+
verifyUser: userVerifiers.authkit({ issuer: env.get('AUTHKIT_ISSUER'), audience: 'flags' }),
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// each app with a frontend
|
|
80
|
+
browser: {},
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Tenants fetch their signing key from the control-plane over the tenant
|
|
84
|
+
connection (needs the `flags:events:write` scope); keys derive from the
|
|
85
|
+
control-plane's `browser.secret` (default `APP_KEY`), per app and environment.
|
|
86
|
+
|
|
87
|
+
## Common mistakes
|
|
88
|
+
|
|
89
|
+
### HIGH — Trusting browser-sent attributes
|
|
90
|
+
|
|
91
|
+
`remoteFlags({ attributes })` are merged *under* the token's identity: they can
|
|
92
|
+
never change who the user is, but anyone can send any value. Never gate access
|
|
93
|
+
on a rule over them; security-relevant attributes come from the `attributes`
|
|
94
|
+
resolver in `config/flags.ts`, which the token endpoint signs into the token.
|
|
95
|
+
|
|
96
|
+
### HIGH — CSRF refusing the flags API
|
|
97
|
+
|
|
98
|
+
With `@adonisjs/shield`, browsers calling the flags API and tenants posting
|
|
99
|
+
events are refused (redirect/403) unless config/shield.ts has
|
|
100
|
+
`exceptRoutes: (ctx) => flagsCsrfExceptions(ctx.request.url())`.
|
|
101
|
+
|
|
102
|
+
### HIGH — `browser` only on the tenant
|
|
103
|
+
|
|
104
|
+
The tenant's token endpoint fetches its key from the control-plane: without
|
|
105
|
+
`browser` there, it answers 503 and the log says so.
|
|
106
|
+
|
|
107
|
+
### HIGH — Console with approvals but no `actor`
|
|
108
|
+
|
|
109
|
+
Anonymous console users are refused in approval environments (two anonymous
|
|
110
|
+
people would be the same actor). Set `actor` in `config/flags_dashboard.ts` or
|
|
111
|
+
sign people in.
|
|
112
|
+
|
|
113
|
+
### HIGH — No `snapshotPath` on tenants
|
|
114
|
+
|
|
115
|
+
A tenant that boots while the control-plane is down serves code defaults
|
|
116
|
+
until it reconnects.
|
|
117
|
+
|
|
118
|
+
### MEDIUM — Enabling `partitionEvents` after the first boot
|
|
119
|
+
|
|
120
|
+
The event table is created on first boot; switching to partitioning later
|
|
121
|
+
needs a manual migration.
|
|
122
|
+
|
|
123
|
+
### MEDIUM — `role: 'worker'` on a tenant
|
|
124
|
+
|
|
125
|
+
Refused at boot: tenants have no relay; the control-plane fans events out.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flags-experiments
|
|
3
|
+
description: >-
|
|
4
|
+
Run A/B experiments with @adonis-agora/flags: metric.conversion / count /
|
|
5
|
+
sum with windowMs and cupedMs, experiments config with metrics and
|
|
6
|
+
guardrails, marking a flag as an experiment with a split, exposures
|
|
7
|
+
recorded by ctx.flags.variant(), flags.track(name, { value, client: trx })
|
|
8
|
+
inside business transactions, flags:results, SRM, sequential p-values,
|
|
9
|
+
CUPED, sticky assignment, layers and the global holdout, environments that
|
|
10
|
+
never mix data, and the analytics backends (lucid, duckdb + parquet sink,
|
|
11
|
+
clickhouse). Use for "A/B test this page", "measure conversion", "why does
|
|
12
|
+
my experiment show sample ratio mismatch", "results are empty", or "move
|
|
13
|
+
analytics to ClickHouse".
|
|
14
|
+
metadata:
|
|
15
|
+
type: core
|
|
16
|
+
library: "@adonis-agora/flags"
|
|
17
|
+
library_version: "0.1.0"
|
|
18
|
+
framework: adonisjs
|
|
19
|
+
license: MIT
|
|
20
|
+
sources:
|
|
21
|
+
- 'DavideCarvalho/adonis-agora-flags:docs/experiments.mdx'
|
|
22
|
+
- 'DavideCarvalho/adonis-agora-flags:docs/events-and-analytics.mdx'
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# @adonis-agora/flags — experiments
|
|
26
|
+
|
|
27
|
+
An experiment is a flag with a split, marked as an experiment. Reading its
|
|
28
|
+
variant records an exposure (once per subject per experiment); `track()`
|
|
29
|
+
records conversions; the worker computes results per environment.
|
|
30
|
+
|
|
31
|
+
## Setup
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
// config/flags.ts
|
|
35
|
+
import { defineConfig, flag, metric } from '@adonis-agora/flags'
|
|
36
|
+
|
|
37
|
+
export default defineConfig({
|
|
38
|
+
flags: { checkout: flag.string({ default: 'control', variants: ['control', 'one_page'] }) },
|
|
39
|
+
metrics: {
|
|
40
|
+
purchase: metric.conversion('purchase'),
|
|
41
|
+
revenue: metric.sum('purchase', { cupedMs: 14 * 24 * 3600 * 1000 }),
|
|
42
|
+
refunds: metric.count('refund', { direction: 'decrease' }),
|
|
43
|
+
},
|
|
44
|
+
experiments: { checkout: { metrics: ['purchase', 'revenue'], guardrails: ['refunds'] } },
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
In the console, give `checkout` a 50/50 split and mark it as an experiment.
|
|
49
|
+
|
|
50
|
+
## Core patterns
|
|
51
|
+
|
|
52
|
+
### Pattern 1 — assign and convert
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const variant = await ctx.flags.variant('checkout') // records the exposure
|
|
56
|
+
|
|
57
|
+
await db.transaction(async (trx) => {
|
|
58
|
+
const order = await Order.create(data, { client: trx })
|
|
59
|
+
await ctx.flags.track('purchase', { value: order.total, client: trx }) // commits with the order
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Pattern 2 — read results
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
node ace flags:results checkout --fresh
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Trust nothing while SRM is flagged. Peek freely with the sequential p-value;
|
|
70
|
+
the fixed-horizon one is only valid at the planned end.
|
|
71
|
+
|
|
72
|
+
### Pattern 3 — scale analytics
|
|
73
|
+
|
|
74
|
+
```ts no-check
|
|
75
|
+
sinks: { warehouse: sinks.lucid(), archive: sinks.parquet({ path: 'storage/flags' }) },
|
|
76
|
+
analytics: analytics.duckdb({ path: 'storage/flags' }),
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Common mistakes
|
|
80
|
+
|
|
81
|
+
### CRITICAL — Changing split weights mid-experiment without `sticky: true`
|
|
82
|
+
|
|
83
|
+
Subjects move between variants and are counted in both. Enable `sticky: true`
|
|
84
|
+
(needs the lucid sink) or restart the experiment.
|
|
85
|
+
|
|
86
|
+
### HIGH — Reading the variant only in the browser
|
|
87
|
+
|
|
88
|
+
`ctx.flags.all()` does not record exposures. Read the variant with
|
|
89
|
+
`ctx.flags.variant()` in the controller that renders the page.
|
|
90
|
+
|
|
91
|
+
### HIGH — Tracking outside the transaction of the business write
|
|
92
|
+
|
|
93
|
+
A rolled-back order would still count as a purchase. Pass `client: trx` (only
|
|
94
|
+
the `db` transport is transactional).
|
|
95
|
+
|
|
96
|
+
### MEDIUM — Expecting staging traffic in production results
|
|
97
|
+
|
|
98
|
+
Events carry their environment. Add `resultsEnvironments: ['production', 'staging']`
|
|
99
|
+
to compute both.
|