npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@spectragraph/memory-store

v0.2.0

Published

A reference implementation and development utility for SpectraGraph that provides CRUD operations, querying, and relationship management for graph data stored entirely in memory.

Readme

SpectraGraph Memory Store

A reference implementation and development utility for SpectraGraph that provides CRUD operations, querying, and relationship management for graph data stored entirely in memory.

Primary Use Cases

1. Reference Implementation Memory Store serves as the canonical example of how to implement the SpectraGraph Store interface correctly. If you're building your own store (database adapter, API client, etc.), use this implementation as your guide for proper schema validation, relationship management, and query handling.

2. Development Stepping Stone Memory Store provides a seamless transition path from development to production. Mock out your data model with JSON fixtures, develop and test your application logic, then swap to a production store when your real backend is ready - with zero code changes to your application layer.

Additional Benefits:

  • Testing: Fast, deterministic test fixtures that reset cleanly between tests
  • Prototyping: Quick demos and MVPs without external dependencies
  • Local Development: Work offline with realistic relational data

Overview

SpectraGraph Memory Store is built around several key principles:

  • Schema-driven: Validates all operations against your SpectraGraph schema
  • Relationship-aware: Automatically maintains bidirectional relationships
  • Query-compatible: Full support for SpectraGraph's query language
  • Memory-efficient: Normalized storage with reference-based relationships

Installation

npm install @spectragraph/memory-store

Core Concepts

Memory Store

The memory store maintains your data in normalized form in memory, with automatic relationship management and validation. All data is stored in a graph structure organized by resource type and ID.

import { createMemoryStore } from "@spectragraph/memory-store";
import { defaultSelectEngine, defaultWhereEngine } from "@spectragraph/core";

const store = createMemoryStore(schema, {
	initialData: existingGraph, // optional
	validator: customValidator, // optional
	selectEngine: defaultSelectEngine, // optional - expression engine for SELECT clauses
	whereEngine: defaultWhereEngine, // optional - expression engine for WHERE clauses
});

Operations

The memory store provides standard CRUD operations plus advanced features:

  • create - Add new resources with automatic ID generation
  • update - Modify existing resources with attribute merging
  • upsert - Create or update based on resource existence
  • delete - Remove resources with relationship cleanup
  • query - Execute SpectraGraph queries against the store
  • merge - Insert complex resource trees with nested relationships

Expression Engines

The memory store uses focused expression engines from SpectraGraph Core to provide different capabilities for different query contexts:

  • SELECT Engine: Full expression capabilities including filtering, aggregations, transformations, and computed fields for SELECT clauses
  • WHERE Engine: Filtering-only operations for WHERE clauses, excluding expensive aggregation operations for performance and security

By default, the memory store uses defaultSelectEngine and defaultWhereEngine from @spectragraph/core. You can provide custom engines in the configuration if needed for specialized use cases.

API Reference

createMemoryStore(schema, config?)

Creates a new in-memory store instance.

Parameters:

  • schema (Schema) - The SpectraGraph schema defining resource types and relationships
  • config.initialData (Graph, optional) - Initial graph data to populate the store
  • config.validator (Ajv, optional) - Custom AJV validator instance
  • config.selectEngine (SelectExpressionEngine, optional) - Expression engine for SELECT clauses
  • config.whereEngine (WhereExpressionEngine, optional) - Expression engine for WHERE clauses

Returns: Memory store instance with CRUD and query operations

import { createMemoryStore } from "@spectragraph/memory-store";

const store = createMemoryStore(schema, {
	initialData: {
		teams: {
			"team-1": {
				type: "teams",
				id: "team-1",
				attributes: { name: "Arizona Bay FC" },
				relationships: { homeMatches: [] },
			},
		},
	},
});

Store Operations

store.create(resource)

Creates a new resource in the store with automatic relationship linking.

Parameters:

  • resource (CreateResource) - The resource to create

Returns: The created normalized resource

const newTeam = store.create({
	type: "teams",
	attributes: {
		name: "Scottsdale Surf",
		city: "Scottsdale",
	},
	relationships: {
		homeField: { type: "fields", id: "field-1" },
	},
});

store.update(resource)

Updates an existing resource, merging attributes and relationships.

Parameters:

  • resource (UpdateResource) - The resource updates to apply

Returns: The updated normalized resource

const updatedTeam = store.update({
	type: "teams",
	id: "team-1",
	attributes: {
		city: "Phoenix", // New attribute
	},
	relationships: {
		homeMatches: [{ type: "matches", id: "match-1" }], // Replace relationship
	},
});

store.upsert(resource)

Creates a resource if it doesn't exist, otherwise updates it.

Parameters:

  • resource (CreateResource | UpdateResource) - The resource to upsert

Returns: The created or updated normalized resource

// Creates if team-2 doesn't exist, updates if it does
const team = store.upsert({
	type: "teams",
	id: "team-2",
	attributes: { name: "Mesa Mariners" },
});

store.delete(resource)

Deletes a resource and cleans up all inverse relationships.

Parameters:

  • resource (DeleteResource) - The resource reference to delete

Returns: The deleted resource reference

store.delete({ type: "teams", id: "team-1" });
// All references to team-1 are automatically removed from related resources

store.query(query)

Executes a SpectraGraph query against the store.

Parameters:

  • query (RootQuery) - The query to execute

Returns: Query results matching the query structure

const results = store.query({
	type: "teams",
	select: {
		name: "name",
		homeMatches: {
			select: ["field", "ageGroup"],
			where: { ageGroup: { $gt: 10 } },
		},
	},
	where: { city: "Phoenix" },
});

