Skip to content

Kopexa Resource Names (KRN) - TypeScript implementation following Google's Resource Name Design

License

Notifications You must be signed in to change notification settings

kopexa-grc/krn-js

Repository files navigation

@kopexa/krn - Kopexa Resource Names

Part of the Kopexa GRC Platform

npm version CI License

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/krn

KRN 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/main

Usage

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-1

Extracting 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(); // null

Version Manipulation

const k = KRN.parse("//kopexa.com/frameworks/iso27001");

// Add version
const versioned = k.withVersion("v1.2.3");
// Result: //kopexa.com/frameworks/iso27001@v1.2.3

// Remove version
const k2 = KRN.parse("//kopexa.com/frameworks/iso27001@v1");
const unversioned = k2.withoutVersion();
// Result: //kopexa.com/frameworks/iso27001

Service 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/iso27001

Comparison

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"); // true

Framework 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.0 not pcidssv4.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

About Kopexa

Kopexa is a modern GRC (Governance, Risk, and Compliance) platform that helps organizations manage their compliance requirements efficiently.

License

Copyright (c) Kopexa GRC

Licensed under the Apache License, Version 2.0. See LICENSE for details.

About

Kopexa Resource Names (KRN) - TypeScript implementation following Google's Resource Name Design

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Contributors 2

  •  
  •