Skip to content
Merged
12 changes: 12 additions & 0 deletions js/.changeset/add-link-interfaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'links-queue-js': minor
---

Add Link and LinkStore interfaces for Phase 1 API contract

- Add `Link` interface with `id`, `source`, `target`, and optional `values` properties
- Add `LinkRef` and `LinkId` types for flexible link referencing
- Add `LinkStore` interface with CRUD operations (create, get, find, update, delete, etc.)
- Add `LinkPattern` interface with `Any` wildcard for pattern matching
- Add utility functions: `isLink`, `isLinkId`, `isLinkRef`, `getLinkId`, `createLink`, `matchesPattern`
- Compatible with links-notation and doublets-rs patterns
167 changes: 151 additions & 16 deletions js/examples/basic-usage.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,162 @@
/**
* Basic usage example
* Demonstrates how to use the package
* Basic usage examples for links-queue
*
* This file demonstrates the core Link and LinkStore interfaces.
*
* Run with any runtime:
* - Node.js: node examples/basic-usage.js
* - Bun: bun examples/basic-usage.js
* - Deno: deno run examples/basic-usage.js
*/

import { add, multiply, delay } from '../src/index.js';
import {
Any,
isLink,
isLinkId,
isLinkRef,
getLinkId,
createLink,
matchesPattern,
} from '../src/index.js';

// =============================================================================
// Creating Links
// =============================================================================

console.log('=== Creating Links ===\n');

// Simple link with numeric IDs
const simpleLink = createLink(1, 2, 3);
console.log('Simple link:', simpleLink);
// Output: { id: 1, source: 2, target: 3 }

// Link with bigint IDs (for large datasets)
const bigintLink = createLink(1n, 9007199254740993n, 9007199254740994n);
console.log('Bigint link:', bigintLink);

// Link with string IDs (for UUID-based systems)
const uuidLink = createLink('link-001', 'source-uuid-123', 'target-uuid-456');
console.log('UUID link:', uuidLink);

// =============================================================================
// Nested Links (Recursive Structures)
// =============================================================================

console.log('\n=== Nested Links ===\n');

// Create nested link structures for representing complex relationships
const personType = createLink(10, 0, 0); // A "type" link
const nameProperty = createLink(11, 0, 0); // A "name" property link
const person = createLink(1, personType, nameProperty);
console.log('Person link with type and name:', person);

// Deep nesting: (1: (2: (3: 30 31)))
const level3 = createLink(3, 30, 31);
const level2 = createLink(2, level3, 21);
const level1 = createLink(1, level2, 11);
console.log('Deeply nested:', level1);
console.log(' Level 1 source ID:', level1.source.id); // 2
console.log(' Level 2 source ID:', level1.source.source.id); // 3

// =============================================================================
// Universal Links (Variable Number of References)
// =============================================================================

console.log('\n=== Universal Links ===\n');

// Universal link with additional values beyond source/target
// Useful for n-ary relationships
const tripleRelation = createLink(
100, // id
1, // source (subject)
2, // target (predicate)
[3, 4, 5] // values (objects)
);
console.log('Triple+ relation:', tripleRelation);

// =============================================================================
// Type Checking
// =============================================================================

console.log('\n=== Type Checking ===\n');

console.log(
'isLink({ id: 1, source: 2, target: 3 }):',
isLink({ id: 1, source: 2, target: 3 })
); // true
console.log('isLink(42):', isLink(42)); // false
console.log('isLink(null):', isLink(null)); // false

console.log('isLinkId(42):', isLinkId(42)); // true
console.log('isLinkId(1n):', isLinkId(1n)); // true
console.log('isLinkId("uuid"):', isLinkId('uuid')); // true
console.log('isLinkId({}):', isLinkId({})); // false

console.log('isLinkRef(42):', isLinkRef(42)); // true
console.log('isLinkRef(simpleLink):', isLinkRef(simpleLink)); // true

// =============================================================================
// Extracting IDs
// =============================================================================

console.log('\n=== Extracting IDs ===\n');

console.log('getLinkId(42):', getLinkId(42)); // 42
console.log('getLinkId(simpleLink):', getLinkId(simpleLink)); // 1

const nestedSource = createLink(5, 50, 51);
const linkWithNestedSource = createLink(1, nestedSource, 10);
console.log(
'getLinkId(linkWithNestedSource.source):',
getLinkId(linkWithNestedSource.source)
); // 5

// =============================================================================
// Pattern Matching
// =============================================================================

console.log('\n=== Pattern Matching ===\n');

const links = [
createLink(1, 10, 20),
createLink(2, 10, 30),
createLink(3, 20, 30),
createLink(4, 30, 40),
];

// Find links with source = 10
const sourcePattern = { source: 10 };
const withSource10 = links.filter((link) =>
matchesPattern(link, sourcePattern)
);
console.log(
'Links with source=10:',
withSource10.map((l) => l.id)
); // [1, 2]

// Find links with target = 30
const targetPattern = { target: 30 };
const withTarget30 = links.filter((link) =>
matchesPattern(link, targetPattern)
);
console.log(
'Links with target=30:',
withTarget30.map((l) => l.id)
); // [2, 3]