store.getOne(type, id)

Retrieves a single resource by type and ID.

Parameters:

  • type (string) - The resource type
  • id (string) - The resource ID

Returns: The normalized resource or null if not found

const team = store.getOne("teams", "team-1");
// Returns: { type: "teams", id: "team-1", attributes: {...}, relationships: {...} }

store.merge(resourceTree)

Inserts a complex resource tree with nested relationships into the store.

Parameters:

  • resourceTree (NormalResourceTree) - The resource tree to merge

Returns: The processed resource tree with all nested resources created/updated

const result = store.merge({
	type: "teams",
	attributes: { name: "Tempe Tidal Wave" },
	relationships: {
		homeMatches: [
			{
				// Nested resource - will be created automatically
				type: "matches",
				attributes: { field: "Tempe Community Center", ageGroup: 12 },
				relationships: {
					awayTeam: { type: "teams", id: "team-2" }, // Reference to existing
				},
			},
		],
	},
});

Advanced Operations

store.linkInverses()

Manually triggers inverse relationship linking across the entire store.

store.linkInverses();
// Ensures all bidirectional relationships are properly connected

Examples

Basic Usage

import { createMemoryStore } from "@spectragraph/memory-store";

// 1. Define your schema (or import from elsewhere)
const schema = {
	resources: {
		teams: {
			attributes: {
				id: { type: "string" },
				name: { type: "string" },
				city: { type: "string" },
			},
			relationships: {
				homeMatches: {
					type: "matches",
					cardinality: "many",
					inverse: "homeTeam",
				},
			},
		},
		matches: {
			attributes: {
				id: { type: "string" },
				field: { type: "string" },
				ageGroup: { type: "integer" },
			},
			relationships: {
				homeTeam: { type: "teams", cardinality: "one", inverse: "homeMatches" },
			},
		},
	},
};

// 2. Create the store
const store = createMemoryStore(schema);

// 3. Create resources
const team = store.create({
	type: "teams",
	attributes: {
		name: "Arizona Bay FC",
		city: "Phoenix",
	},
});

const match = store.create({
	type: "matches",
	attributes: {
		field: "Phoenix Park 1",
		ageGroup: 11,
	},
	relationships: {
		homeTeam: { type: "teams", id: team.id },
	},
});

// 4. Query the data
const results = store.query({
	type: "teams",
	select: {
		name: "name",
		homeMatches: {
			select: ["field", "ageGroup"],
		},
	},
});

console.log(results);
// [{ name: "Arizona Bay FC", homeMatches: [{ field: "Phoenix Park 1", ageGroup: 11 }] }]

Complex Resource Trees

// Create a team with nested matches and relationships
const teamWithMatches = store.merge({
	type: "teams",
	attributes: {
		name: "Scottsdale Surf",
		city: "Scottsdale",
	},
	relationships: {
		homeMatches: [
			{
				type: "matches",
				attributes: {
					field: "Scottsdale Community Center",
					ageGroup: 12,
				},
				relationships: {
					awayTeam: { type: "teams", id: team.id }, // Reference to existing team
				},
			},
			{
				type: "matches",
				attributes: {
					field: "Desert Breeze Park",
					ageGroup: 14,
				},
			},
		],
	},
});

// All relationships are automatically linked bidirectionally
const phoenixTeam = store.getOne("teams", team.id);
console.log(phoenixTeam.relationships.awayMatches.length); // 1

Data Validation

// Validation happens automatically on all operations
try {
	store.create({
		type: "teams",
		attributes: {
			name: 123, // Invalid: should be string
		},
	});
} catch (error) {
	console.error("Validation failed:", error.message);
}

// Custom validator for additional constraints
import { createValidator } from "@spectragraph/core";

const customValidator = createValidator({
	ajvSchemas: [myCustomSchema],
});

const strictStore = createMemoryStore(schema, {
	validator: customValidator,
});

Querying with Filters

// Find all matches for teams from Phoenix
const phoenixMatches = store.query({
	type: "matches",
	select: {
		field: "field",
		ageGroup: "ageGroup",
		homeTeamName: "homeTeam.name",
	},
	where: {
		"homeTeam.city": "Phoenix",
		ageGroup: { $gte: 11 },
	},
	order: [{ ageGroup: "asc" }, { field: "asc" }],
	limit: 10,
});

TypeScript Support

SpectraGraph Memory Store includes comprehensive TypeScript definitions:

import type { MemoryStore, MemoryStoreConfig } from "@spectragraph/memory-store";
import type { Schema, CreateResource } from "@spectragraph/core";

const schema: Schema = {
	resources: {
		teams: {
			attributes: {
				id: { type: "string" },
				name: { type: "string" },
			},
			relationships: {},
		},
	},
};

const store: MemoryStore = createMemoryStore(schema);

const newTeam: CreateResource = {
	type: "teams",
	attributes: { name: "Mesa Mariners" },
};

Production Considerations

Memory Store is designed for development, not production use. Key limitations:

  • Memory growth: No cleanup mechanisms - data accumulates indefinitely
  • Single-process only: All data is lost when the process restarts
  • No persistence: Changes are not saved to disk
  • Performance: O(n) queries without indexing, suitable for small development datasets

For production applications, transition to a persistent store like @spectragraph/postgres-store or implement your own store using this as a reference.

Related Packages

  • @spectragraph/core - Core SpectraGraph functionality and validation
  • @spectragraph/postgres-store - PostgreSQL backend store
  • @spectragraph/jsonapi-store - JSON:API client store