@kopexa/krn
v1.0.1
Published
Kopexa Resource Names (KRN) - TypeScript implementation
Readme
@kopexa/krn - Kopexa Resource Names
A TypeScript package for working with Kopexa Resource Names (KRN), following Google's Resource Name Design.
Installation
npm install @kopexa/krn
# or
pnpm add @kopexa/krn
# or
yarn add @kopexa/krnKRN Specification
Format
//kopexa.com/{collection}/{resource-id}[/{collection}/{resource-id}][@{version}]
//{service}.kopexa.com/{collection}/{resource-id}[/{collection}/{resource-id}][@{version}]Components
| Component | Description | Example |
|-----------|-------------|---------|
| Service | Optional service subdomain | catalog, isms, policy |
| Domain | Always kopexa.com | kopexa.com |
| Collection | Resource type (plural) | frameworks, controls, tenants |
| Resource ID | Unique identifier | iso27001, 5.1.1, acme-corp |
| Version | Optional version tag | @v1, @v1.2.3, @latest, @draft |
Examples
//kopexa.com/frameworks/iso27001
//kopexa.com/frameworks/iso27001/controls/5.1.1
//catalog.kopexa.com/frameworks/iso27001
//catalog.kopexa.com/frameworks/iso27001/controls/5.1.1@v2
//isms.kopexa.com/tenants/acme-corp/workspaces/mainUsage
Parsing KRNs
import { KRN } from "@kopexa/krn";
// Parse a KRN string
const k = KRN.parse("//kopexa.com/frameworks/iso27001");
// Parse without throwing (returns null on error)
const k2 = KRN.tryParse("//kopexa.com/frameworks/iso27001");
if (k2) {
console.log(k2.basename());
}
// Check if a string is a valid KRN
if (KRN.isValid(s)) {
// ...
}Building KRNs
import { krn } from "@kopexa/krn";
// Build a simple KRN
const k = krn()
.resource("frameworks", "iso27001")
.build();
// Result: //kopexa.com/frameworks/iso27001
// Build a nested KRN with version
const k2 = krn()
.resource("frameworks", "iso27001")
.resource("controls", "a-5-1")
.version("v2")
.build();
// Result: //kopexa.com/frameworks/iso27001/controls/a-5-1@v2
// Build a KRN with service
const k3 = krn()
.service("catalog")
.resource("frameworks", "iso27001")
.build();
// Result: //catalog.kopexa.com/frameworks/iso27001
// Build without throwing (returns null on error)
const k4 = krn().resource("frameworks", "iso27001").tryBuild();Creating Child KRNs
const parent = KRN.parse("//kopexa.com/frameworks/iso27001");
// Create a child KRN
const child = parent.child("controls", "a-5-1");
// Result: //kopexa.com/frameworks/iso27001/controls/a-5-1Extracting Information
const k = KRN.parse("//catalog.kopexa.com/frameworks/iso27001/controls/5.1.1@v1");
k.service; // "catalog"
k.hasService(); // true
k.fullDomain(); // "catalog.kopexa.com"
k.path(); // "frameworks/iso27001/controls/5.1.1"
k.version; // "v1"
k.hasVersion(); // true
k.basename(); // "5.1.1"
k.basenameCollection(); // "controls"
k.depth(); // 2
// Get resource ID by collection
const frameworkId = k.resourceId("frameworks"); // "iso27001"
const controlId = k.tryResourceId("controls"); // "5.1.1" or null
// Check if a collection exists
k.hasResource("frameworks"); // true
k.hasResource("policies"); // false
// Get all segments
for (const seg of k.segments()) {
console.log(`${seg.collection}: ${seg.resourceId}`);
}Working with Parents
const k = KRN.parse("//kopexa.com/frameworks/iso27001/controls/a-5-1");
const parent = k.parent();
// Result: //kopexa.com/frameworks/iso27001
// Root resources return null
const root = KRN.parse("//kopexa.com/frameworks/iso27001");
root.parent(); // nullVersion Manipulation
const k = KRN.parse("//kopexa.com/frameworks/iso27001");
// Add version
const versioned = k.withVersion("v1.2.3");
// Result: //kopexa.com/frameworks/[email protected]
// Remove version
const k2 = KRN.parse("//kopexa.com/frameworks/iso27001@v1");
const unversioned = k2.withoutVersion();
// Result: //kopexa.com/frameworks/iso27001Service Manipulation
const k = KRN.parse("//kopexa.com/frameworks/iso27001");
// Add service
const withService = k.withService("catalog");
// Result: //catalog.kopexa.com/frameworks/iso27001
// Remove service
const k2 = KRN.parse("//catalog.kopexa.com/frameworks/iso27001");
const withoutService = k2.withoutService();
// Result: //kopexa.com/frameworks/iso27001Comparison
const k1 = KRN.parse("//kopexa.com/frameworks/iso27001");
const k2 = KRN.parse("//kopexa.com/frameworks/iso27001");
k1.equals(k2); // true
k1.equalsString("//kopexa.com/frameworks/iso27001"); // trueFramework Versioning
Compliance frameworks often have different editions (e.g., ISO 27001:2013 vs ISO 27001:2022). Include the edition year or version number in the framework resource ID:
// ISO 27001:2022
const k1 = KRN.parse("//catalog.kopexa.com/frameworks/iso27001-2022/controls/5.1.1");
// NIST CSF 2.0
const k2 = KRN.parse("//catalog.kopexa.com/frameworks/nist-csf-2.0/controls/GV.OC-01");
// CIS AWS Benchmark v1.4.0
const k3 = KRN.parse("//catalog.kopexa.com/frameworks/cis-aws-1.4.0/controls/1.1.1");Naming conventions:
- Use lowercase with hyphens:
iso27001-2022,nist-csf-2.0 - Include the year or version:
iso27001-2022,cis-aws-1.4.0 - Keep it readable:
pci-dss-4.0notpcidssv4.0
Note: The @version suffix (e.g., @v1, @draft) is for versioning the KRN content itself
(like draft vs published mappings), not for framework editions.
Utility Functions
import {
getResource,
isValidResourceId,
isValidVersion,
isValidService,
safeResourceId
} from "@kopexa/krn";
// Quick resource extraction from string
const id = getResource("//kopexa.com/frameworks/iso27001", "frameworks");
// Result: "iso27001"
// Validate resource IDs
isValidResourceId("valid-id"); // true
isValidResourceId("-invalid"); // false
// Validate versions
isValidVersion("v1.2.3"); // true
isValidVersion("latest"); // true
isValidVersion("invalid"); // false
// Validate service names
isValidService("catalog"); // true
isValidService("Catalog"); // false (must be lowercase)
isValidService("1svc"); // false (can't start with number)
// Convert strings to valid resource IDs
safeResourceId("Hello World!"); // "Hello-World"Service Name Rules
Service names must follow DNS label rules:
- Length: 1-63 characters
- Allowed characters:
a-z,0-9,- - Must start with a letter
- Cannot end with
-
Resource ID Rules
Resource IDs must follow these rules:
- Length: 1-200 characters
- Allowed characters:
a-z,A-Z,0-9,-,_,. - Cannot start or end with
-or.
Version Formats
Supported version formats:
- Semantic:
v1,v1.2,v1.2.3 - Keywords:
latest,draft
Error Handling
The package exports a KRNError class with error codes:
import { KRN, KRNError, KRNErrorCode } from "@kopexa/krn";
try {
const k = KRN.parse(input);
} catch (err) {
if (err instanceof KRNError) {
switch (err.code) {
case KRNErrorCode.EMPTY_KRN:
// Handle empty input
break;
case KRNErrorCode.INVALID_KRN:
// Handle invalid format
break;
case KRNErrorCode.INVALID_DOMAIN:
// Handle wrong domain
break;
case KRNErrorCode.INVALID_RESOURCE_ID:
// Handle invalid resource ID
break;
case KRNErrorCode.INVALID_VERSION:
// Handle invalid version format
break;
case KRNErrorCode.RESOURCE_NOT_FOUND:
// Handle missing resource
break;
}
}
}Related Packages
- github.com/kopexa-grc/krn - Go implementation
License
Copyright (c) Kopexa GRC
Licensed under the Apache License, Version 2.0. See LICENSE for details.