// Example: Using add function
console.log('Addition examples:');
console.log(` 2 + 3 = ${add(2, 3)}`);
console.log(` -1 + 5 = ${add(-1, 5)}`);
// Find links with any source and target = 30
const anySourcePattern = { source: Any, target: 30 };
const anySourceTarget30 = links.filter((link) =>
matchesPattern(link, anySourcePattern)
);
console.log(
'Links with Any source, target=30:',
anySourceTarget30.map((l) => l.id)
); // [2, 3]

// Example: Using multiply function
console.log('\nMultiplication examples:');
console.log(` 4 * 5 = ${multiply(4, 5)}`);
console.log(` -2 * 3 = ${multiply(-2, 3)}`);
// Match all links
const allPattern = { source: Any, target: Any };
const allMatched = links.filter((link) => matchesPattern(link, allPattern));
console.log('All links (Any, Any):', allMatched.length); // 4

// Example: Using async delay function
console.log('\nAsync example:');
console.log(' Waiting 100ms...');
await delay(100);
console.log(' Done!');
console.log('\n=== Done ===\n');
160 changes: 147 additions & 13 deletions js/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,161 @@
/**
* Example module type definitions
* Replace this with your actual type definitions
* links-queue - Core type definitions
*
* This module exports the Link and LinkStore interfaces that form
* the API contract for all implementations.
*/

// =============================================================================
// ID and Reference Types
// =============================================================================

/**
* A link identifier. Can be a number, bigint, or string.
*/
export type LinkId = number | bigint | string;

/**
* A reference to a link, which can be an ID or a nested Link object.
*/
export type LinkRef = LinkId | Link;

// =============================================================================
// Link Interface
// =============================================================================

/**
* Represents a link (associative data structure) connecting source to target.
*/
export interface Link {
readonly id: LinkId;
readonly source: LinkRef;
readonly target: LinkRef;
readonly values?: readonly LinkRef[];
}

// =============================================================================
// Pattern Matching
// =============================================================================

/**
* Special value indicating "any" match in pattern queries.
*/
export declare const Any: unique symbol;
export type AnyType = typeof Any;

/**
* Pattern for matching links in queries.
*/
export interface LinkPattern {
readonly id?: LinkId | AnyType;
readonly source?: LinkRef | AnyType;
readonly target?: LinkRef | AnyType;
}

// =============================================================================
// LinkStore Interface
// =============================================================================

/**
* Interface for link storage operations.
*/
export interface LinkStore {
create(source: LinkRef, target: LinkRef): Promise<Link>;
createWithValues(
source: LinkRef,
target: LinkRef,
values: readonly LinkRef[]
): Promise<Link>;
get(id: LinkId): Promise<Link | null>;
exists(id: LinkId): Promise<boolean>;
find(pattern: LinkPattern): Promise<Link[]>;
count(pattern?: LinkPattern): Promise<number>;
update(id: LinkId, source: LinkRef, target: LinkRef): Promise<Link>;
delete(id: LinkId): Promise<boolean>;
deleteMatching(pattern: LinkPattern): Promise<number>;
iterate(pattern?: LinkPattern): AsyncIterable<Link>;
}

// =============================================================================
// Utility Types
// =============================================================================

export type LinkIdOf<L extends Link> = L['id'];

export interface MutableLink {
id: LinkId;
source: LinkRef;
target: LinkRef;
values?: LinkRef[];
}

export interface CreateLinkOptions {
source: LinkRef;
target: LinkRef;
values?: LinkRef[];
}

export type LinkResult<T> =
| { success: true; value: T }
| { success: false; error: string };

// =============================================================================
// Utility Functions
// =============================================================================

/**
* Checks if a value is a Link object.
*/
export declare const isLink: (value: unknown) => value is Link;

/**
* Checks if a value is a valid LinkId.
*/
export declare const isLinkId: (value: unknown) => value is LinkId;

/**
* Checks if a value is a valid LinkRef.
*/
export declare const isLinkRef: (value: unknown) => value is LinkRef;

/**
* Extracts the LinkId from a LinkRef.
*/
export declare const getLinkId: (ref: LinkRef) => LinkId;

/**
* Creates a Link object from source and target references.
*/
export declare const createLink: (
id: LinkId,
source: LinkRef,
target: LinkRef,
values?: LinkRef[]
) => Link;

/**
* Checks if a pattern matches a link.
*/
export declare const matchesPattern: (
link: Link,
pattern: LinkPattern
) => boolean;

// =============================================================================
// Deprecated exports (keep for backward compatibility)
// =============================================================================

/**
* Adds two numbers
* @param a - First number
* @param b - Second number
* @returns Sum of a and b
* @deprecated Use the new Link interface
*/
export declare const add: (a: number, b: number) => number;

/**
* Multiplies two numbers
* @param a - First number
* @param b - Second number
* @returns Product of a and b
* @deprecated Use the new Link interface
*/
export declare const multiply: (a: number, b: number) => number;

/**
* Delays execution for specified milliseconds
* @param ms - Milliseconds to wait
* @returns Promise that resolves after the delay
* @deprecated Will be removed in future versions
*/
export declare const delay: (ms: number) => Promise<void>;
Loading