@happyvertical/smrt-tags 0.30.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.
@@ -0,0 +1,110 @@
1
+ import { SmrtHierarchical } from '@happyvertical/smrt-core';
2
+ import { SmrtObjectOptions } from '@happyvertical/smrt-core';
3
+
4
+ declare class Tag extends SmrtHierarchical {
5
+ protected _slug: string;
6
+ protected _context: string;
7
+ get slug(): string;
8
+ set slug(value: string);
9
+ get context(): string;
10
+ set context(value: string);
11
+ name: string;
12
+ level: number;
13
+ description: string;
14
+ metadata: string;
15
+ tenantId: string | null;
16
+ createdAt: Date;
17
+ updatedAt: Date;
18
+ constructor(options?: TagOptions);
19
+ /**
20
+ * Get metadata as parsed object
21
+ *
22
+ * @returns Parsed metadata object or empty object if no metadata
23
+ */
24
+ getMetadata(): TagMetadata;
25
+ /**
26
+ * Set metadata from object
27
+ *
28
+ * @param data - Metadata object to store
29
+ */
30
+ setMetadata(data: TagMetadata): void;
31
+ /**
32
+ * Update metadata by merging with existing values
33
+ *
34
+ * @param updates - Partial metadata to merge
35
+ */
36
+ updateMetadata(updates: Partial<TagMetadata>): void;
37
+ /**
38
+ * Convenience method for slug-based lookup
39
+ *
40
+ * @param slug - The slug to search for
41
+ * @param context - Optional context filter
42
+ * @returns Tag instance or null if not found
43
+ */
44
+ static getBySlug(_slug: string, _context?: string): Promise<Tag | null>;
45
+ /**
46
+ * Get root tags (no parent) for a context
47
+ *
48
+ * @param context - The context to filter by
49
+ * @returns Array of root tags
50
+ */
51
+ static getRootTags(_context?: string): Promise<Tag[]>;
52
+ }
53
+
54
+ /**
55
+ * Options for creating a TagAlias instance
56
+ */
57
+ export declare interface TagAliasOptions extends SmrtObjectOptions {
58
+ tagSlug?: string;
59
+ alias?: string;
60
+ language?: string;
61
+ context?: string;
62
+ tenantId?: string | null;
63
+ }
64
+
65
+ /**
66
+ * Tag hierarchy result structure
67
+ */
68
+ export declare interface TagHierarchy {
69
+ ancestors: Tag[];
70
+ current: Tag;
71
+ descendants: Tag[];
72
+ }
73
+
74
+ /**
75
+ * Tag metadata structure (flexible, application-specific)
76
+ */
77
+ export declare interface TagMetadata {
78
+ color?: string;
79
+ backgroundColor?: string;
80
+ icon?: string;
81
+ emoji?: string;
82
+ usageCount?: number;
83
+ lastUsed?: string;
84
+ trending?: boolean;
85
+ featured?: boolean;
86
+ sortOrder?: number;
87
+ showInNav?: boolean;
88
+ displayFormat?: string;
89
+ aiGenerated?: boolean;
90
+ confidence?: number;
91
+ source?: string;
92
+ reviewStatus?: string;
93
+ [key: string]: any;
94
+ }
95
+
96
+ /**
97
+ * Options for creating a Tag instance
98
+ */
99
+ export declare interface TagOptions extends SmrtObjectOptions {
100
+ slug?: string;
101
+ name?: string;
102
+ context?: string;
103
+ parentId?: string | null;
104
+ level?: number;
105
+ description?: string;
106
+ metadata?: string | Record<string, any>;
107
+ tenantId?: string | null;
108
+ }
109
+
110
+ export { }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,319 @@
1
+ import { SmrtCollection } from '@happyvertical/smrt-core';
2
+ import { SmrtHierarchical } from '@happyvertical/smrt-core';
3
+ import { SmrtObjectOptions } from '@happyvertical/smrt-core';
4
+
5
+ /**
6
+ * Calculate hierarchy level
7
+ *
8
+ * Determines the level (depth) of a tag based on its parent.
9
+ * Root tags have level 0, their children have level 1, etc.
10
+ *
11
+ * @param parentSlug - The parent tag slug (null for root)
12
+ * @param tagCollection - TagCollection instance for queries
13
+ * @returns The calculated level
14
+ */
15
+ export declare function calculateLevel(parentSlug: string | null, tagCollection: TagCollection): Promise<number>;
16
+
17
+ /**
18
+ * Generate a unique slug from a name
19
+ *
20
+ * Creates a slug and ensures uniqueness by appending a number if needed.
21
+ *
22
+ * @param name - The name to convert to slug
23
+ * @param context - The context for uniqueness checking
24
+ * @param tagCollection - TagCollection instance for queries
25
+ * @returns Unique slug
26
+ */
27
+ export declare function generateUniqueSlug(name: string, context: string, tagCollection: TagCollection): Promise<string>;
28
+
29
+ /**
30
+ * Validate hierarchy for circular references
31
+ *
32
+ * Checks if setting a parent would create a circular reference
33
+ * (e.g., making a tag its own ancestor). The actual move call in
34
+ * `TagCollection.moveTag` also runs `SmrtHierarchical.moveTo`'s
35
+ * descendant-cycle check; this helper remains exported for callers that
36
+ * want to pre-validate a candidate parent without attempting the move.
37
+ *
38
+ * Tags are identified by `(slug, context)`. If `context` is omitted,
39
+ * slug-only lookups are used — fine when slugs are unique across all
40
+ * contexts, but the walk can traverse the wrong chain when the same
41
+ * slug exists in multiple contexts. Pass the candidate parent's
42
+ * `context` for accurate cross-context-safe validation.
43
+ *
44
+ * @param slug - The tag being moved
45
+ * @param parentSlug - The proposed new parent
46
+ * @param tagCollection - TagCollection instance for queries
47
+ * @param context - Optional context to scope every slug lookup to
48
+ * @returns True if circular reference detected
49
+ */
50
+ export declare function hasCircularReference(slug: string, parentSlug: string, tagCollection: TagCollection, context?: string): Promise<boolean>;
51
+
52
+ /**
53
+ * Sanitize slug input
54
+ *
55
+ * Converts to lowercase, replaces spaces with hyphens,
56
+ * removes invalid characters, and ensures proper format.
57
+ *
58
+ * @param input - The input string to sanitize
59
+ * @returns Sanitized slug
60
+ */
61
+ export declare function sanitizeSlug(input: string): string;
62
+
63
+ declare class Tag extends SmrtHierarchical {
64
+ protected _slug: string;
65
+ protected _context: string;
66
+ get slug(): string;
67
+ set slug(value: string);
68
+ get context(): string;
69
+ set context(value: string);
70
+ name: string;
71
+ level: number;
72
+ description: string;
73
+ metadata: string;
74
+ tenantId: string | null;
75
+ createdAt: Date;
76
+ updatedAt: Date;
77
+ constructor(options?: TagOptions);
78
+ /**
79
+ * Get metadata as parsed object
80
+ *
81
+ * @returns Parsed metadata object or empty object if no metadata
82
+ */
83
+ getMetadata(): TagMetadata;
84
+ /**
85
+ * Set metadata from object
86
+ *
87
+ * @param data - Metadata object to store
88
+ */
89
+ setMetadata(data: TagMetadata): void;
90
+ /**
91
+ * Update metadata by merging with existing values
92
+ *
93
+ * @param updates - Partial metadata to merge
94
+ */
95
+ updateMetadata(updates: Partial<TagMetadata>): void;
96
+ /**
97
+ * Convenience method for slug-based lookup
98
+ *
99
+ * @param slug - The slug to search for
100
+ * @param context - Optional context filter
101
+ * @returns Tag instance or null if not found
102
+ */
103
+ static getBySlug(_slug: string, _context?: string): Promise<Tag | null>;
104
+ /**
105
+ * Get root tags (no parent) for a context
106
+ *
107
+ * @param context - The context to filter by
108
+ * @returns Array of root tags
109
+ */
110
+ static getRootTags(_context?: string): Promise<Tag[]>;
111
+ }
112
+
113
+ declare class TagCollection extends SmrtCollection<Tag> {
114
+ static readonly _itemClass: typeof Tag;
115
+ /**
116
+ * Get or create a tag with context
117
+ *
118
+ * @param slug - Tag slug
119
+ * @param context - Tag context (default: 'global')
120
+ * @returns Tag instance
121
+ */
122
+ getOrCreate(slug: string, context?: string): Promise<Tag>;
123
+ /**
124
+ * Resolve a tag by slug, optionally scoped to a context.
125
+ *
126
+ * Tags are identified by `(slug, context)`. When `context` is omitted
127
+ * and the slug exists in more than one context, this throws a clear
128
+ * ambiguity error rather than silently picking the first matching row.
129
+ * Callers that know their context should pass it; callers that work in
130
+ * a single-context world can leave it off.
131
+ *
132
+ * @returns The matching Tag, or `null` if nothing matches.
133
+ * @throws Error if `context` is omitted and the slug is ambiguous.
134
+ */
135
+ private resolveBySlug;
136
+ /**
137
+ * List tags by context with optional parent filtering by slug.
138
+ *
139
+ * @param context - The context to filter by
140
+ * @param parentSlug - Optional parent slug to filter children. Pass an
141
+ * empty string or `null` to find root tags; pass a slug to find that
142
+ * tag's immediate children. Typed as `string | null` so TypeScript
143
+ * callers can pass `null` without a cast — the `null` and `''` paths
144
+ * are both treated as "roots only".
145
+ * @returns Array of matching tags
146
+ */
147
+ listByContext(context: string, parentSlug?: string | null): Promise<Tag[]>;
148
+ /**
149
+ * Get root tags (no parent) for a context
150
+ *
151
+ * @param context - The context to filter by (default: 'global')
152
+ * @returns Array of root tags
153
+ */
154
+ getRootTags(context?: string): Promise<Tag[]>;
155
+ /**
156
+ * Get immediate children of a parent tag, looked up by slug.
157
+ *
158
+ * @param parentSlug - The parent tag slug
159
+ * @param context - Optional context for the parent lookup. When omitted,
160
+ * the parent slug must be unambiguous across contexts (throws if not).
161
+ * @returns Array of child tags, or `[]` if the parent slug doesn't
162
+ * resolve. Children are filtered to the resolved parent's context so
163
+ * cross-context children don't leak in.
164
+ */
165
+ getChildren(parentSlug: string, context?: string): Promise<Tag[]>;
166
+ /**
167
+ * Get tag hierarchy (all ancestors and descendants)
168
+ *
169
+ * @param slug - The tag slug
170
+ * @param context - Optional context for the slug lookup. When omitted,
171
+ * the slug must be unambiguous across contexts.
172
+ * @returns Object with ancestors, current tag, and descendants
173
+ */
174
+ getHierarchy(slug: string, context?: string): Promise<TagHierarchy>;
175
+ /**
176
+ * Move a tag to a new parent. Slug-based API; UUIDs resolved internally.
177
+ *
178
+ * Cycle detection is inlined here (mirroring `SmrtHierarchical.moveTo`'s
179
+ * self-loop + descendant checks) so that both `parentId` and the
180
+ * denormalised `level` field can be persisted in a single `save()`.
181
+ * Delegating to `moveTo` would write `parentId` first and `level` in a
182
+ * second save — if the second save failed, the tag would be left with
183
+ * the new parent but a stale level, breaking the depth cache.
184
+ *
185
+ * After the moved tag persists, descendant levels are recalculated
186
+ * recursively via `updateDescendantLevels`.
187
+ *
188
+ * @param slug - The tag to move
189
+ * @param newParentSlug - The new parent slug (null for root)
190
+ * @param context - Optional context. When provided, both source and new
191
+ * parent are resolved within it. When omitted, both slugs must be
192
+ * unambiguous across contexts; the resolver throws otherwise.
193
+ * @throws Error if either slug fails to resolve, if either slug is
194
+ * ambiguous across contexts (no context provided), if source and new
195
+ * parent live in different contexts, or if the move would create a
196
+ * cycle.
197
+ */
198
+ moveTag(slug: string, newParentSlug: string | null, context?: string): Promise<void>;
199
+ /**
200
+ * Merge one tag into another (updates all references)
201
+ *
202
+ * Reparents `fromTag`'s direct children onto `toTag` and recalculates
203
+ * their `level` field plus the level of every descendant — without
204
+ * this, children moved from a different depth would carry stale
205
+ * levels relative to their new parent. `TagAlias.tagSlug` references
206
+ * are also rewritten, then `fromTag` is deleted.
207
+ *
208
+ * Note: Consuming packages are responsible for updating their own
209
+ * join tables (e.g. `asset_tags`).
210
+ *
211
+ * @param fromSlug - The tag to merge from
212
+ * @param toSlug - The tag to merge into
213
+ * @param context - Optional context. When provided, both tags are
214
+ * resolved within it. When omitted, both slugs must be unambiguous
215
+ * across contexts.
216
+ * @throws Error if either slug fails to resolve, if either slug is
217
+ * ambiguous, or if the two tags live in different contexts.
218
+ */
219
+ mergeTag(fromSlug: string, toSlug: string, context?: string): Promise<void>;
220
+ /**
221
+ * Remove tags with no references (cleanup unused tags)
222
+ *
223
+ * Note: This requires consuming packages to provide usage information.
224
+ * By default, only removes tags with no children and no aliases.
225
+ *
226
+ * @param context - Optional context to filter cleanup
227
+ */
228
+ cleanupUnused(context?: string): Promise<number>;
229
+ /**
230
+ * Calculate hierarchy level for a tag, looking the parent up by slug.
231
+ *
232
+ * @param parentSlug - The parent tag slug (null/empty for root)
233
+ * @param context - Optional context for the parent lookup. When
234
+ * omitted, the parent slug must be unambiguous across contexts.
235
+ * @returns The calculated level (root parent → 1, missing parent → 0)
236
+ */
237
+ calculateLevel(parentSlug: string | null, context?: string): Promise<number>;
238
+ /**
239
+ * Update levels for all descendants after moving a tag
240
+ *
241
+ * @param tag - The tag that was moved
242
+ */
243
+ private updateDescendantLevels;
244
+ /**
245
+ * Find all tags belonging to a specific tenant
246
+ *
247
+ * @param tenantId - The tenant ID to filter by
248
+ * @returns Array of tags for the specified tenant
249
+ */
250
+ findByTenant(tenantId: string): Promise<Tag[]>;
251
+ /**
252
+ * Find all global (tenant-less) tags
253
+ *
254
+ * @returns Array of global tags with null tenantId
255
+ */
256
+ findGlobal(): Promise<Tag[]>;
257
+ /**
258
+ * Find tags for a tenant including global tags
259
+ *
260
+ * @param tenantId - The tenant ID to filter by
261
+ * @returns Array of tags for the tenant plus all global tags
262
+ */
263
+ findWithGlobals(tenantId: string): Promise<Tag[]>;
264
+ }
265
+
266
+ /**
267
+ * Tag hierarchy result structure
268
+ */
269
+ declare interface TagHierarchy {
270
+ ancestors: Tag[];
271
+ current: Tag;
272
+ descendants: Tag[];
273
+ }
274
+
275
+ /**
276
+ * Tag metadata structure (flexible, application-specific)
277
+ */
278
+ declare interface TagMetadata {
279
+ color?: string;
280
+ backgroundColor?: string;
281
+ icon?: string;
282
+ emoji?: string;
283
+ usageCount?: number;
284
+ lastUsed?: string;
285
+ trending?: boolean;
286
+ featured?: boolean;
287
+ sortOrder?: number;
288
+ showInNav?: boolean;
289
+ displayFormat?: string;
290
+ aiGenerated?: boolean;
291
+ confidence?: number;
292
+ source?: string;
293
+ reviewStatus?: string;
294
+ [key: string]: any;
295
+ }
296
+
297
+ /**
298
+ * Options for creating a Tag instance
299
+ */
300
+ declare interface TagOptions extends SmrtObjectOptions {
301
+ slug?: string;
302
+ name?: string;
303
+ context?: string;
304
+ parentId?: string | null;
305
+ level?: number;
306
+ description?: string;
307
+ metadata?: string | Record<string, any>;
308
+ tenantId?: string | null;
309
+ }
310
+
311
+ /**
312
+ * Validate slug format (lowercase, alphanumeric + hyphens)
313
+ *
314
+ * @param slug - The slug to validate
315
+ * @returns True if slug is valid
316
+ */
317
+ export declare function validateSlug(slug: string): boolean;
318
+
319
+ export { }
package/dist/utils.js ADDED
@@ -0,0 +1,52 @@
1
+ function validateSlug(slug) {
2
+ const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
3
+ return slugPattern.test(slug);
4
+ }
5
+ function sanitizeSlug(input) {
6
+ return input.toLowerCase().trim().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
7
+ }
8
+ async function hasCircularReference(slug, parentSlug, tagCollection, context) {
9
+ let current = parentSlug;
10
+ const slugWhere = (slugValue) => {
11
+ const w = { slug: slugValue };
12
+ if (context !== void 0) w.context = context;
13
+ return w;
14
+ };
15
+ while (current) {
16
+ if (current === slug) return true;
17
+ const parent = await tagCollection.get(slugWhere(current));
18
+ if (!parent?.parentId) break;
19
+ const grand = await tagCollection.get({ id: parent.parentId });
20
+ current = grand?.slug ?? null;
21
+ }
22
+ return false;
23
+ }
24
+ async function calculateLevel(parentSlug, tagCollection) {
25
+ if (!parentSlug) return 0;
26
+ const parent = await tagCollection.get({ slug: parentSlug });
27
+ if (!parent) return 0;
28
+ return parent.level + 1;
29
+ }
30
+ async function generateUniqueSlug(name, context, tagCollection) {
31
+ const baseSlug = sanitizeSlug(name);
32
+ let slug = baseSlug;
33
+ let counter = 1;
34
+ while (true) {
35
+ const existing = await tagCollection.list({
36
+ where: { slug, context },
37
+ limit: 1
38
+ });
39
+ if (existing.length === 0) break;
40
+ slug = `${baseSlug}-${counter}`;
41
+ counter++;
42
+ }
43
+ return slug;
44
+ }
45
+ export {
46
+ calculateLevel,
47
+ generateUniqueSlug,
48
+ hasCircularReference,
49
+ sanitizeSlug,
50
+ validateSlug
51
+ };
52
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sources":["../src/utils.ts"],"sourcesContent":["/**\n * Utility functions for tag management\n */\n\nimport type { TagCollection } from './tags';\n\n/**\n * Validate slug format (lowercase, alphanumeric + hyphens)\n *\n * @param slug - The slug to validate\n * @returns True if slug is valid\n */\nexport function validateSlug(slug: string): boolean {\n // Slug must be lowercase alphanumeric with hyphens\n const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n return slugPattern.test(slug);\n}\n\n/**\n * Sanitize slug input\n *\n * Converts to lowercase, replaces spaces with hyphens,\n * removes invalid characters, and ensures proper format.\n *\n * @param input - The input string to sanitize\n * @returns Sanitized slug\n */\nexport function sanitizeSlug(input: string): string {\n return input\n .toLowerCase() // Convert to lowercase\n .trim() // Remove leading/trailing whitespace\n .replace(/\\s+/g, '-') // Replace spaces with hyphens\n .replace(/[^a-z0-9-]/g, '') // Remove invalid characters\n .replace(/-+/g, '-') // Replace multiple hyphens with single\n .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens\n}\n\n/**\n * Validate hierarchy for circular references\n *\n * Checks if setting a parent would create a circular reference\n * (e.g., making a tag its own ancestor). The actual move call in\n * `TagCollection.moveTag` also runs `SmrtHierarchical.moveTo`'s\n * descendant-cycle check; this helper remains exported for callers that\n * want to pre-validate a candidate parent without attempting the move.\n *\n * Tags are identified by `(slug, context)`. If `context` is omitted,\n * slug-only lookups are used — fine when slugs are unique across all\n * contexts, but the walk can traverse the wrong chain when the same\n * slug exists in multiple contexts. Pass the candidate parent's\n * `context` for accurate cross-context-safe validation.\n *\n * @param slug - The tag being moved\n * @param parentSlug - The proposed new parent\n * @param tagCollection - TagCollection instance for queries\n * @param context - Optional context to scope every slug lookup to\n * @returns True if circular reference detected\n */\nexport async function hasCircularReference(\n slug: string,\n parentSlug: string,\n tagCollection: TagCollection,\n context?: string,\n): Promise<boolean> {\n // Walk up the proposed parent's chain by slug. Resolve each step to its\n // UUID parent so the iteration matches the new `parentId` FK storage.\n let current: string | null = parentSlug;\n const slugWhere = (slugValue: string): Record<string, string> => {\n const w: Record<string, string> = { slug: slugValue };\n if (context !== undefined) w.context = context;\n return w;\n };\n\n while (current) {\n if (current === slug) return true; // Circular reference found\n\n const parent = await tagCollection.get(slugWhere(current));\n if (!parent?.parentId) break;\n\n // Step to the grandparent by UUID. UUIDs are globally unique, so no\n // context filter is needed here.\n const grand = await tagCollection.get({ id: parent.parentId });\n current = grand?.slug ?? null;\n }\n\n return false;\n}\n\n/**\n * Calculate hierarchy level\n *\n * Determines the level (depth) of a tag based on its parent.\n * Root tags have level 0, their children have level 1, etc.\n *\n * @param parentSlug - The parent tag slug (null for root)\n * @param tagCollection - TagCollection instance for queries\n * @returns The calculated level\n */\nexport async function calculateLevel(\n parentSlug: string | null,\n tagCollection: TagCollection,\n): Promise<number> {\n if (!parentSlug) return 0;\n\n const parent = await tagCollection.get({ slug: parentSlug });\n if (!parent) return 0;\n\n return parent.level + 1;\n}\n\n/**\n * Generate a unique slug from a name\n *\n * Creates a slug and ensures uniqueness by appending a number if needed.\n *\n * @param name - The name to convert to slug\n * @param context - The context for uniqueness checking\n * @param tagCollection - TagCollection instance for queries\n * @returns Unique slug\n */\nexport async function generateUniqueSlug(\n name: string,\n context: string,\n tagCollection: TagCollection,\n): Promise<string> {\n const baseSlug = sanitizeSlug(name);\n let slug = baseSlug;\n let counter = 1;\n\n // Check if slug exists, append number if needed\n while (true) {\n const existing = await tagCollection.list({\n where: { slug, context },\n limit: 1,\n });\n\n if (existing.length === 0) break;\n\n slug = `${baseSlug}-${counter}`;\n counter++;\n }\n\n return slug;\n}\n"],"names":[],"mappings":"AAYO,SAAS,aAAa,MAAuB;AAElD,QAAM,cAAc;AACpB,SAAO,YAAY,KAAK,IAAI;AAC9B;AAWO,SAAS,aAAa,OAAuB;AAClD,SAAO,MACJ,cACA,OACA,QAAQ,QAAQ,GAAG,EACnB,QAAQ,eAAe,EAAE,EACzB,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE;AACzB;AAuBA,eAAsB,qBACpB,MACA,YACA,eACA,SACkB;AAGlB,MAAI,UAAyB;AAC7B,QAAM,YAAY,CAAC,cAA8C;AAC/D,UAAM,IAA4B,EAAE,MAAM,UAAA;AAC1C,QAAI,YAAY,OAAW,GAAE,UAAU;AACvC,WAAO;AAAA,EACT;AAEA,SAAO,SAAS;AACd,QAAI,YAAY,KAAM,QAAO;AAE7B,UAAM,SAAS,MAAM,cAAc,IAAI,UAAU,OAAO,CAAC;AACzD,QAAI,CAAC,QAAQ,SAAU;AAIvB,UAAM,QAAQ,MAAM,cAAc,IAAI,EAAE,IAAI,OAAO,UAAU;AAC7D,cAAU,OAAO,QAAQ;AAAA,EAC3B;AAEA,SAAO;AACT;AAYA,eAAsB,eACpB,YACA,eACiB;AACjB,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,SAAS,MAAM,cAAc,IAAI,EAAE,MAAM,YAAY;AAC3D,MAAI,CAAC,OAAQ,QAAO;AAEpB,SAAO,OAAO,QAAQ;AACxB;AAYA,eAAsB,mBACpB,MACA,SACA,eACiB;AACjB,QAAM,WAAW,aAAa,IAAI;AAClC,MAAI,OAAO;AACX,MAAI,UAAU;AAGd,SAAO,MAAM;AACX,UAAM,WAAW,MAAM,cAAc,KAAK;AAAA,MACxC,OAAO,EAAE,MAAM,QAAA;AAAA,MACf,OAAO;AAAA,IAAA,CACR;AAED,QAAI,SAAS,WAAW,EAAG;AAE3B,WAAO,GAAG,QAAQ,IAAI,OAAO;AAC7B;AAAA,EACF;AAEA,SAAO;AACT;"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@happyvertical/smrt-tags",
3
+ "version": "0.30.0",
4
+ "description": "Reusable tagging system with hierarchies, contexts, and multi-language support for SMRT framework",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "CLAUDE.md",
11
+ "AGENTS.md"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ },
18
+ "./utils": {
19
+ "types": "./dist/utils.d.ts",
20
+ "import": "./dist/utils.js"
21
+ },
22
+ "./manifest": "./dist/manifest.json",
23
+ "./manifest.json": "./dist/manifest.json"
24
+ },
25
+ "dependencies": {
26
+ "@happyvertical/ai": "^0.74.7",
27
+ "@happyvertical/files": "^0.74.7",
28
+ "@happyvertical/logger": "^0.74.7",
29
+ "@happyvertical/sql": "^0.74.7",
30
+ "@happyvertical/utils": "^0.74.7",
31
+ "@happyvertical/smrt-core": "0.30.0",
32
+ "@happyvertical/smrt-tenancy": "0.30.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "25.0.9",
36
+ "typescript": "^5.9.3",
37
+ "vite": "^7.3.1",
38
+ "vitest": "^4.0.17"
39
+ },
40
+ "keywords": [
41
+ "ai",
42
+ "agents",
43
+ "tags",
44
+ "tagging",
45
+ "taxonomy",
46
+ "hierarchy",
47
+ "smrt"
48
+ ],
49
+ "author": "HappyVertical",
50
+ "license": "MIT",
51
+ "publishConfig": {
52
+ "registry": "https://registry.npmjs.org",
53
+ "access": "public"
54
+ },
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/happyvertical/smrt.git",
58
+ "directory": "packages/tags"
59
+ },
60
+ "scripts": {
61
+ "build": "vite build --mode library",
62
+ "build:watch": "vite build --mode library --watch",
63
+ "clean": "rm -rf dist",
64
+ "dev": "vite dev",
65
+ "pretest": "[ -f ../cli/dist/index.js ] && node ../cli/dist/index.js test --manifest-only || true",
66
+ "test": "vitest run",
67
+ "test:watch": "vitest",
68
+ "typecheck": "tsc --noEmit -p tsconfig.json",
69
+ "verify:pack": "node ../../scripts/verify-package-types-exports.js ."
70
+ }
71
+ }