digital-products 2.1.3 → 2.4.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/.turbo/turbo-build.log +4 -5
- package/CHANGELOG.md +17 -0
- package/README.md +2 -0
- package/dist/api.js +7 -7
- package/dist/api.js.map +1 -1
- package/dist/app.js +6 -6
- package/dist/app.js.map +1 -1
- package/dist/client.d.ts +157 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +69 -0
- package/dist/client.js.map +1 -0
- package/dist/content.js +7 -7
- package/dist/content.js.map +1 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +6 -6
- package/dist/data.js.map +1 -1
- package/dist/dataset.js +5 -5
- package/dist/dataset.js.map +1 -1
- package/dist/index.d.ts +92 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +139 -15
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +17 -10
- package/dist/mcp.js.map +1 -1
- package/dist/product.js +2 -2
- package/dist/product.js.map +1 -1
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +52 -16
- package/dist/sdk.js.map +1 -1
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +12 -8
- package/dist/site.js.map +1 -1
- package/dist/types.d.ts +830 -12
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +495 -2
- package/dist/types.js.map +1 -1
- package/dist/worker.d.ts +205 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +356 -0
- package/dist/worker.js.map +1 -0
- package/package.json +29 -13
- package/src/api.ts +7 -7
- package/src/app.ts +6 -6
- package/src/client.ts +192 -0
- package/src/content.ts +7 -7
- package/src/data.ts +12 -7
- package/src/dataset.ts +5 -5
- package/src/index.ts +151 -15
- package/src/mcp.ts +18 -11
- package/src/product.ts +2 -2
- package/src/sdk.ts +54 -15
- package/src/site.ts +12 -8
- package/src/types.ts +821 -12
- package/src/worker.ts +525 -0
- package/test/product.test.ts +53 -198
- package/test/unified-types.test.ts +589 -0
- package/test/worker.test.ts +912 -0
- package/vitest.config.ts +42 -0
- package/wrangler.jsonc +36 -0
- package/LICENSE +0 -21
- package/dist/features/define.d.ts +0 -63
- package/dist/features/define.d.ts.map +0 -1
- package/dist/features/define.js +0 -72
- package/dist/features/define.js.map +0 -1
- package/dist/features/flags.d.ts +0 -98
- package/dist/features/flags.d.ts.map +0 -1
- package/dist/features/flags.js +0 -145
- package/dist/features/flags.js.map +0 -1
- package/dist/features/toggles.d.ts +0 -75
- package/dist/features/toggles.d.ts.map +0 -1
- package/dist/features/toggles.js +0 -107
- package/dist/features/toggles.js.map +0 -1
- package/dist/tiers/define.d.ts +0 -63
- package/dist/tiers/define.d.ts.map +0 -1
- package/dist/tiers/define.js +0 -78
- package/dist/tiers/define.js.map +0 -1
- package/dist/tiers/entitlements.d.ts +0 -94
- package/dist/tiers/entitlements.d.ts.map +0 -1
- package/dist/tiers/entitlements.js +0 -94
- package/dist/tiers/entitlements.js.map +0 -1
- package/src/api.js +0 -128
- package/src/app.js +0 -106
- package/src/content.js +0 -77
- package/src/data.js +0 -106
- package/src/dataset.js +0 -49
- package/src/entities/ai.js +0 -858
- package/src/entities/content.js +0 -783
- package/src/entities/index.js +0 -88
- package/src/entities/interfaces.js +0 -929
- package/src/entities/lifecycle.js +0 -803
- package/src/entities/products.js +0 -797
- package/src/entities/web.js +0 -657
- package/src/features/define.ts +0 -130
- package/src/features/flags.ts +0 -247
- package/src/features/toggles.ts +0 -189
- package/src/index.js +0 -35
- package/src/mcp.js +0 -139
- package/src/pricing/billing.ts +0 -386
- package/src/pricing/plans.ts +0 -214
- package/src/product.js +0 -53
- package/src/registry.js +0 -31
- package/src/sdk.js +0 -127
- package/src/site.js +0 -112
- package/src/tiers/define.ts +0 -137
- package/src/tiers/entitlements.ts +0 -201
- package/src/types.js +0 -4
- package/test/analytics/events.test.ts +0 -319
- package/test/analytics/experiments.test.ts +0 -327
- package/test/features/define.test.ts +0 -187
- package/test/features/flags.test.ts +0 -259
- package/test/features/toggles.test.ts +0 -178
- package/test/lifecycle/stages.test.ts +0 -233
- package/test/lifecycle/transitions.test.ts +0 -207
- package/test/onboarding/flows.test.ts +0 -307
- package/test/pricing/billing.test.ts +0 -287
- package/test/pricing/plans.test.ts +0 -307
- package/test/roadmap/milestones.test.ts +0 -231
- package/test/roadmap/priorities.test.ts +0 -239
- package/test/tiers/define.test.ts +0 -192
- package/test/tiers/entitlements.test.ts +0 -220
package/src/features/define.ts
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Feature Definition
|
|
3
|
-
* Primitives for defining product features
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Feature status options
|
|
8
|
-
*/
|
|
9
|
-
export type FeatureStatus = 'active' | 'beta' | 'deprecated' | 'disabled'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Feature definition
|
|
13
|
-
*/
|
|
14
|
-
export interface FeatureDefinition {
|
|
15
|
-
/** Unique identifier */
|
|
16
|
-
id: string
|
|
17
|
-
/** Human-readable name */
|
|
18
|
-
name: string
|
|
19
|
-
/** Description of the feature */
|
|
20
|
-
description: string
|
|
21
|
-
/** Feature category */
|
|
22
|
-
category?: string
|
|
23
|
-
/** Dependencies on other features */
|
|
24
|
-
dependencies?: string[]
|
|
25
|
-
/** Metadata */
|
|
26
|
-
metadata?: Record<string, unknown>
|
|
27
|
-
/** Feature status */
|
|
28
|
-
status?: FeatureStatus
|
|
29
|
-
/** Version */
|
|
30
|
-
version?: string
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Create a feature definition
|
|
35
|
-
*/
|
|
36
|
-
export function Feature(config: Omit<FeatureDefinition, 'status'> & { status?: FeatureStatus }): FeatureDefinition {
|
|
37
|
-
return {
|
|
38
|
-
id: config.id,
|
|
39
|
-
name: config.name,
|
|
40
|
-
description: config.description,
|
|
41
|
-
category: config.category,
|
|
42
|
-
dependencies: config.dependencies,
|
|
43
|
-
metadata: config.metadata,
|
|
44
|
-
status: config.status ?? 'active',
|
|
45
|
-
version: config.version,
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Create and validate a feature definition
|
|
51
|
-
*/
|
|
52
|
-
export function defineFeature(
|
|
53
|
-
config: Omit<FeatureDefinition, 'status'> & { status?: FeatureStatus }
|
|
54
|
-
): FeatureDefinition {
|
|
55
|
-
if (!config.id || config.id.trim() === '') {
|
|
56
|
-
throw new Error('Feature ID is required')
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return Feature(config)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Feature registry interface
|
|
64
|
-
*/
|
|
65
|
-
export interface FeatureRegistry {
|
|
66
|
-
/** Register a feature */
|
|
67
|
-
register(feature: FeatureDefinition): void
|
|
68
|
-
/** Get a feature by ID */
|
|
69
|
-
get(id: string): FeatureDefinition | undefined
|
|
70
|
-
/** List all features */
|
|
71
|
-
list(): FeatureDefinition[]
|
|
72
|
-
/** List features by category */
|
|
73
|
-
listByCategory(category: string): FeatureDefinition[]
|
|
74
|
-
/** Get dependency graph for a feature */
|
|
75
|
-
getDependencyGraph(featureId: string): string[]
|
|
76
|
-
/** Clear registry */
|
|
77
|
-
clear(): void
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Create a feature registry
|
|
82
|
-
*/
|
|
83
|
-
export function createFeatureRegistry(): FeatureRegistry {
|
|
84
|
-
const features = new Map<string, FeatureDefinition>()
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
register(feature: FeatureDefinition): void {
|
|
88
|
-
features.set(feature.id, feature)
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
get(id: string): FeatureDefinition | undefined {
|
|
92
|
-
return features.get(id)
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
list(): FeatureDefinition[] {
|
|
96
|
-
return Array.from(features.values())
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
listByCategory(category: string): FeatureDefinition[] {
|
|
100
|
-
return Array.from(features.values()).filter((f) => f.category === category)
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
getDependencyGraph(featureId: string): string[] {
|
|
104
|
-
const result: string[] = []
|
|
105
|
-
const visited = new Set<string>()
|
|
106
|
-
|
|
107
|
-
const traverse = (id: string) => {
|
|
108
|
-
if (visited.has(id)) return
|
|
109
|
-
visited.add(id)
|
|
110
|
-
|
|
111
|
-
const feature = features.get(id)
|
|
112
|
-
if (feature?.dependencies) {
|
|
113
|
-
for (const depId of feature.dependencies) {
|
|
114
|
-
traverse(depId)
|
|
115
|
-
if (!result.includes(depId)) {
|
|
116
|
-
result.push(depId)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
traverse(featureId)
|
|
123
|
-
return result
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
clear(): void {
|
|
127
|
-
features.clear()
|
|
128
|
-
},
|
|
129
|
-
}
|
|
130
|
-
}
|
package/src/features/flags.ts
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Feature Flags
|
|
3
|
-
* Runtime feature flag system
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { createHash } from 'crypto'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Flag value types
|
|
10
|
-
*/
|
|
11
|
-
export type FlagType = 'boolean' | 'string' | 'number' | 'json'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Targeting condition
|
|
15
|
-
*/
|
|
16
|
-
export interface TargetingCondition {
|
|
17
|
-
attribute: string
|
|
18
|
-
operator: 'equals' | 'not_equals' | 'in' | 'not_in' | 'gt' | 'lt' | 'gte' | 'lte' | 'contains'
|
|
19
|
-
value: unknown
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Targeting rule
|
|
24
|
-
*/
|
|
25
|
-
export interface TargetingRule {
|
|
26
|
-
condition: TargetingCondition
|
|
27
|
-
value: unknown
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Feature flag definition
|
|
32
|
-
*/
|
|
33
|
-
export interface FeatureFlagDefinition {
|
|
34
|
-
/** Unique key */
|
|
35
|
-
key: string
|
|
36
|
-
/** Human-readable name */
|
|
37
|
-
name: string
|
|
38
|
-
/** Flag type */
|
|
39
|
-
type: FlagType
|
|
40
|
-
/** Default value */
|
|
41
|
-
defaultValue: unknown
|
|
42
|
-
/** Allowed variants (for string type) */
|
|
43
|
-
variants?: string[]
|
|
44
|
-
/** Min value (for number type) */
|
|
45
|
-
min?: number
|
|
46
|
-
/** Max value (for number type) */
|
|
47
|
-
max?: number
|
|
48
|
-
/** Targeting rules */
|
|
49
|
-
rules?: TargetingRule[]
|
|
50
|
-
/** Rollout percentage (0-100) */
|
|
51
|
-
rolloutPercentage?: number
|
|
52
|
-
/** Description */
|
|
53
|
-
description?: string
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Create a feature flag
|
|
58
|
-
*/
|
|
59
|
-
export function FeatureFlag(config: FeatureFlagDefinition): FeatureFlagDefinition {
|
|
60
|
-
return {
|
|
61
|
-
key: config.key,
|
|
62
|
-
name: config.name,
|
|
63
|
-
type: config.type,
|
|
64
|
-
defaultValue: config.defaultValue,
|
|
65
|
-
variants: config.variants,
|
|
66
|
-
min: config.min,
|
|
67
|
-
max: config.max,
|
|
68
|
-
rules: config.rules,
|
|
69
|
-
rolloutPercentage: config.rolloutPercentage,
|
|
70
|
-
description: config.description,
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Create a feature flag (alias)
|
|
76
|
-
*/
|
|
77
|
-
export const createFeatureFlag = FeatureFlag
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Evaluation context for flag evaluation
|
|
81
|
-
*/
|
|
82
|
-
export interface FlagEvaluationContext {
|
|
83
|
-
userId?: string
|
|
84
|
-
sessionId?: string
|
|
85
|
-
attributes?: Record<string, unknown>
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Evaluation stats
|
|
90
|
-
*/
|
|
91
|
-
export interface EvaluationStats {
|
|
92
|
-
totalEvaluations: number
|
|
93
|
-
trueCount: number
|
|
94
|
-
falseCount: number
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Feature flag manager interface
|
|
99
|
-
*/
|
|
100
|
-
export interface FeatureFlagManager {
|
|
101
|
-
/** Register a flag */
|
|
102
|
-
register(flag: FeatureFlagDefinition): void
|
|
103
|
-
/** Get a flag by key */
|
|
104
|
-
get(key: string): FeatureFlagDefinition | undefined
|
|
105
|
-
/** Evaluate a flag */
|
|
106
|
-
evaluate(key: string, context: FlagEvaluationContext): unknown
|
|
107
|
-
/** Get evaluation stats */
|
|
108
|
-
getEvaluationStats(key: string): EvaluationStats
|
|
109
|
-
/** Set override for testing */
|
|
110
|
-
setOverride(key: string, value: unknown): void
|
|
111
|
-
/** Clear override */
|
|
112
|
-
clearOverride(key: string): void
|
|
113
|
-
/** List all flags */
|
|
114
|
-
list(): FeatureFlagDefinition[]
|
|
115
|
-
/** Remove a flag */
|
|
116
|
-
remove(key: string): void
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Create a feature flag manager
|
|
121
|
-
*/
|
|
122
|
-
export function createFeatureFlagManager(): FeatureFlagManager {
|
|
123
|
-
const flags = new Map<string, FeatureFlagDefinition>()
|
|
124
|
-
const overrides = new Map<string, unknown>()
|
|
125
|
-
const stats = new Map<string, EvaluationStats>()
|
|
126
|
-
|
|
127
|
-
const initStats = (key: string) => {
|
|
128
|
-
if (!stats.has(key)) {
|
|
129
|
-
stats.set(key, { totalEvaluations: 0, trueCount: 0, falseCount: 0 })
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const evaluateCondition = (condition: TargetingCondition, context: FlagEvaluationContext): boolean => {
|
|
134
|
-
const attributeValue = context.attributes?.[condition.attribute]
|
|
135
|
-
|
|
136
|
-
switch (condition.operator) {
|
|
137
|
-
case 'equals':
|
|
138
|
-
return attributeValue === condition.value
|
|
139
|
-
case 'not_equals':
|
|
140
|
-
return attributeValue !== condition.value
|
|
141
|
-
case 'in':
|
|
142
|
-
return Array.isArray(condition.value) && condition.value.includes(attributeValue)
|
|
143
|
-
case 'not_in':
|
|
144
|
-
return Array.isArray(condition.value) && !condition.value.includes(attributeValue)
|
|
145
|
-
case 'gt':
|
|
146
|
-
return typeof attributeValue === 'number' && attributeValue > (condition.value as number)
|
|
147
|
-
case 'lt':
|
|
148
|
-
return typeof attributeValue === 'number' && attributeValue < (condition.value as number)
|
|
149
|
-
case 'gte':
|
|
150
|
-
return typeof attributeValue === 'number' && attributeValue >= (condition.value as number)
|
|
151
|
-
case 'lte':
|
|
152
|
-
return typeof attributeValue === 'number' && attributeValue <= (condition.value as number)
|
|
153
|
-
case 'contains':
|
|
154
|
-
return typeof attributeValue === 'string' && attributeValue.includes(condition.value as string)
|
|
155
|
-
default:
|
|
156
|
-
return false
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const hashUserToPercentage = (userId: string, key: string): number => {
|
|
161
|
-
const hash = createHash('md5').update(`${userId}-${key}`).digest('hex')
|
|
162
|
-
const num = parseInt(hash.substring(0, 8), 16)
|
|
163
|
-
return (num % 100) + 1
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
register(flag: FeatureFlagDefinition): void {
|
|
168
|
-
flags.set(flag.key, flag)
|
|
169
|
-
initStats(flag.key)
|
|
170
|
-
},
|
|
171
|
-
|
|
172
|
-
get(key: string): FeatureFlagDefinition | undefined {
|
|
173
|
-
return flags.get(key)
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
evaluate(key: string, context: FlagEvaluationContext): unknown {
|
|
177
|
-
initStats(key)
|
|
178
|
-
const flagStats = stats.get(key)!
|
|
179
|
-
flagStats.totalEvaluations++
|
|
180
|
-
|
|
181
|
-
// Check for override first
|
|
182
|
-
if (overrides.has(key)) {
|
|
183
|
-
const value = overrides.get(key)
|
|
184
|
-
if (value === true) flagStats.trueCount++
|
|
185
|
-
else if (value === false) flagStats.falseCount++
|
|
186
|
-
return value
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const flag = flags.get(key)
|
|
190
|
-
if (!flag) {
|
|
191
|
-
return undefined
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Check targeting rules
|
|
195
|
-
if (flag.rules) {
|
|
196
|
-
for (const rule of flag.rules) {
|
|
197
|
-
if (evaluateCondition(rule.condition, context)) {
|
|
198
|
-
const value = rule.value
|
|
199
|
-
if (value === true) flagStats.trueCount++
|
|
200
|
-
else if (value === false) flagStats.falseCount++
|
|
201
|
-
return value
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Check rollout percentage
|
|
207
|
-
if (flag.rolloutPercentage !== undefined && context.userId) {
|
|
208
|
-
const userPercentage = hashUserToPercentage(context.userId, key)
|
|
209
|
-
if (userPercentage <= flag.rolloutPercentage) {
|
|
210
|
-
if (flag.type === 'boolean') {
|
|
211
|
-
flagStats.trueCount++
|
|
212
|
-
return true
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Return default value
|
|
218
|
-
const value = flag.defaultValue
|
|
219
|
-
if (value === true) flagStats.trueCount++
|
|
220
|
-
else if (value === false) flagStats.falseCount++
|
|
221
|
-
return value
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
getEvaluationStats(key: string): EvaluationStats {
|
|
225
|
-
initStats(key)
|
|
226
|
-
return stats.get(key)!
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
setOverride(key: string, value: unknown): void {
|
|
230
|
-
overrides.set(key, value)
|
|
231
|
-
},
|
|
232
|
-
|
|
233
|
-
clearOverride(key: string): void {
|
|
234
|
-
overrides.delete(key)
|
|
235
|
-
},
|
|
236
|
-
|
|
237
|
-
list(): FeatureFlagDefinition[] {
|
|
238
|
-
return Array.from(flags.values())
|
|
239
|
-
},
|
|
240
|
-
|
|
241
|
-
remove(key: string): void {
|
|
242
|
-
flags.delete(key)
|
|
243
|
-
overrides.delete(key)
|
|
244
|
-
stats.delete(key)
|
|
245
|
-
},
|
|
246
|
-
}
|
|
247
|
-
}
|
package/src/features/toggles.ts
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Feature Toggles
|
|
3
|
-
* Operational feature toggles for release management
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Toggle categories following Martin Fowler's feature toggle taxonomy
|
|
8
|
-
*/
|
|
9
|
-
export type ToggleCategory = 'ops' | 'release' | 'experiment' | 'permission'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Feature toggle definition
|
|
13
|
-
*/
|
|
14
|
-
export interface FeatureToggleDefinition {
|
|
15
|
-
/** Toggle name */
|
|
16
|
-
name: string
|
|
17
|
-
/** Category */
|
|
18
|
-
category: ToggleCategory
|
|
19
|
-
/** Whether toggle is enabled */
|
|
20
|
-
enabled: boolean
|
|
21
|
-
/** Description */
|
|
22
|
-
description?: string
|
|
23
|
-
/** Release date (for release toggles) */
|
|
24
|
-
releaseDate?: Date
|
|
25
|
-
/** Experiment ID (for experiment toggles) */
|
|
26
|
-
experimentId?: string
|
|
27
|
-
/** Required roles (for permission toggles) */
|
|
28
|
-
requiredRoles?: string[]
|
|
29
|
-
/** Expiration date */
|
|
30
|
-
expiresAt?: Date
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Create a feature toggle
|
|
35
|
-
*/
|
|
36
|
-
export function FeatureToggle(config: FeatureToggleDefinition): FeatureToggleDefinition {
|
|
37
|
-
return {
|
|
38
|
-
name: config.name,
|
|
39
|
-
category: config.category,
|
|
40
|
-
enabled: config.enabled,
|
|
41
|
-
description: config.description,
|
|
42
|
-
releaseDate: config.releaseDate,
|
|
43
|
-
experimentId: config.experimentId,
|
|
44
|
-
requiredRoles: config.requiredRoles,
|
|
45
|
-
expiresAt: config.expiresAt,
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Create a toggle (alias)
|
|
51
|
-
*/
|
|
52
|
-
export const createToggle = FeatureToggle
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Toggle evaluation context
|
|
56
|
-
*/
|
|
57
|
-
export interface ToggleContext {
|
|
58
|
-
roles?: string[]
|
|
59
|
-
userId?: string
|
|
60
|
-
attributes?: Record<string, unknown>
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Toggle service interface
|
|
65
|
-
*/
|
|
66
|
-
export interface ToggleService {
|
|
67
|
-
/** Register a toggle */
|
|
68
|
-
register(toggle: FeatureToggleDefinition): void
|
|
69
|
-
/** Check if toggle is enabled */
|
|
70
|
-
isEnabled(name: string, context?: ToggleContext): boolean
|
|
71
|
-
/** Enable a toggle */
|
|
72
|
-
enable(name: string): void
|
|
73
|
-
/** Disable a toggle */
|
|
74
|
-
disable(name: string): void
|
|
75
|
-
/** List toggles by category */
|
|
76
|
-
listByCategory(category: ToggleCategory): FeatureToggleDefinition[]
|
|
77
|
-
/** Bulk enable toggles */
|
|
78
|
-
bulkEnable(names: string[]): void
|
|
79
|
-
/** Bulk disable toggles */
|
|
80
|
-
bulkDisable(names: string[]): void
|
|
81
|
-
/** Export toggle state */
|
|
82
|
-
export(): FeatureToggleDefinition[]
|
|
83
|
-
/** Import toggle state */
|
|
84
|
-
import(toggles: FeatureToggleDefinition[]): void
|
|
85
|
-
/** Get toggle by name */
|
|
86
|
-
get(name: string): FeatureToggleDefinition | undefined
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Create a toggle service
|
|
91
|
-
*/
|
|
92
|
-
export function createToggleService(): ToggleService {
|
|
93
|
-
const toggles = new Map<string, FeatureToggleDefinition>()
|
|
94
|
-
|
|
95
|
-
const checkPermission = (toggle: FeatureToggleDefinition, context?: ToggleContext): boolean => {
|
|
96
|
-
if (toggle.category !== 'permission' || !toggle.requiredRoles) {
|
|
97
|
-
return true
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (!context?.roles) {
|
|
101
|
-
return false
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return toggle.requiredRoles.some((role) => context.roles!.includes(role))
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const checkExpiration = (toggle: FeatureToggleDefinition): boolean => {
|
|
108
|
-
if (!toggle.expiresAt) {
|
|
109
|
-
return true
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return new Date() < toggle.expiresAt
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
register(toggle: FeatureToggleDefinition): void {
|
|
117
|
-
toggles.set(toggle.name, toggle)
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
isEnabled(name: string, context?: ToggleContext): boolean {
|
|
121
|
-
const toggle = toggles.get(name)
|
|
122
|
-
if (!toggle) {
|
|
123
|
-
return false
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Check expiration
|
|
127
|
-
if (!checkExpiration(toggle)) {
|
|
128
|
-
return false
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Check permission for permission toggles
|
|
132
|
-
if (!checkPermission(toggle, context)) {
|
|
133
|
-
return false
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return toggle.enabled
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
enable(name: string): void {
|
|
140
|
-
const toggle = toggles.get(name)
|
|
141
|
-
if (toggle) {
|
|
142
|
-
toggle.enabled = true
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
disable(name: string): void {
|
|
147
|
-
const toggle = toggles.get(name)
|
|
148
|
-
if (toggle) {
|
|
149
|
-
toggle.enabled = false
|
|
150
|
-
}
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
listByCategory(category: ToggleCategory): FeatureToggleDefinition[] {
|
|
154
|
-
return Array.from(toggles.values()).filter((t) => t.category === category)
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
bulkEnable(names: string[]): void {
|
|
158
|
-
for (const name of names) {
|
|
159
|
-
const toggle = toggles.get(name)
|
|
160
|
-
if (toggle) {
|
|
161
|
-
toggle.enabled = true
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
bulkDisable(names: string[]): void {
|
|
167
|
-
for (const name of names) {
|
|
168
|
-
const toggle = toggles.get(name)
|
|
169
|
-
if (toggle) {
|
|
170
|
-
toggle.enabled = false
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
},
|
|
174
|
-
|
|
175
|
-
export(): FeatureToggleDefinition[] {
|
|
176
|
-
return Array.from(toggles.values())
|
|
177
|
-
},
|
|
178
|
-
|
|
179
|
-
import(importedToggles: FeatureToggleDefinition[]): void {
|
|
180
|
-
for (const toggle of importedToggles) {
|
|
181
|
-
toggles.set(toggle.name, toggle)
|
|
182
|
-
}
|
|
183
|
-
},
|
|
184
|
-
|
|
185
|
-
get(name: string): FeatureToggleDefinition | undefined {
|
|
186
|
-
return toggles.get(name)
|
|
187
|
-
},
|
|
188
|
-
}
|
|
189
|
-
}
|
package/src/index.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* digital-products - Primitives for defining and building digital products
|
|
3
|
-
*
|
|
4
|
-
* This package provides primitives for defining digital products:
|
|
5
|
-
* - Apps: Interactive user-facing applications
|
|
6
|
-
* - APIs: Programmatic interfaces
|
|
7
|
-
* - Content: Text/media content with schemas
|
|
8
|
-
* - Data: Structured data definitions
|
|
9
|
-
* - Datasets: Curated data collections
|
|
10
|
-
* - Sites: Websites and documentation
|
|
11
|
-
* - MCPs: Model Context Protocol servers
|
|
12
|
-
* - SDKs: Software development kits
|
|
13
|
-
*
|
|
14
|
-
* @packageDocumentation
|
|
15
|
-
*/
|
|
16
|
-
// Export all types
|
|
17
|
-
export * from './types.js';
|
|
18
|
-
// Export entity definitions (Nouns) as namespace to avoid conflicts with types
|
|
19
|
-
export * as Nouns from './entities/index.js';
|
|
20
|
-
// Also export individual entity collections for convenience
|
|
21
|
-
export { AllDigitalProductEntities, DigitalProductEntityCategories, Entities,
|
|
22
|
-
// Category exports
|
|
23
|
-
ProductEntities, ProductCategories, InterfaceEntities, InterfaceCategories, ContentEntities, ContentCategories, WebEntities, WebCategories, AIEntities, AICategories, LifecycleEntities, LifecycleCategories, } from './entities/index.js';
|
|
24
|
-
// Export registry
|
|
25
|
-
export { registry } from './registry.js';
|
|
26
|
-
// Export product constructors
|
|
27
|
-
export { Product, createProduct, registerProduct } from './product.js';
|
|
28
|
-
export { App, Route, State, Auth } from './app.js';
|
|
29
|
-
export { API, Endpoint, APIAuth, RateLimit } from './api.js';
|
|
30
|
-
export { Content, Workflow } from './content.js';
|
|
31
|
-
export { Data, Index, Relationship, Validate } from './data.js';
|
|
32
|
-
export { Dataset } from './dataset.js';
|
|
33
|
-
export { Site, Nav, SEO, Analytics } from './site.js';
|
|
34
|
-
export { MCP, Tool, Resource, Prompt, MCPConfig } from './mcp.js';
|
|
35
|
-
export { SDK, Export, Example } from './sdk.js';
|