@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,311 @@
|
|
|
1
|
+
import { dialectOf, query } from './sql.js';
|
|
2
|
+
/**
|
|
3
|
+
* Every table this package owns. Timestamps are BIGINT epoch milliseconds throughout (see
|
|
4
|
+
* `toDate` in sql.ts), JSON is JSONB / JSON / TEXT depending on the dialect.
|
|
5
|
+
*/
|
|
6
|
+
export const FLAGS_TABLES = [
|
|
7
|
+
'flags_environments',
|
|
8
|
+
'flags_entities',
|
|
9
|
+
'flags_audit',
|
|
10
|
+
'flags_change_requests',
|
|
11
|
+
'flags_schedules',
|
|
12
|
+
'flags_usage',
|
|
13
|
+
'flags_declarations',
|
|
14
|
+
'flags_events',
|
|
15
|
+
'flags_sink_cursors',
|
|
16
|
+
'flags_exposures',
|
|
17
|
+
'flags_tracks',
|
|
18
|
+
'flags_results',
|
|
19
|
+
];
|
|
20
|
+
function typesFor(dialect) {
|
|
21
|
+
if (dialect === 'postgres') {
|
|
22
|
+
return {
|
|
23
|
+
json: 'JSONB',
|
|
24
|
+
bigintPk: 'BIGINT GENERATED BY DEFAULT AS IDENTITY',
|
|
25
|
+
double: 'DOUBLE PRECISION',
|
|
26
|
+
text: 'TEXT',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (dialect === 'mysql') {
|
|
30
|
+
return { json: 'JSON', bigintPk: 'BIGINT AUTO_INCREMENT', double: 'DOUBLE', text: 'TEXT' };
|
|
31
|
+
}
|
|
32
|
+
return { json: 'TEXT', bigintPk: 'INTEGER', double: 'REAL', text: 'TEXT' };
|
|
33
|
+
}
|
|
34
|
+
/** Variable-length keys stay ≤ 191 chars so MySQL utf8mb4 indexes fit. */
|
|
35
|
+
const K = 'VARCHAR(191)';
|
|
36
|
+
function statements(dialect, options) {
|
|
37
|
+
const t = typesFor(dialect);
|
|
38
|
+
const sqlite = dialect === 'sqlite';
|
|
39
|
+
const eventsPk = sqlite
|
|
40
|
+
? 'id INTEGER PRIMARY KEY AUTOINCREMENT'
|
|
41
|
+
: options.partitionEvents && dialect === 'postgres'
|
|
42
|
+
? `id ${t.bigintPk}`
|
|
43
|
+
: `id ${t.bigintPk} PRIMARY KEY`;
|
|
44
|
+
const eventsTail = options.partitionEvents && dialect === 'postgres'
|
|
45
|
+
? ', PRIMARY KEY (id, ts)) PARTITION BY RANGE (ts)'
|
|
46
|
+
: ')';
|
|
47
|
+
return [
|
|
48
|
+
`CREATE TABLE IF NOT EXISTS flags_environments (
|
|
49
|
+
name ${K} PRIMARY KEY,
|
|
50
|
+
version BIGINT NOT NULL DEFAULT 0,
|
|
51
|
+
updated_at BIGINT NOT NULL
|
|
52
|
+
)`,
|
|
53
|
+
`CREATE TABLE IF NOT EXISTS flags_entities (
|
|
54
|
+
environment ${K} NOT NULL,
|
|
55
|
+
entity VARCHAR(16) NOT NULL,
|
|
56
|
+
entity_key ${K} NOT NULL,
|
|
57
|
+
definition ${t.json} NOT NULL,
|
|
58
|
+
updated_at BIGINT NOT NULL,
|
|
59
|
+
updated_by ${K} NOT NULL,
|
|
60
|
+
PRIMARY KEY (environment, entity, entity_key)
|
|
61
|
+
)`,
|
|
62
|
+
`CREATE TABLE IF NOT EXISTS flags_audit (
|
|
63
|
+
id VARCHAR(36) PRIMARY KEY,
|
|
64
|
+
environment ${K} NOT NULL,
|
|
65
|
+
entity VARCHAR(16) NOT NULL,
|
|
66
|
+
entity_key ${K} NULL,
|
|
67
|
+
action VARCHAR(16) NOT NULL,
|
|
68
|
+
before_value ${t.json} NULL,
|
|
69
|
+
after_value ${t.json} NULL,
|
|
70
|
+
actor_id ${K} NOT NULL,
|
|
71
|
+
actor_name ${K} NULL,
|
|
72
|
+
change_request_id VARCHAR(36) NULL,
|
|
73
|
+
version BIGINT NOT NULL,
|
|
74
|
+
at BIGINT NOT NULL
|
|
75
|
+
)`,
|
|
76
|
+
`CREATE TABLE IF NOT EXISTS flags_change_requests (
|
|
77
|
+
id VARCHAR(36) PRIMARY KEY,
|
|
78
|
+
environment ${K} NOT NULL,
|
|
79
|
+
change_value ${t.json} NOT NULL,
|
|
80
|
+
note ${t.text} NULL,
|
|
81
|
+
requested_by ${t.json} NOT NULL,
|
|
82
|
+
requested_at BIGINT NOT NULL,
|
|
83
|
+
status VARCHAR(16) NOT NULL,
|
|
84
|
+
reviewed_by ${t.json} NULL,
|
|
85
|
+
reviewed_at BIGINT NULL,
|
|
86
|
+
review_note ${t.text} NULL,
|
|
87
|
+
base_version VARCHAR(32) NOT NULL
|
|
88
|
+
)`,
|
|
89
|
+
`CREATE TABLE IF NOT EXISTS flags_schedules (
|
|
90
|
+
id VARCHAR(36) PRIMARY KEY,
|
|
91
|
+
environment ${K} NOT NULL,
|
|
92
|
+
flag ${K} NOT NULL,
|
|
93
|
+
kind VARCHAR(16) NOT NULL,
|
|
94
|
+
payload ${t.json} NOT NULL,
|
|
95
|
+
run_at BIGINT NOT NULL,
|
|
96
|
+
status VARCHAR(16) NOT NULL,
|
|
97
|
+
created_by ${t.json} NOT NULL,
|
|
98
|
+
error ${t.text} NULL,
|
|
99
|
+
executed_at BIGINT NULL
|
|
100
|
+
)`,
|
|
101
|
+
`CREATE TABLE IF NOT EXISTS flags_usage (
|
|
102
|
+
environment ${K} NOT NULL,
|
|
103
|
+
flag ${K} NOT NULL,
|
|
104
|
+
day CHAR(10) NOT NULL,
|
|
105
|
+
evaluations BIGINT NOT NULL,
|
|
106
|
+
PRIMARY KEY (environment, flag, day)
|
|
107
|
+
)`,
|
|
108
|
+
`CREATE TABLE IF NOT EXISTS flags_declarations (
|
|
109
|
+
app ${K} NOT NULL,
|
|
110
|
+
flag ${K} NOT NULL,
|
|
111
|
+
declaration ${t.json} NOT NULL,
|
|
112
|
+
last_seen_at BIGINT NOT NULL,
|
|
113
|
+
PRIMARY KEY (app, flag)
|
|
114
|
+
)`,
|
|
115
|
+
`CREATE TABLE IF NOT EXISTS flags_events (
|
|
116
|
+
${eventsPk},
|
|
117
|
+
event_id VARCHAR(36) NOT NULL,
|
|
118
|
+
schema_version SMALLINT NOT NULL,
|
|
119
|
+
app ${K} NOT NULL,
|
|
120
|
+
environment VARCHAR(64) NOT NULL,
|
|
121
|
+
type VARCHAR(16) NOT NULL,
|
|
122
|
+
flag ${K} NULL,
|
|
123
|
+
experiment ${K} NULL,
|
|
124
|
+
variant ${K} NULL,
|
|
125
|
+
name ${K} NULL,
|
|
126
|
+
subject ${K} NOT NULL,
|
|
127
|
+
subject_type VARCHAR(64) NOT NULL,
|
|
128
|
+
value ${t.double} NULL,
|
|
129
|
+
props ${t.json} NULL,
|
|
130
|
+
ts BIGINT NOT NULL${dialect === 'postgres'
|
|
131
|
+
? `,
|
|
132
|
+
tx xid8 NOT NULL DEFAULT pg_current_xact_id()`
|
|
133
|
+
: ''}
|
|
134
|
+
${eventsTail}`,
|
|
135
|
+
`CREATE TABLE IF NOT EXISTS flags_sink_cursors (
|
|
136
|
+
sink ${K} PRIMARY KEY,
|
|
137
|
+
last_id BIGINT NOT NULL DEFAULT 0,
|
|
138
|
+
last_tx BIGINT NOT NULL DEFAULT 0,
|
|
139
|
+
leased_by ${K} NULL,
|
|
140
|
+
lease_until BIGINT NULL,
|
|
141
|
+
delivered BIGINT NOT NULL DEFAULT 0,
|
|
142
|
+
last_error ${t.text} NULL,
|
|
143
|
+
updated_at BIGINT NOT NULL
|
|
144
|
+
)`,
|
|
145
|
+
`CREATE TABLE IF NOT EXISTS flags_exposures (
|
|
146
|
+
environment VARCHAR(64) NOT NULL,
|
|
147
|
+
experiment ${K} NOT NULL,
|
|
148
|
+
subject ${K} NOT NULL,
|
|
149
|
+
variant ${K} NOT NULL,
|
|
150
|
+
flag ${K} NOT NULL,
|
|
151
|
+
app ${K} NOT NULL,
|
|
152
|
+
subject_type VARCHAR(64) NOT NULL,
|
|
153
|
+
first_seen_ts BIGINT NOT NULL,
|
|
154
|
+
PRIMARY KEY (environment, experiment, subject)
|
|
155
|
+
)`,
|
|
156
|
+
`CREATE TABLE IF NOT EXISTS flags_tracks (
|
|
157
|
+
event_id VARCHAR(36) PRIMARY KEY,
|
|
158
|
+
environment VARCHAR(64) NOT NULL,
|
|
159
|
+
name ${K} NOT NULL,
|
|
160
|
+
subject ${K} NOT NULL,
|
|
161
|
+
subject_type VARCHAR(64) NOT NULL,
|
|
162
|
+
app ${K} NOT NULL,
|
|
163
|
+
value ${t.double} NULL,
|
|
164
|
+
props ${t.json} NULL,
|
|
165
|
+
ts BIGINT NOT NULL
|
|
166
|
+
)`,
|
|
167
|
+
`CREATE TABLE IF NOT EXISTS flags_results (
|
|
168
|
+
environment VARCHAR(64) NOT NULL,
|
|
169
|
+
experiment ${K} NOT NULL,
|
|
170
|
+
payload ${t.json} NOT NULL,
|
|
171
|
+
computed_at BIGINT NOT NULL,
|
|
172
|
+
PRIMARY KEY (environment, experiment)
|
|
173
|
+
)`,
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
const INDEXES = [
|
|
177
|
+
['flags_audit_env_version_idx', 'flags_audit', 'environment, version'],
|
|
178
|
+
['flags_change_requests_status_idx', 'flags_change_requests', 'status, requested_at'],
|
|
179
|
+
['flags_schedules_due_idx', 'flags_schedules', 'status, run_at'],
|
|
180
|
+
['flags_events_ts_idx', 'flags_events', 'ts'],
|
|
181
|
+
['flags_exposures_subject_idx', 'flags_exposures', 'subject'],
|
|
182
|
+
['flags_tracks_env_name_subject_ts_idx', 'flags_tracks', 'environment, name, subject, ts'],
|
|
183
|
+
['flags_tracks_subject_idx', 'flags_tracks', 'subject'],
|
|
184
|
+
];
|
|
185
|
+
async function createIndex(client, dialect, name, table, columns) {
|
|
186
|
+
if (dialect !== 'mysql') {
|
|
187
|
+
await client.rawQuery(`CREATE INDEX IF NOT EXISTS ${name} ON ${table} (${columns})`);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
await client.rawQuery(`CREATE INDEX ${name} ON ${table} (${columns})`);
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
if (!/duplicate key name|already exists/i.test(String(error.message)))
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Serializes concurrent DDL across replicas booting together: a Postgres advisory lock or a MySQL
|
|
200
|
+
* named lock. SQLite has a single writer already.
|
|
201
|
+
*/
|
|
202
|
+
async function withDdlLock(db, dialect, fn) {
|
|
203
|
+
if (dialect === 'postgres') {
|
|
204
|
+
return db.transaction(async (trx) => {
|
|
205
|
+
await trx.rawQuery("SELECT pg_advisory_xact_lock(hashtext('adonis-agora-flags-ddl'))");
|
|
206
|
+
return fn(trx);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (dialect === 'mysql') {
|
|
210
|
+
// GET_LOCK belongs to the session, so the lock, the DDL and the release must share one
|
|
211
|
+
// connection: a transaction pins it. DDL commits implicitly, but the session (and the named
|
|
212
|
+
// lock) survives that.
|
|
213
|
+
return db.transaction(async (trx) => {
|
|
214
|
+
await trx.rawQuery("SELECT GET_LOCK('adonis-agora-flags-ddl', 60)");
|
|
215
|
+
try {
|
|
216
|
+
return await fn(trx);
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
await trx.rawQuery("SELECT RELEASE_LOCK('adonis-agora-flags-ddl')");
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
return fn(db);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Creates every table and index. Idempotent and safe to run from several replicas at once. Called
|
|
227
|
+
* by the provider at boot when `autoCreateSchema` is on (the default) and by the published
|
|
228
|
+
* migration — the same function on both paths, so the schema cannot drift.
|
|
229
|
+
*/
|
|
230
|
+
export async function createFlagsTables(db, options = {}) {
|
|
231
|
+
const dialect = dialectOf(db);
|
|
232
|
+
await withDdlLock(db, dialect, async (client) => {
|
|
233
|
+
for (const sql of statements(dialect, options))
|
|
234
|
+
await client.rawQuery(sql);
|
|
235
|
+
for (const [name, table, columns] of INDEXES) {
|
|
236
|
+
// A BRIN index fits an append-only log ordered by time far better than a B-tree.
|
|
237
|
+
if (name === 'flags_events_ts_idx' && dialect === 'postgres') {
|
|
238
|
+
await client.rawQuery(`CREATE INDEX IF NOT EXISTS ${name} ON ${table} USING BRIN (${columns})`);
|
|
239
|
+
// The relay reads in (tx, id) order; see DbTransport.
|
|
240
|
+
await client.rawQuery('CREATE INDEX IF NOT EXISTS flags_events_tx_id_idx ON flags_events (tx, id)');
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
await createIndex(client, dialect, name, table, columns);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
if (options.partitionEvents && dialect === 'postgres')
|
|
247
|
+
await ensureEventPartitions(db, Date.now());
|
|
248
|
+
}
|
|
249
|
+
/** Drops every table this package owns. */
|
|
250
|
+
export async function dropFlagsTables(db) {
|
|
251
|
+
for (const table of [...FLAGS_TABLES].reverse())
|
|
252
|
+
await db.rawQuery(`DROP TABLE IF EXISTS ${table}`);
|
|
253
|
+
}
|
|
254
|
+
/** Whether `flags_events` is a partitioned table (Postgres). */
|
|
255
|
+
export async function eventsArePartitioned(db) {
|
|
256
|
+
if (dialectOf(db) !== 'postgres')
|
|
257
|
+
return false;
|
|
258
|
+
const rows = await query(db, "SELECT c.relkind FROM pg_class c WHERE c.relname = 'flags_events' AND c.relnamespace = to_regnamespace(current_schema())");
|
|
259
|
+
return rows[0]?.relkind === 'p';
|
|
260
|
+
}
|
|
261
|
+
function monthStart(ms, offset = 0) {
|
|
262
|
+
const d = new Date(ms);
|
|
263
|
+
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + offset, 1));
|
|
264
|
+
}
|
|
265
|
+
function partitionName(start) {
|
|
266
|
+
return `flags_events_${start.getUTCFullYear()}${String(start.getUTCMonth() + 1).padStart(2, '0')}`;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Creates the monthly partitions for the current and the next two months. Called at boot and by the
|
|
270
|
+
* worker, so inserts never hit a missing partition.
|
|
271
|
+
*/
|
|
272
|
+
export async function ensureEventPartitions(db, now) {
|
|
273
|
+
if (!(await eventsArePartitioned(db)))
|
|
274
|
+
return;
|
|
275
|
+
for (let offset = 0; offset < 3; offset++) {
|
|
276
|
+
const start = monthStart(now, offset);
|
|
277
|
+
const end = monthStart(now, offset + 1);
|
|
278
|
+
await db.rawQuery(`CREATE TABLE IF NOT EXISTS ${partitionName(start)} PARTITION OF flags_events FOR VALUES FROM (${start.getTime()}) TO (${end.getTime()})`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Removes events older than `before` (epoch ms) that every sink cursor has already passed. On a
|
|
283
|
+
* partitioned table whole monthly partitions are dropped; otherwise rows are deleted in batches.
|
|
284
|
+
* Returns how many partitions or rows went away.
|
|
285
|
+
*/
|
|
286
|
+
export async function pruneEvents(db, before, safeId) {
|
|
287
|
+
if (await eventsArePartitioned(db)) {
|
|
288
|
+
const parts = await query(db, `SELECT c.relname AS name, pg_get_expr(c.relpartbound, c.oid) AS bound
|
|
289
|
+
FROM pg_inherits i JOIN pg_class c ON c.oid = i.inhrelid
|
|
290
|
+
JOIN pg_class p ON p.oid = i.inhparent
|
|
291
|
+
WHERE p.relname = 'flags_events' AND p.relnamespace = to_regnamespace(current_schema())`);
|
|
292
|
+
let partitions = 0;
|
|
293
|
+
for (const part of parts) {
|
|
294
|
+
const match = /TO \('?(\d+)'?\)/.exec(part.bound);
|
|
295
|
+
if (!match || Number(match[1]) > before)
|
|
296
|
+
continue;
|
|
297
|
+
const maxRows = await query(db, `SELECT MAX(id) AS max FROM ${part.name}`);
|
|
298
|
+
const max = maxRows[0]?.max;
|
|
299
|
+
if (max !== null && max !== undefined && Number(max) > safeId)
|
|
300
|
+
continue;
|
|
301
|
+
await db.rawQuery(`DROP TABLE ${part.name}`);
|
|
302
|
+
partitions++;
|
|
303
|
+
}
|
|
304
|
+
return { partitions, rows: 0 };
|
|
305
|
+
}
|
|
306
|
+
const result = await query(db, dialectOf(db) === 'postgres'
|
|
307
|
+
? 'WITH gone AS (DELETE FROM flags_events WHERE ts < ? AND id <= ? RETURNING 1) SELECT COUNT(*) AS count FROM gone'
|
|
308
|
+
: 'DELETE FROM flags_events WHERE ts < ? AND id <= ?', [before, safeId]);
|
|
309
|
+
return { partitions: 0, rows: Number(result[0]?.count ?? 0) };
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FlagsRuntime } from '../runtime.js';
|
|
2
|
+
export declare function setRuntime(runtime: FlagsRuntime | undefined): void;
|
|
3
|
+
export declare function getRuntime(): FlagsRuntime;
|
|
4
|
+
export declare function hasRuntime(): boolean;
|
|
5
|
+
//# sourceMappingURL=accessor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessor.d.ts","sourceRoot":"","sources":["../../../src/services/accessor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAWlD,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,QAE3D;AAED,wBAAgB,UAAU,IAAI,YAAY,CAQzC;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runtime the provider built, readable outside the container (middleware, the OpenFeature
|
|
3
|
+
* provider, commands). Kept on a `Symbol.for` slot so two copies of this package in one process
|
|
4
|
+
* still share it.
|
|
5
|
+
*/
|
|
6
|
+
const SLOT = Symbol.for('@adonis-agora/flags/runtime');
|
|
7
|
+
export function setRuntime(runtime) {
|
|
8
|
+
globalThis[SLOT] = runtime;
|
|
9
|
+
}
|
|
10
|
+
export function getRuntime() {
|
|
11
|
+
const runtime = globalThis[SLOT];
|
|
12
|
+
if (!runtime) {
|
|
13
|
+
throw new Error('@adonis-agora/flags is not booted: register "@adonis-agora/flags/flags_provider" in adonisrc.ts (node ace configure @adonis-agora/flags)');
|
|
14
|
+
}
|
|
15
|
+
return runtime;
|
|
16
|
+
}
|
|
17
|
+
export function hasRuntime() {
|
|
18
|
+
return globalThis[SLOT] !== undefined;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=accessor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessor.js","sourceRoot":"","sources":["../../../src/services/accessor.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;AAIvD,MAAM,UAAU,UAAU,CAAC,OAAiC;IACzD,UAAqB,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,OAAO,GAAI,UAAqB,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,0IAA0I,CAC3I,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAQ,UAAqB,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { FlagEvent } from '../events/event.js';
|
|
2
|
+
import type { Sink } from './sink.js';
|
|
3
|
+
export interface ClickHouseOptions {
|
|
4
|
+
/** HTTP endpoint, e.g. `http://localhost:8123`. */
|
|
5
|
+
url: string;
|
|
6
|
+
database?: string;
|
|
7
|
+
username?: string;
|
|
8
|
+
password?: string;
|
|
9
|
+
/** Table for every event. Default `flags_events`. */
|
|
10
|
+
table?: string;
|
|
11
|
+
fetch?: typeof fetch;
|
|
12
|
+
}
|
|
13
|
+
/** A tiny ClickHouse HTTP client: no driver dependency, just the HTTP interface. */
|
|
14
|
+
export declare class ClickHouseClient {
|
|
15
|
+
#private;
|
|
16
|
+
readonly options: ClickHouseOptions;
|
|
17
|
+
readonly table: string;
|
|
18
|
+
constructor(options: ClickHouseOptions);
|
|
19
|
+
exec(sql: string): Promise<string>;
|
|
20
|
+
insert(rows: Array<Record<string, unknown>>): Promise<void>;
|
|
21
|
+
/** Runs a SELECT with `{name:Type}` parameters and returns the rows. */
|
|
22
|
+
select<T = Record<string, unknown>>(sql: string, params?: Record<string, string | number>): Promise<T[]>;
|
|
23
|
+
createTable(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/** Every event into one ClickHouse table, read back by the `clickhouse` analytics backend. */
|
|
26
|
+
export declare class ClickHouseSink implements Sink {
|
|
27
|
+
#private;
|
|
28
|
+
readonly kind = "clickhouse";
|
|
29
|
+
readonly client: ClickHouseClient;
|
|
30
|
+
constructor(options: ClickHouseOptions & {
|
|
31
|
+
batchSize?: number;
|
|
32
|
+
intervalMs?: number;
|
|
33
|
+
autoCreateSchema?: boolean;
|
|
34
|
+
});
|
|
35
|
+
readonly batchSize: number;
|
|
36
|
+
readonly intervalMs?: number;
|
|
37
|
+
setup(): Promise<void>;
|
|
38
|
+
write(events: FlagEvent[]): Promise<void>;
|
|
39
|
+
forget(subject: string): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=clickhouse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clickhouse.d.ts","sourceRoot":"","sources":["../../../src/sinks/clickhouse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,oFAAoF;AACpF,qBAAa,gBAAgB;;IAIf,QAAQ,CAAC,OAAO,EAAE,iBAAiB;IAF/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,YAAqB,OAAO,EAAE,iBAAiB,EAG9C;IAuBD,IAAI,CAAC,GAAG,EAAE,MAAM,mBAEf;IAEK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,iBAQhD;IAED,wEAAwE;IAClE,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAM,GAC3C,OAAO,CAAC,CAAC,EAAE,CAAC,CAQd;IAEK,WAAW,kBAqBhB;CACF;AAED,8FAA8F;AAC9F,qBAAa,cAAe,YAAW,IAAI;;IACzC,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAElC,YACE,OAAO,EAAE,iBAAiB,GAAG;QAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B,EAMF;IAED,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAGvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAEK,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB9C;IAEK,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI3C;CACF"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/** A tiny ClickHouse HTTP client: no driver dependency, just the HTTP interface. */
|
|
2
|
+
export class ClickHouseClient {
|
|
3
|
+
options;
|
|
4
|
+
#fetch;
|
|
5
|
+
table;
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.options = options;
|
|
8
|
+
this.#fetch = options.fetch ?? fetch;
|
|
9
|
+
this.table = options.table ?? 'flags_events';
|
|
10
|
+
}
|
|
11
|
+
/** POSTs `sql` (in the `query` parameter) with an optional data body. */
|
|
12
|
+
async #request(sql, body, settings = {}) {
|
|
13
|
+
const url = new URL(this.options.url);
|
|
14
|
+
url.searchParams.set('query', sql);
|
|
15
|
+
if (this.options.database)
|
|
16
|
+
url.searchParams.set('database', this.options.database);
|
|
17
|
+
for (const [key, value] of Object.entries(settings))
|
|
18
|
+
url.searchParams.set(key, String(value));
|
|
19
|
+
const headers = {};
|
|
20
|
+
if (this.options.username) {
|
|
21
|
+
headers.authorization = `Basic ${Buffer.from(`${this.options.username}:${this.options.password ?? ''}`).toString('base64')}`;
|
|
22
|
+
}
|
|
23
|
+
const response = await this.#fetch(url, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers,
|
|
26
|
+
...(body !== undefined ? { body } : {}),
|
|
27
|
+
});
|
|
28
|
+
const text = await response.text();
|
|
29
|
+
if (!response.ok)
|
|
30
|
+
throw new Error(`ClickHouse answered ${response.status}: ${text.slice(0, 500)}`);
|
|
31
|
+
return text;
|
|
32
|
+
}
|
|
33
|
+
exec(sql) {
|
|
34
|
+
return this.#request(sql);
|
|
35
|
+
}
|
|
36
|
+
async insert(rows) {
|
|
37
|
+
if (rows.length === 0)
|
|
38
|
+
return;
|
|
39
|
+
await this.#request(`INSERT INTO ${this.table} FORMAT JSONEachRow`, rows.map((row) => JSON.stringify(row)).join('\n'),
|
|
40
|
+
// Server-side batching: many small inserts become few parts.
|
|
41
|
+
{ async_insert: 1, wait_for_async_insert: 1 });
|
|
42
|
+
}
|
|
43
|
+
/** Runs a SELECT with `{name:Type}` parameters and returns the rows. */
|
|
44
|
+
async select(sql, params = {}) {
|
|
45
|
+
const settings = {};
|
|
46
|
+
for (const [key, value] of Object.entries(params))
|
|
47
|
+
settings[`param_${key}`] = value;
|
|
48
|
+
const text = await this.#request(`${sql} FORMAT JSONEachRow`, undefined, settings);
|
|
49
|
+
return text
|
|
50
|
+
.split('\n')
|
|
51
|
+
.filter((line) => line.trim().length > 0)
|
|
52
|
+
.map((line) => JSON.parse(line));
|
|
53
|
+
}
|
|
54
|
+
async createTable() {
|
|
55
|
+
// ReplacingMergeTree on the event id: re-sent events collapse on merge, and the analytics
|
|
56
|
+
// queries count DISTINCT subjects, so pre-merge duplicates never change a result.
|
|
57
|
+
await this.exec(`CREATE TABLE IF NOT EXISTS ${this.table} (
|
|
58
|
+
event_id String,
|
|
59
|
+
schema_version UInt8,
|
|
60
|
+
app LowCardinality(String),
|
|
61
|
+
environment LowCardinality(String),
|
|
62
|
+
type LowCardinality(String),
|
|
63
|
+
flag Nullable(String),
|
|
64
|
+
experiment Nullable(String),
|
|
65
|
+
variant Nullable(String),
|
|
66
|
+
name Nullable(String),
|
|
67
|
+
subject String,
|
|
68
|
+
subject_type LowCardinality(String),
|
|
69
|
+
value Nullable(Float64),
|
|
70
|
+
props String,
|
|
71
|
+
ts Int64
|
|
72
|
+
) ENGINE = ReplacingMergeTree
|
|
73
|
+
PARTITION BY toYYYYMM(fromUnixTimestamp64Milli(ts))
|
|
74
|
+
ORDER BY (environment, type, ifNull(experiment, ''), ifNull(name, ''), subject, event_id)`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Every event into one ClickHouse table, read back by the `clickhouse` analytics backend. */
|
|
78
|
+
export class ClickHouseSink {
|
|
79
|
+
kind = 'clickhouse';
|
|
80
|
+
client;
|
|
81
|
+
constructor(options) {
|
|
82
|
+
this.client = new ClickHouseClient(options);
|
|
83
|
+
this.batchSize = options.batchSize ?? 5000;
|
|
84
|
+
if (options.intervalMs !== undefined)
|
|
85
|
+
this.intervalMs = options.intervalMs;
|
|
86
|
+
this.#autoCreate = options.autoCreateSchema !== false;
|
|
87
|
+
}
|
|
88
|
+
batchSize;
|
|
89
|
+
intervalMs;
|
|
90
|
+
#autoCreate;
|
|
91
|
+
async setup() {
|
|
92
|
+
if (this.#autoCreate)
|
|
93
|
+
await this.client.createTable();
|
|
94
|
+
}
|
|
95
|
+
async write(events) {
|
|
96
|
+
await this.client.insert(events.map((e) => ({
|
|
97
|
+
event_id: e.id,
|
|
98
|
+
schema_version: e.schemaVersion,
|
|
99
|
+
app: e.app,
|
|
100
|
+
environment: e.environment,
|
|
101
|
+
type: e.type,
|
|
102
|
+
flag: e.flag ?? null,
|
|
103
|
+
experiment: e.experiment ?? null,
|
|
104
|
+
variant: e.variant ?? null,
|
|
105
|
+
name: e.name ?? null,
|
|
106
|
+
subject: e.subject,
|
|
107
|
+
subject_type: e.subjectType,
|
|
108
|
+
value: e.value ?? null,
|
|
109
|
+
props: JSON.stringify(e.props ?? {}),
|
|
110
|
+
ts: e.ts,
|
|
111
|
+
})));
|
|
112
|
+
}
|
|
113
|
+
async forget(subject) {
|
|
114
|
+
await this.client.exec(`ALTER TABLE ${this.client.table} DELETE WHERE subject = '${subject.replaceAll('\\', '\\\\').replaceAll("'", "\\'")}'`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=clickhouse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clickhouse.js","sourceRoot":"","sources":["../../../src/sinks/clickhouse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FlagEvent } from '../events/event.js';
|
|
2
|
+
import { type Dialect, type SqlDatabase } from '../sql.js';
|
|
3
|
+
import type { Sink } from './sink.js';
|
|
4
|
+
/**
|
|
5
|
+
* The analysis tables in your database: `flags_exposures` (one row per experiment and subject — the
|
|
6
|
+
* first exposure wins, which is also what sticky assignment reads) and `flags_tracks` (one row per
|
|
7
|
+
* tracked event, deduped by event id). The `lucid` analytics backend reads them.
|
|
8
|
+
*/
|
|
9
|
+
export declare class LucidSink implements Sink {
|
|
10
|
+
readonly db: SqlDatabase;
|
|
11
|
+
private readonly options;
|
|
12
|
+
readonly kind = "lucid";
|
|
13
|
+
readonly dialect: Dialect;
|
|
14
|
+
constructor(db: SqlDatabase, options?: {
|
|
15
|
+
autoCreateSchema?: boolean;
|
|
16
|
+
batchSize?: number;
|
|
17
|
+
intervalMs?: number;
|
|
18
|
+
});
|
|
19
|
+
get batchSize(): number | undefined;
|
|
20
|
+
get intervalMs(): number | undefined;
|
|
21
|
+
setup(): Promise<void>;
|
|
22
|
+
write(events: FlagEvent[]): Promise<void>;
|
|
23
|
+
forget(subject: string): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=lucid_sink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lucid_sink.d.ts","sourceRoot":"","sources":["../../../src/sinks/lucid_sink.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,KAAK,OAAO,EAAsC,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAC/F,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC;;;;GAIG;AACH,qBAAa,SAAU,YAAW,IAAI;IAKlC,QAAQ,CAAC,EAAE,EAAE,WAAW;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B,YACW,EAAE,EAAE,WAAW,EACP,OAAO,GAAE;QACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KAChB,EAGP;IAED,IAAI,SAAS,uBAEZ;IAED,IAAI,UAAU,uBAEb;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAEK,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmE9C;IAEK,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3C;CACF"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createFlagsTables } from '../schema.js';
|
|
2
|
+
import { dialectOf, insertIgnore, jsonValue } from '../sql.js';
|
|
3
|
+
/**
|
|
4
|
+
* The analysis tables in your database: `flags_exposures` (one row per experiment and subject — the
|
|
5
|
+
* first exposure wins, which is also what sticky assignment reads) and `flags_tracks` (one row per
|
|
6
|
+
* tracked event, deduped by event id). The `lucid` analytics backend reads them.
|
|
7
|
+
*/
|
|
8
|
+
export class LucidSink {
|
|
9
|
+
db;
|
|
10
|
+
options;
|
|
11
|
+
kind = 'lucid';
|
|
12
|
+
dialect;
|
|
13
|
+
constructor(db, options = {}) {
|
|
14
|
+
this.db = db;
|
|
15
|
+
this.options = options;
|
|
16
|
+
this.dialect = dialectOf(db);
|
|
17
|
+
}
|
|
18
|
+
get batchSize() {
|
|
19
|
+
return this.options.batchSize;
|
|
20
|
+
}
|
|
21
|
+
get intervalMs() {
|
|
22
|
+
return this.options.intervalMs;
|
|
23
|
+
}
|
|
24
|
+
async setup() {
|
|
25
|
+
if (this.options.autoCreateSchema !== false)
|
|
26
|
+
await createFlagsTables(this.db);
|
|
27
|
+
}
|
|
28
|
+
async write(events) {
|
|
29
|
+
const exposures = events.filter((e) => e.type === 'exposure' && e.experiment && e.variant);
|
|
30
|
+
const tracks = events.filter((e) => e.type === 'track' && e.name);
|
|
31
|
+
await this.db.transaction(async (trx) => {
|
|
32
|
+
for (let i = 0; i < exposures.length; i += 300) {
|
|
33
|
+
const chunk = exposures.slice(i, i + 300);
|
|
34
|
+
await trx.rawQuery(insertIgnore(this.dialect, 'flags_exposures', [
|
|
35
|
+
'environment',
|
|
36
|
+
'experiment',
|
|
37
|
+
'subject',
|
|
38
|
+
'variant',
|
|
39
|
+
'flag',
|
|
40
|
+
'app',
|
|
41
|
+
'subject_type',
|
|
42
|
+
'first_seen_ts',
|
|
43
|
+
], chunk.length), chunk.flatMap((e) => [
|
|
44
|
+
e.environment,
|
|
45
|
+
e.experiment,
|
|
46
|
+
e.subject,
|
|
47
|
+
e.variant,
|
|
48
|
+
e.flag ?? e.experiment,
|
|
49
|
+
e.app,
|
|
50
|
+
e.subjectType,
|
|
51
|
+
e.ts,
|
|
52
|
+
]));
|
|
53
|
+
}
|
|
54
|
+
for (let i = 0; i < tracks.length; i += 300) {
|
|
55
|
+
const chunk = tracks.slice(i, i + 300);
|
|
56
|
+
await trx.rawQuery(insertIgnore(this.dialect, 'flags_tracks', [
|
|
57
|
+
'event_id',
|
|
58
|
+
'environment',
|
|
59
|
+
'name',
|
|
60
|
+
'subject',
|
|
61
|
+
'subject_type',
|
|
62
|
+
'app',
|
|
63
|
+
'value',
|
|
64
|
+
'props',
|
|
65
|
+
'ts',
|
|
66
|
+
], chunk.length), chunk.flatMap((e) => [
|
|
67
|
+
e.id,
|
|
68
|
+
e.environment,
|
|
69
|
+
e.name,
|
|
70
|
+
e.subject,
|
|
71
|
+
e.subjectType,
|
|
72
|
+
e.app,
|
|
73
|
+
e.value ?? null,
|
|
74
|
+
jsonValue(e.props),
|
|
75
|
+
e.ts,
|
|
76
|
+
]));
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async forget(subject) {
|
|
81
|
+
await this.db.transaction(async (trx) => {
|
|
82
|
+
await trx.rawQuery('DELETE FROM flags_exposures WHERE subject = ?', [subject]);
|
|
83
|
+
await trx.rawQuery('DELETE FROM flags_tracks WHERE subject = ?', [subject]);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=lucid_sink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lucid_sink.js","sourceRoot":"","sources":["../../../src/sinks/lucid_sink.ts"],"names":[],"mappings":"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"}
|