Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@plasius/gpu-lock-free-queue

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@plasius/gpu-lock-free-queue - npm Package Compare versions

Comparing version
0.2.13
to
0.2.14
+21
-0
CHANGELOG.md

@@ -23,2 +23,22 @@ # Changelog

## [0.2.14] - 2026-03-14
- **Added**
- `createDagJobGraph(...)` now derives `priorityLanes`, `jobIds`,
`unresolvedDependencyCount`, and `dependentCount` so host code can size
multi-root ready queues and inspect dependency fan-out directly.
- **Changed**
- DAG scheduler docs now describe the host-side graph contract in the same
terms as the GPU ready-queue implementation.
- Updated GitHub Actions workflows to run JavaScript actions on Node 24,
refreshed core workflow action versions, and switched Codecov uploads to
the Codecov CLI.
- **Fixed**
- (placeholder)
- **Security**
- (placeholder)
## [0.2.13] - 2026-03-13

@@ -290,1 +310,2 @@

[0.2.13]: https://github.com/Plasius-LTD/gpu-lock-free-queue/releases/tag/v0.2.13
[0.2.14]: https://github.com/Plasius-LTD/gpu-lock-free-queue/releases/tag/v0.2.14

@@ -58,2 +58,28 @@ // src/index.cjs

}
function buildPriorityLanes(graphJobs) {
const lanes = /* @__PURE__ */ new Map();
for (const job of graphJobs) {
const lane = lanes.get(job.priority) ?? {
priority: job.priority,
jobIds: [],
rootJobIds: []
};
lane.jobIds.push(job.id);
if (job.root) {
lane.rootJobIds.push(job.id);
}
lanes.set(job.priority, lane);
}
return Object.freeze(
[...lanes.values()].sort((left, right) => right.priority - left.priority).map(
(lane) => Object.freeze({
priority: lane.priority,
jobIds: Object.freeze([...lane.jobIds]),
rootJobIds: Object.freeze([...lane.rootJobIds]),
jobCount: lane.jobIds.length,
rootCount: lane.rootJobIds.length
})
)
);
}
async function loadWgslAsset(assetUrl, options = {}) {

@@ -168,5 +194,8 @@ const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};

dependencyCount: job.dependencies.length,
unresolvedDependencyCount: job.dependencies.length,
dependentCount: (dependentsById.get(job.id) ?? []).length,
root: job.dependencies.length === 0
})
);
const priorityLanes = buildPriorityLanes(graphJobs);
return Object.freeze({

@@ -176,4 +205,6 @@ mode: "dag",

maxPriority,
jobIds: Object.freeze(graphJobs.map((job) => job.id)),
roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),
topologicalOrder: Object.freeze(topo),
priorityLanes,
jobs: Object.freeze(graphJobs)

@@ -180,0 +211,0 @@ });

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/index.cjs"],"sourcesContent":["const { pathToFileURL, fileURLToPath } = require(\"node:url\");\nconst { readFile } = require(\"node:fs/promises\");\n\nconst queueWgslUrl = new URL(\"./queue.wgsl\", pathToFileURL(__filename));\nconst dagQueueWgslUrl = new URL(\"./dag-queue.wgsl\", pathToFileURL(__filename));\nconst schedulerModes = Object.freeze([\"flat\", \"dag\"]);\nconst IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/u;\n\nfunction assertSchedulerMode(mode) {\n const resolved = mode ?? \"flat\";\n if (!schedulerModes.includes(resolved)) {\n throw new Error(`mode must be one of: ${schedulerModes.join(\", \")}.`);\n }\n return resolved;\n}\n\nfunction assertIdentifier(name, value) {\n if (typeof value !== \"string\" || !IDENTIFIER_PATTERN.test(value)) {\n throw new Error(\n `${name} must match ${IDENTIFIER_PATTERN.toString()} and be at most 64 characters long.`\n );\n }\n return value;\n}\n\nfunction readNonNegativeInteger(name, value) {\n if (value === undefined) {\n return 0;\n }\n if (\n typeof value !== \"number\" ||\n !Number.isInteger(value) ||\n value < 0 ||\n !Number.isFinite(value)\n ) {\n throw new Error(`${name} must be a non-negative integer.`);\n }\n return value;\n}\n\nfunction normalizeDependencies(name, value) {\n if (value === undefined) {\n return [];\n }\n if (!Array.isArray(value)) {\n throw new Error(`${name} must be an array of dependency ids.`);\n }\n return [...new Set(value.map((entry, index) =>\n assertIdentifier(`${name}[${index}]`, entry)\n ))];\n}\n\nfunction resolveGraphJobId(job, index) {\n if (typeof job.id === \"string\") {\n return assertIdentifier(`jobs[${index}].id`, job.id);\n }\n if (typeof job.label === \"string\") {\n return assertIdentifier(`jobs[${index}].label`, job.label);\n }\n if (typeof job.key === \"string\") {\n return assertIdentifier(`jobs[${index}].key`, job.key);\n }\n if (typeof job.jobType === \"string\") {\n return assertIdentifier(`jobs[${index}].jobType`, job.jobType);\n }\n return assertIdentifier(`jobs[${index}].id`, `job-${index}`);\n}\n\nasync function loadWgslAsset(assetUrl, options = {}) {\n const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};\n const wgslUrl = url instanceof URL ? url : new URL(url, assetUrl);\n\n if (!fetcher || wgslUrl.protocol === \"file:\") {\n return readFile(fileURLToPath(wgslUrl), \"utf8\");\n }\n\n const response = await fetcher(wgslUrl);\n if (!response.ok) {\n const status = \"status\" in response ? response.status : \"unknown\";\n const statusText = \"statusText\" in response ? response.statusText : \"\";\n const detail = statusText ? `${status} ${statusText}` : `${status}`;\n throw new Error(`Failed to load WGSL (${detail})`);\n }\n return response.text();\n}\n\nasync function loadQueueWgsl(options = {}) {\n return loadWgslAsset(queueWgslUrl, options);\n}\n\nasync function loadDagQueueWgsl(options = {}) {\n return loadWgslAsset(dagQueueWgslUrl, options);\n}\n\nasync function loadSchedulerWgsl(options = {}) {\n const { mode = \"flat\", ...rest } = options ?? {};\n const resolvedMode = assertSchedulerMode(mode);\n if (resolvedMode === \"dag\") {\n return loadDagQueueWgsl(rest);\n }\n return loadQueueWgsl(rest);\n}\n\nfunction createDagJobGraph(jobs = []) {\n if (!Array.isArray(jobs)) {\n throw new Error(\"jobs must be an array.\");\n }\n\n const normalized = jobs.map((job, index) => {\n if (!job || typeof job !== \"object\" || Array.isArray(job)) {\n throw new Error(`jobs[${index}] must be an object.`);\n }\n\n const id = resolveGraphJobId(job, index);\n const dependencies = normalizeDependencies(\n `jobs[${index}].dependencies`,\n job.dependencies ?? job.dependsOn\n );\n const priority = readNonNegativeInteger(\n `jobs[${index}].priority`,\n job.priority\n );\n\n return {\n id,\n key: typeof job.key === \"string\" ? job.key : undefined,\n label: typeof job.label === \"string\" ? job.label : undefined,\n jobType: job.jobType,\n queueClass: typeof job.queueClass === \"string\" ? job.queueClass : undefined,\n priority,\n dependencies,\n };\n });\n\n const ids = new Set();\n for (const job of normalized) {\n if (ids.has(job.id)) {\n throw new Error(`Duplicate DAG job id detected: ${job.id}`);\n }\n ids.add(job.id);\n }\n\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n if (!ids.has(dependency)) {\n throw new Error(\n `Job \"${job.id}\" depends on unknown job \"${dependency}\".`\n );\n }\n if (dependency === job.id) {\n throw new Error(`Job \"${job.id}\" cannot depend on itself.`);\n }\n }\n }\n\n const dependentsById = new Map(normalized.map((job) => [job.id, []]));\n const indegree = new Map(normalized.map((job) => [job.id, job.dependencies.length]));\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n dependentsById.get(dependency).push(job.id);\n }\n }\n\n const ready = normalized\n .filter((job) => job.dependencies.length === 0)\n .map((job) => job.id);\n const topo = [];\n const queue = [...ready];\n while (queue.length > 0) {\n const currentId = queue.shift();\n topo.push(currentId);\n for (const dependentId of dependentsById.get(currentId) ?? []) {\n const next = (indegree.get(dependentId) ?? 0) - 1;\n indegree.set(dependentId, next);\n if (next === 0) {\n queue.push(dependentId);\n }\n }\n }\n\n if (topo.length !== normalized.length) {\n throw new Error(\"DAG graph contains a cycle.\");\n }\n\n const maxPriority = normalized.reduce(\n (current, job) => Math.max(current, job.priority),\n 0\n );\n\n const graphJobs = normalized.map((job) =>\n Object.freeze({\n ...job,\n dependencies: Object.freeze([...job.dependencies]),\n dependents: Object.freeze([...(dependentsById.get(job.id) ?? [])]),\n dependencyCount: job.dependencies.length,\n root: job.dependencies.length === 0,\n })\n );\n\n return Object.freeze({\n mode: \"dag\",\n jobCount: graphJobs.length,\n maxPriority,\n roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),\n topologicalOrder: Object.freeze(topo),\n jobs: Object.freeze(graphJobs),\n });\n}\n\nmodule.exports = {\n queueWgslUrl,\n dagQueueWgslUrl,\n schedulerModes,\n loadQueueWgsl,\n loadDagQueueWgsl,\n loadSchedulerWgsl,\n createDagJobGraph,\n};\n"],"mappings":"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","names":[]}
{"version":3,"sources":["../src/index.cjs"],"sourcesContent":["const { pathToFileURL, fileURLToPath } = require(\"node:url\");\nconst { readFile } = require(\"node:fs/promises\");\n\nconst queueWgslUrl = new URL(\"./queue.wgsl\", pathToFileURL(__filename));\nconst dagQueueWgslUrl = new URL(\"./dag-queue.wgsl\", pathToFileURL(__filename));\nconst schedulerModes = Object.freeze([\"flat\", \"dag\"]);\nconst IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/u;\n\nfunction assertSchedulerMode(mode) {\n const resolved = mode ?? \"flat\";\n if (!schedulerModes.includes(resolved)) {\n throw new Error(`mode must be one of: ${schedulerModes.join(\", \")}.`);\n }\n return resolved;\n}\n\nfunction assertIdentifier(name, value) {\n if (typeof value !== \"string\" || !IDENTIFIER_PATTERN.test(value)) {\n throw new Error(\n `${name} must match ${IDENTIFIER_PATTERN.toString()} and be at most 64 characters long.`\n );\n }\n return value;\n}\n\nfunction readNonNegativeInteger(name, value) {\n if (value === undefined) {\n return 0;\n }\n if (\n typeof value !== \"number\" ||\n !Number.isInteger(value) ||\n value < 0 ||\n !Number.isFinite(value)\n ) {\n throw new Error(`${name} must be a non-negative integer.`);\n }\n return value;\n}\n\nfunction normalizeDependencies(name, value) {\n if (value === undefined) {\n return [];\n }\n if (!Array.isArray(value)) {\n throw new Error(`${name} must be an array of dependency ids.`);\n }\n return [...new Set(value.map((entry, index) =>\n assertIdentifier(`${name}[${index}]`, entry)\n ))];\n}\n\nfunction resolveGraphJobId(job, index) {\n if (typeof job.id === \"string\") {\n return assertIdentifier(`jobs[${index}].id`, job.id);\n }\n if (typeof job.label === \"string\") {\n return assertIdentifier(`jobs[${index}].label`, job.label);\n }\n if (typeof job.key === \"string\") {\n return assertIdentifier(`jobs[${index}].key`, job.key);\n }\n if (typeof job.jobType === \"string\") {\n return assertIdentifier(`jobs[${index}].jobType`, job.jobType);\n }\n return assertIdentifier(`jobs[${index}].id`, `job-${index}`);\n}\n\nfunction buildPriorityLanes(graphJobs) {\n const lanes = new Map();\n\n for (const job of graphJobs) {\n const lane = lanes.get(job.priority) ?? {\n priority: job.priority,\n jobIds: [],\n rootJobIds: [],\n };\n lane.jobIds.push(job.id);\n if (job.root) {\n lane.rootJobIds.push(job.id);\n }\n lanes.set(job.priority, lane);\n }\n\n return Object.freeze(\n [...lanes.values()]\n .sort((left, right) => right.priority - left.priority)\n .map((lane) =>\n Object.freeze({\n priority: lane.priority,\n jobIds: Object.freeze([...lane.jobIds]),\n rootJobIds: Object.freeze([...lane.rootJobIds]),\n jobCount: lane.jobIds.length,\n rootCount: lane.rootJobIds.length,\n })\n )\n );\n}\n\nasync function loadWgslAsset(assetUrl, options = {}) {\n const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};\n const wgslUrl = url instanceof URL ? url : new URL(url, assetUrl);\n\n if (!fetcher || wgslUrl.protocol === \"file:\") {\n return readFile(fileURLToPath(wgslUrl), \"utf8\");\n }\n\n const response = await fetcher(wgslUrl);\n if (!response.ok) {\n const status = \"status\" in response ? response.status : \"unknown\";\n const statusText = \"statusText\" in response ? response.statusText : \"\";\n const detail = statusText ? `${status} ${statusText}` : `${status}`;\n throw new Error(`Failed to load WGSL (${detail})`);\n }\n return response.text();\n}\n\nasync function loadQueueWgsl(options = {}) {\n return loadWgslAsset(queueWgslUrl, options);\n}\n\nasync function loadDagQueueWgsl(options = {}) {\n return loadWgslAsset(dagQueueWgslUrl, options);\n}\n\nasync function loadSchedulerWgsl(options = {}) {\n const { mode = \"flat\", ...rest } = options ?? {};\n const resolvedMode = assertSchedulerMode(mode);\n if (resolvedMode === \"dag\") {\n return loadDagQueueWgsl(rest);\n }\n return loadQueueWgsl(rest);\n}\n\nfunction createDagJobGraph(jobs = []) {\n if (!Array.isArray(jobs)) {\n throw new Error(\"jobs must be an array.\");\n }\n\n const normalized = jobs.map((job, index) => {\n if (!job || typeof job !== \"object\" || Array.isArray(job)) {\n throw new Error(`jobs[${index}] must be an object.`);\n }\n\n const id = resolveGraphJobId(job, index);\n const dependencies = normalizeDependencies(\n `jobs[${index}].dependencies`,\n job.dependencies ?? job.dependsOn\n );\n const priority = readNonNegativeInteger(\n `jobs[${index}].priority`,\n job.priority\n );\n\n return {\n id,\n key: typeof job.key === \"string\" ? job.key : undefined,\n label: typeof job.label === \"string\" ? job.label : undefined,\n jobType: job.jobType,\n queueClass: typeof job.queueClass === \"string\" ? job.queueClass : undefined,\n priority,\n dependencies,\n };\n });\n\n const ids = new Set();\n for (const job of normalized) {\n if (ids.has(job.id)) {\n throw new Error(`Duplicate DAG job id detected: ${job.id}`);\n }\n ids.add(job.id);\n }\n\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n if (!ids.has(dependency)) {\n throw new Error(\n `Job \"${job.id}\" depends on unknown job \"${dependency}\".`\n );\n }\n if (dependency === job.id) {\n throw new Error(`Job \"${job.id}\" cannot depend on itself.`);\n }\n }\n }\n\n const dependentsById = new Map(normalized.map((job) => [job.id, []]));\n const indegree = new Map(normalized.map((job) => [job.id, job.dependencies.length]));\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n dependentsById.get(dependency).push(job.id);\n }\n }\n\n const ready = normalized\n .filter((job) => job.dependencies.length === 0)\n .map((job) => job.id);\n const topo = [];\n const queue = [...ready];\n while (queue.length > 0) {\n const currentId = queue.shift();\n topo.push(currentId);\n for (const dependentId of dependentsById.get(currentId) ?? []) {\n const next = (indegree.get(dependentId) ?? 0) - 1;\n indegree.set(dependentId, next);\n if (next === 0) {\n queue.push(dependentId);\n }\n }\n }\n\n if (topo.length !== normalized.length) {\n throw new Error(\"DAG graph contains a cycle.\");\n }\n\n const maxPriority = normalized.reduce(\n (current, job) => Math.max(current, job.priority),\n 0\n );\n\n const graphJobs = normalized.map((job) =>\n Object.freeze({\n ...job,\n dependencies: Object.freeze([...job.dependencies]),\n dependents: Object.freeze([...(dependentsById.get(job.id) ?? [])]),\n dependencyCount: job.dependencies.length,\n unresolvedDependencyCount: job.dependencies.length,\n dependentCount: (dependentsById.get(job.id) ?? []).length,\n root: job.dependencies.length === 0,\n })\n );\n\n const priorityLanes = buildPriorityLanes(graphJobs);\n\n return Object.freeze({\n mode: \"dag\",\n jobCount: graphJobs.length,\n maxPriority,\n jobIds: Object.freeze(graphJobs.map((job) => job.id)),\n roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),\n topologicalOrder: Object.freeze(topo),\n priorityLanes,\n jobs: Object.freeze(graphJobs),\n });\n}\n\nmodule.exports = {\n queueWgslUrl,\n dagQueueWgslUrl,\n schedulerModes,\n loadQueueWgsl,\n loadDagQueueWgsl,\n loadSchedulerWgsl,\n createDagJobGraph,\n};\n"],"mappings":"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","names":[]}

@@ -58,2 +58,28 @@ // src/index.js

}
function buildPriorityLanes(graphJobs) {
const lanes = /* @__PURE__ */ new Map();
for (const job of graphJobs) {
const lane = lanes.get(job.priority) ?? {
priority: job.priority,
jobIds: [],
rootJobIds: []
};
lane.jobIds.push(job.id);
if (job.root) {
lane.rootJobIds.push(job.id);
}
lanes.set(job.priority, lane);
}
return Object.freeze(
[...lanes.values()].sort((left, right) => right.priority - left.priority).map(
(lane) => Object.freeze({
priority: lane.priority,
jobIds: Object.freeze([...lane.jobIds]),
rootJobIds: Object.freeze([...lane.rootJobIds]),
jobCount: lane.jobIds.length,
rootCount: lane.rootJobIds.length
})
)
);
}
async function loadWgslAsset(assetUrl, options = {}) {

@@ -170,5 +196,8 @@ const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};

dependencyCount: job.dependencies.length,
unresolvedDependencyCount: job.dependencies.length,
dependentCount: (dependentsById.get(job.id) ?? []).length,
root: job.dependencies.length === 0
})
);
const priorityLanes = buildPriorityLanes(graphJobs);
return Object.freeze({

@@ -178,4 +207,6 @@ mode: "dag",

maxPriority,
jobIds: Object.freeze(graphJobs.map((job) => job.id)),
roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),
topologicalOrder: Object.freeze(topo),
priorityLanes,
jobs: Object.freeze(graphJobs)

@@ -182,0 +213,0 @@ });

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/index.js"],"sourcesContent":["export const queueWgslUrl = new URL(\"./queue.wgsl\", import.meta.url);\nexport const dagQueueWgslUrl = new URL(\"./dag-queue.wgsl\", import.meta.url);\nexport const schedulerModes = Object.freeze([\"flat\", \"dag\"]);\n\nconst IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/u;\n\nfunction assertSchedulerMode(mode) {\n const resolved = mode ?? \"flat\";\n if (!schedulerModes.includes(resolved)) {\n throw new Error(\n `mode must be one of: ${schedulerModes.join(\", \")}.`\n );\n }\n return resolved;\n}\n\nfunction assertIdentifier(name, value) {\n if (typeof value !== \"string\" || !IDENTIFIER_PATTERN.test(value)) {\n throw new Error(\n `${name} must match ${IDENTIFIER_PATTERN.toString()} and be at most 64 characters long.`\n );\n }\n return value;\n}\n\nfunction readNonNegativeInteger(name, value) {\n if (value === undefined) {\n return 0;\n }\n if (\n typeof value !== \"number\" ||\n !Number.isInteger(value) ||\n value < 0 ||\n !Number.isFinite(value)\n ) {\n throw new Error(`${name} must be a non-negative integer.`);\n }\n return value;\n}\n\nfunction normalizeDependencies(name, value) {\n if (value === undefined) {\n return [];\n }\n if (!Array.isArray(value)) {\n throw new Error(`${name} must be an array of dependency ids.`);\n }\n return [...new Set(value.map((entry, index) =>\n assertIdentifier(`${name}[${index}]`, entry)\n ))];\n}\n\nfunction resolveGraphJobId(job, index) {\n if (typeof job.id === \"string\") {\n return assertIdentifier(`jobs[${index}].id`, job.id);\n }\n if (typeof job.label === \"string\") {\n return assertIdentifier(`jobs[${index}].label`, job.label);\n }\n if (typeof job.key === \"string\") {\n return assertIdentifier(`jobs[${index}].key`, job.key);\n }\n if (typeof job.jobType === \"string\") {\n return assertIdentifier(`jobs[${index}].jobType`, job.jobType);\n }\n return assertIdentifier(`jobs[${index}].id`, `job-${index}`);\n}\n\nasync function loadWgslAsset(assetUrl, options = {}) {\n const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};\n const wgslUrl = url instanceof URL ? url : new URL(url, assetUrl);\n\n if (!fetcher || wgslUrl.protocol === \"file:\") {\n const { readFile } = await import(\"node:fs/promises\");\n const { fileURLToPath } = await import(\"node:url\");\n return readFile(fileURLToPath(wgslUrl), \"utf8\");\n }\n\n const response = await fetcher(wgslUrl);\n if (!response.ok) {\n const status = \"status\" in response ? response.status : \"unknown\";\n const statusText = \"statusText\" in response ? response.statusText : \"\";\n const detail = statusText ? `${status} ${statusText}` : `${status}`;\n throw new Error(`Failed to load WGSL (${detail})`);\n }\n return response.text();\n}\n\nexport async function loadQueueWgsl(options = {}) {\n return loadWgslAsset(queueWgslUrl, options);\n}\n\nexport async function loadDagQueueWgsl(options = {}) {\n return loadWgslAsset(dagQueueWgslUrl, options);\n}\n\nexport async function loadSchedulerWgsl(options = {}) {\n const { mode = \"flat\", ...rest } = options ?? {};\n const resolvedMode = assertSchedulerMode(mode);\n if (resolvedMode === \"dag\") {\n return loadDagQueueWgsl(rest);\n }\n return loadQueueWgsl(rest);\n}\n\nexport function createDagJobGraph(jobs = []) {\n if (!Array.isArray(jobs)) {\n throw new Error(\"jobs must be an array.\");\n }\n\n const normalized = jobs.map((job, index) => {\n if (!job || typeof job !== \"object\" || Array.isArray(job)) {\n throw new Error(`jobs[${index}] must be an object.`);\n }\n\n const id = resolveGraphJobId(job, index);\n const dependencies = normalizeDependencies(\n `jobs[${index}].dependencies`,\n job.dependencies ?? job.dependsOn\n );\n const priority = readNonNegativeInteger(\n `jobs[${index}].priority`,\n job.priority\n );\n\n return {\n id,\n key: typeof job.key === \"string\" ? job.key : undefined,\n label: typeof job.label === \"string\" ? job.label : undefined,\n jobType: job.jobType,\n queueClass: typeof job.queueClass === \"string\" ? job.queueClass : undefined,\n priority,\n dependencies,\n };\n });\n\n const ids = new Set();\n for (const job of normalized) {\n if (ids.has(job.id)) {\n throw new Error(`Duplicate DAG job id detected: ${job.id}`);\n }\n ids.add(job.id);\n }\n\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n if (!ids.has(dependency)) {\n throw new Error(\n `Job \"${job.id}\" depends on unknown job \"${dependency}\".`\n );\n }\n if (dependency === job.id) {\n throw new Error(`Job \"${job.id}\" cannot depend on itself.`);\n }\n }\n }\n\n const dependentsById = new Map(normalized.map((job) => [job.id, []]));\n const indegree = new Map(normalized.map((job) => [job.id, job.dependencies.length]));\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n dependentsById.get(dependency).push(job.id);\n }\n }\n\n const ready = normalized\n .filter((job) => job.dependencies.length === 0)\n .map((job) => job.id);\n const topo = [];\n const queue = [...ready];\n while (queue.length > 0) {\n const currentId = queue.shift();\n topo.push(currentId);\n for (const dependentId of dependentsById.get(currentId) ?? []) {\n const next = (indegree.get(dependentId) ?? 0) - 1;\n indegree.set(dependentId, next);\n if (next === 0) {\n queue.push(dependentId);\n }\n }\n }\n\n if (topo.length !== normalized.length) {\n throw new Error(\"DAG graph contains a cycle.\");\n }\n\n const maxPriority = normalized.reduce(\n (current, job) => Math.max(current, job.priority),\n 0\n );\n\n const graphJobs = normalized.map((job) =>\n Object.freeze({\n ...job,\n dependencies: Object.freeze([...job.dependencies]),\n dependents: Object.freeze([...(dependentsById.get(job.id) ?? [])]),\n dependencyCount: job.dependencies.length,\n root: job.dependencies.length === 0,\n })\n );\n\n return Object.freeze({\n mode: \"dag\",\n jobCount: graphJobs.length,\n maxPriority,\n roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),\n topologicalOrder: Object.freeze(topo),\n jobs: Object.freeze(graphJobs),\n });\n}\n"],"mappings":";AAAO,IAAM,eAAe,IAAI,IAAI,gBAAgB,YAAY,GAAG;AAC5D,IAAM,kBAAkB,IAAI,IAAI,oBAAoB,YAAY,GAAG;AACnE,IAAM,iBAAiB,OAAO,OAAO,CAAC,QAAQ,KAAK,CAAC;AAE3D,IAAM,qBAAqB;AAE3B,SAAS,oBAAoB,MAAM;AACjC,QAAM,WAAW,QAAQ;AACzB,MAAI,CAAC,eAAe,SAAS,QAAQ,GAAG;AACtC,UAAM,IAAI;AAAA,MACR,wBAAwB,eAAe,KAAK,IAAI,CAAC;AAAA,IACnD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,MAAM,OAAO;AACrC,MAAI,OAAO,UAAU,YAAY,CAAC,mBAAmB,KAAK,KAAK,GAAG;AAChE,UAAM,IAAI;AAAA,MACR,GAAG,IAAI,eAAe,mBAAmB,SAAS,CAAC;AAAA,IACrD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,uBAAuB,MAAM,OAAO;AAC3C,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AACA,MACE,OAAO,UAAU,YACjB,CAAC,OAAO,UAAU,KAAK,KACvB,QAAQ,KACR,CAAC,OAAO,SAAS,KAAK,GACtB;AACA,UAAM,IAAI,MAAM,GAAG,IAAI,kCAAkC;AAAA,EAC3D;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,MAAM,OAAO;AAC1C,MAAI,UAAU,QAAW;AACvB,WAAO,CAAC;AAAA,EACV;AACA,MAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzB,UAAM,IAAI,MAAM,GAAG,IAAI,sCAAsC;AAAA,EAC/D;AACA,SAAO,CAAC,GAAG,IAAI,IAAI,MAAM;AAAA,IAAI,CAAC,OAAO,UACnC,iBAAiB,GAAG,IAAI,IAAI,KAAK,KAAK,KAAK;AAAA,EAC7C,CAAC,CAAC;AACJ;AAEA,SAAS,kBAAkB,KAAK,OAAO;AACrC,MAAI,OAAO,IAAI,OAAO,UAAU;AAC9B,WAAO,iBAAiB,QAAQ,KAAK,QAAQ,IAAI,EAAE;AAAA,EACrD;AACA,MAAI,OAAO,IAAI,UAAU,UAAU;AACjC,WAAO,iBAAiB,QAAQ,KAAK,WAAW,IAAI,KAAK;AAAA,EAC3D;AACA,MAAI,OAAO,IAAI,QAAQ,UAAU;AAC/B,WAAO,iBAAiB,QAAQ,KAAK,SAAS,IAAI,GAAG;AAAA,EACvD;AACA,MAAI,OAAO,IAAI,YAAY,UAAU;AACnC,WAAO,iBAAiB,QAAQ,KAAK,aAAa,IAAI,OAAO;AAAA,EAC/D;AACA,SAAO,iBAAiB,QAAQ,KAAK,QAAQ,OAAO,KAAK,EAAE;AAC7D;AAEA,eAAe,cAAc,UAAU,UAAU,CAAC,GAAG;AACnD,QAAM,EAAE,MAAM,UAAU,UAAU,WAAW,MAAM,IAAI,WAAW,CAAC;AACnE,QAAM,UAAU,eAAe,MAAM,MAAM,IAAI,IAAI,KAAK,QAAQ;AAEhE,MAAI,CAAC,WAAW,QAAQ,aAAa,SAAS;AAC5C,UAAM,EAAE,SAAS,IAAI,MAAM,OAAO,aAAkB;AACpD,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,KAAU;AACjD,WAAO,SAAS,cAAc,OAAO,GAAG,MAAM;AAAA,EAChD;AAEA,QAAM,WAAW,MAAM,QAAQ,OAAO;AACtC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,SAAS,YAAY,WAAW,SAAS,SAAS;AACxD,UAAM,aAAa,gBAAgB,WAAW,SAAS,aAAa;AACpE,UAAM,SAAS,aAAa,GAAG,MAAM,IAAI,UAAU,KAAK,GAAG,MAAM;AACjE,UAAM,IAAI,MAAM,wBAAwB,MAAM,GAAG;AAAA,EACnD;AACA,SAAO,SAAS,KAAK;AACvB;AAEA,eAAsB,cAAc,UAAU,CAAC,GAAG;AAChD,SAAO,cAAc,cAAc,OAAO;AAC5C;AAEA,eAAsB,iBAAiB,UAAU,CAAC,GAAG;AACnD,SAAO,cAAc,iBAAiB,OAAO;AAC/C;AAEA,eAAsB,kBAAkB,UAAU,CAAC,GAAG;AACpD,QAAM,EAAE,OAAO,QAAQ,GAAG,KAAK,IAAI,WAAW,CAAC;AAC/C,QAAM,eAAe,oBAAoB,IAAI;AAC7C,MAAI,iBAAiB,OAAO;AAC1B,WAAO,iBAAiB,IAAI;AAAA,EAC9B;AACA,SAAO,cAAc,IAAI;AAC3B;AAEO,SAAS,kBAAkB,OAAO,CAAC,GAAG;AAC3C,MAAI,CAAC,MAAM,QAAQ,IAAI,GAAG;AACxB,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,QAAM,aAAa,KAAK,IAAI,CAAC,KAAK,UAAU;AAC1C,QAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AACzD,YAAM,IAAI,MAAM,QAAQ,KAAK,sBAAsB;AAAA,IACrD;AAEA,UAAM,KAAK,kBAAkB,KAAK,KAAK;AACvC,UAAM,eAAe;AAAA,MACnB,QAAQ,KAAK;AAAA,MACb,IAAI,gBAAgB,IAAI;AAAA,IAC1B;AACA,UAAM,WAAW;AAAA,MACf,QAAQ,KAAK;AAAA,MACb,IAAI;AAAA,IACN;AAEA,WAAO;AAAA,MACL;AAAA,MACA,KAAK,OAAO,IAAI,QAAQ,WAAW,IAAI,MAAM;AAAA,MAC7C,OAAO,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ;AAAA,MACnD,SAAS,IAAI;AAAA,MACb,YAAY,OAAO,IAAI,eAAe,WAAW,IAAI,aAAa;AAAA,MAClE;AAAA,MACA;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,MAAM,oBAAI,IAAI;AACpB,aAAW,OAAO,YAAY;AAC5B,QAAI,IAAI,IAAI,IAAI,EAAE,GAAG;AACnB,YAAM,IAAI,MAAM,kCAAkC,IAAI,EAAE,EAAE;AAAA,IAC5D;AACA,QAAI,IAAI,IAAI,EAAE;AAAA,EAChB;AAEA,aAAW,OAAO,YAAY;AAC5B,eAAW,cAAc,IAAI,cAAc;AACzC,UAAI,CAAC,IAAI,IAAI,UAAU,GAAG;AACxB,cAAM,IAAI;AAAA,UACR,QAAQ,IAAI,EAAE,6BAA6B,UAAU;AAAA,QACvD;AAAA,MACF;AACA,UAAI,eAAe,IAAI,IAAI;AACzB,cAAM,IAAI,MAAM,QAAQ,IAAI,EAAE,4BAA4B;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AAEA,QAAM,iBAAiB,IAAI,IAAI,WAAW,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;AACpE,QAAM,WAAW,IAAI,IAAI,WAAW,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,aAAa,MAAM,CAAC,CAAC;AACnF,aAAW,OAAO,YAAY;AAC5B,eAAW,cAAc,IAAI,cAAc;AACzC,qBAAe,IAAI,UAAU,EAAE,KAAK,IAAI,EAAE;AAAA,IAC5C;AAAA,EACF;AAEA,QAAM,QAAQ,WACX,OAAO,CAAC,QAAQ,IAAI,aAAa,WAAW,CAAC,EAC7C,IAAI,CAAC,QAAQ,IAAI,EAAE;AACtB,QAAM,OAAO,CAAC;AACd,QAAM,QAAQ,CAAC,GAAG,KAAK;AACvB,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,YAAY,MAAM,MAAM;AAC9B,SAAK,KAAK,SAAS;AACnB,eAAW,eAAe,eAAe,IAAI,SAAS,KAAK,CAAC,GAAG;AAC7D,YAAM,QAAQ,SAAS,IAAI,WAAW,KAAK,KAAK;AAChD,eAAS,IAAI,aAAa,IAAI;AAC9B,UAAI,SAAS,GAAG;AACd,cAAM,KAAK,WAAW;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,KAAK,WAAW,WAAW,QAAQ;AACrC,UAAM,IAAI,MAAM,6BAA6B;AAAA,EAC/C;AAEA,QAAM,cAAc,WAAW;AAAA,IAC7B,CAAC,SAAS,QAAQ,KAAK,IAAI,SAAS,IAAI,QAAQ;AAAA,IAChD;AAAA,EACF;AAEA,QAAM,YAAY,WAAW;AAAA,IAAI,CAAC,QAChC,OAAO,OAAO;AAAA,MACZ,GAAG;AAAA,MACH,cAAc,OAAO,OAAO,CAAC,GAAG,IAAI,YAAY,CAAC;AAAA,MACjD,YAAY,OAAO,OAAO,CAAC,GAAI,eAAe,IAAI,IAAI,EAAE,KAAK,CAAC,CAAE,CAAC;AAAA,MACjE,iBAAiB,IAAI,aAAa;AAAA,MAClC,MAAM,IAAI,aAAa,WAAW;AAAA,IACpC,CAAC;AAAA,EACH;AAEA,SAAO,OAAO,OAAO;AAAA,IACnB,MAAM;AAAA,IACN,UAAU,UAAU;AAAA,IACpB;AAAA,IACA,OAAO,OAAO,OAAO,UAAU,OAAO,CAAC,QAAQ,IAAI,IAAI,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;AAAA,IAC7E,kBAAkB,OAAO,OAAO,IAAI;AAAA,IACpC,MAAM,OAAO,OAAO,SAAS;AAAA,EAC/B,CAAC;AACH;","names":[]}
{"version":3,"sources":["../src/index.js"],"sourcesContent":["export const queueWgslUrl = new URL(\"./queue.wgsl\", import.meta.url);\nexport const dagQueueWgslUrl = new URL(\"./dag-queue.wgsl\", import.meta.url);\nexport const schedulerModes = Object.freeze([\"flat\", \"dag\"]);\n\nconst IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/u;\n\nfunction assertSchedulerMode(mode) {\n const resolved = mode ?? \"flat\";\n if (!schedulerModes.includes(resolved)) {\n throw new Error(\n `mode must be one of: ${schedulerModes.join(\", \")}.`\n );\n }\n return resolved;\n}\n\nfunction assertIdentifier(name, value) {\n if (typeof value !== \"string\" || !IDENTIFIER_PATTERN.test(value)) {\n throw new Error(\n `${name} must match ${IDENTIFIER_PATTERN.toString()} and be at most 64 characters long.`\n );\n }\n return value;\n}\n\nfunction readNonNegativeInteger(name, value) {\n if (value === undefined) {\n return 0;\n }\n if (\n typeof value !== \"number\" ||\n !Number.isInteger(value) ||\n value < 0 ||\n !Number.isFinite(value)\n ) {\n throw new Error(`${name} must be a non-negative integer.`);\n }\n return value;\n}\n\nfunction normalizeDependencies(name, value) {\n if (value === undefined) {\n return [];\n }\n if (!Array.isArray(value)) {\n throw new Error(`${name} must be an array of dependency ids.`);\n }\n return [...new Set(value.map((entry, index) =>\n assertIdentifier(`${name}[${index}]`, entry)\n ))];\n}\n\nfunction resolveGraphJobId(job, index) {\n if (typeof job.id === \"string\") {\n return assertIdentifier(`jobs[${index}].id`, job.id);\n }\n if (typeof job.label === \"string\") {\n return assertIdentifier(`jobs[${index}].label`, job.label);\n }\n if (typeof job.key === \"string\") {\n return assertIdentifier(`jobs[${index}].key`, job.key);\n }\n if (typeof job.jobType === \"string\") {\n return assertIdentifier(`jobs[${index}].jobType`, job.jobType);\n }\n return assertIdentifier(`jobs[${index}].id`, `job-${index}`);\n}\n\nfunction buildPriorityLanes(graphJobs) {\n const lanes = new Map();\n\n for (const job of graphJobs) {\n const lane = lanes.get(job.priority) ?? {\n priority: job.priority,\n jobIds: [],\n rootJobIds: [],\n };\n lane.jobIds.push(job.id);\n if (job.root) {\n lane.rootJobIds.push(job.id);\n }\n lanes.set(job.priority, lane);\n }\n\n return Object.freeze(\n [...lanes.values()]\n .sort((left, right) => right.priority - left.priority)\n .map((lane) =>\n Object.freeze({\n priority: lane.priority,\n jobIds: Object.freeze([...lane.jobIds]),\n rootJobIds: Object.freeze([...lane.rootJobIds]),\n jobCount: lane.jobIds.length,\n rootCount: lane.rootJobIds.length,\n })\n )\n );\n}\n\nasync function loadWgslAsset(assetUrl, options = {}) {\n const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};\n const wgslUrl = url instanceof URL ? url : new URL(url, assetUrl);\n\n if (!fetcher || wgslUrl.protocol === \"file:\") {\n const { readFile } = await import(\"node:fs/promises\");\n const { fileURLToPath } = await import(\"node:url\");\n return readFile(fileURLToPath(wgslUrl), \"utf8\");\n }\n\n const response = await fetcher(wgslUrl);\n if (!response.ok) {\n const status = \"status\" in response ? response.status : \"unknown\";\n const statusText = \"statusText\" in response ? response.statusText : \"\";\n const detail = statusText ? `${status} ${statusText}` : `${status}`;\n throw new Error(`Failed to load WGSL (${detail})`);\n }\n return response.text();\n}\n\nexport async function loadQueueWgsl(options = {}) {\n return loadWgslAsset(queueWgslUrl, options);\n}\n\nexport async function loadDagQueueWgsl(options = {}) {\n return loadWgslAsset(dagQueueWgslUrl, options);\n}\n\nexport async function loadSchedulerWgsl(options = {}) {\n const { mode = \"flat\", ...rest } = options ?? {};\n const resolvedMode = assertSchedulerMode(mode);\n if (resolvedMode === \"dag\") {\n return loadDagQueueWgsl(rest);\n }\n return loadQueueWgsl(rest);\n}\n\nexport function createDagJobGraph(jobs = []) {\n if (!Array.isArray(jobs)) {\n throw new Error(\"jobs must be an array.\");\n }\n\n const normalized = jobs.map((job, index) => {\n if (!job || typeof job !== \"object\" || Array.isArray(job)) {\n throw new Error(`jobs[${index}] must be an object.`);\n }\n\n const id = resolveGraphJobId(job, index);\n const dependencies = normalizeDependencies(\n `jobs[${index}].dependencies`,\n job.dependencies ?? job.dependsOn\n );\n const priority = readNonNegativeInteger(\n `jobs[${index}].priority`,\n job.priority\n );\n\n return {\n id,\n key: typeof job.key === \"string\" ? job.key : undefined,\n label: typeof job.label === \"string\" ? job.label : undefined,\n jobType: job.jobType,\n queueClass: typeof job.queueClass === \"string\" ? job.queueClass : undefined,\n priority,\n dependencies,\n };\n });\n\n const ids = new Set();\n for (const job of normalized) {\n if (ids.has(job.id)) {\n throw new Error(`Duplicate DAG job id detected: ${job.id}`);\n }\n ids.add(job.id);\n }\n\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n if (!ids.has(dependency)) {\n throw new Error(\n `Job \"${job.id}\" depends on unknown job \"${dependency}\".`\n );\n }\n if (dependency === job.id) {\n throw new Error(`Job \"${job.id}\" cannot depend on itself.`);\n }\n }\n }\n\n const dependentsById = new Map(normalized.map((job) => [job.id, []]));\n const indegree = new Map(normalized.map((job) => [job.id, job.dependencies.length]));\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n dependentsById.get(dependency).push(job.id);\n }\n }\n\n const ready = normalized\n .filter((job) => job.dependencies.length === 0)\n .map((job) => job.id);\n const topo = [];\n const queue = [...ready];\n while (queue.length > 0) {\n const currentId = queue.shift();\n topo.push(currentId);\n for (const dependentId of dependentsById.get(currentId) ?? []) {\n const next = (indegree.get(dependentId) ?? 0) - 1;\n indegree.set(dependentId, next);\n if (next === 0) {\n queue.push(dependentId);\n }\n }\n }\n\n if (topo.length !== normalized.length) {\n throw new Error(\"DAG graph contains a cycle.\");\n }\n\n const maxPriority = normalized.reduce(\n (current, job) => Math.max(current, job.priority),\n 0\n );\n\n const graphJobs = normalized.map((job) =>\n Object.freeze({\n ...job,\n dependencies: Object.freeze([...job.dependencies]),\n dependents: Object.freeze([...(dependentsById.get(job.id) ?? [])]),\n dependencyCount: job.dependencies.length,\n unresolvedDependencyCount: job.dependencies.length,\n dependentCount: (dependentsById.get(job.id) ?? []).length,\n root: job.dependencies.length === 0,\n })\n );\n\n const priorityLanes = buildPriorityLanes(graphJobs);\n\n return Object.freeze({\n mode: \"dag\",\n jobCount: graphJobs.length,\n maxPriority,\n jobIds: Object.freeze(graphJobs.map((job) => job.id)),\n roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),\n topologicalOrder: Object.freeze(topo),\n priorityLanes,\n jobs: Object.freeze(graphJobs),\n });\n}\n"],"mappings":"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","names":[]}
{
"name": "@plasius/gpu-lock-free-queue",
"version": "0.2.13",
"version": "0.2.14",
"description": "WebGPU lock-free queue assets for flat and DAG-ready GPU job scheduling.",

@@ -5,0 +5,0 @@ "type": "module",

@@ -46,2 +46,4 @@ # @plasius/gpu-lock-free-queue

console.log(graph.roots);
console.log(graph.priorityLanes);
const dagSchedulerWgsl = await loadDagQueueWgsl();

@@ -67,2 +69,12 @@ const selectedWgsl = await loadSchedulerWgsl({ mode: graph.mode });

The JS graph helper is the canonical preflight contract for DAG metadata. It
returns:
- `jobIds` for stable upload order
- `roots` for the initial runnable set
- `topologicalOrder` for validation and planning
- `priorityLanes` so callers can size ready queues per priority bucket
- per-job `dependencies`, `dependents`, `dependencyCount`,
`unresolvedDependencyCount`, and `dependentCount`
## Buffer layout (breaking change in v0.4.0)

@@ -69,0 +81,0 @@ Bindings are:

@@ -69,2 +69,33 @@ const { pathToFileURL, fileURLToPath } = require("node:url");

function buildPriorityLanes(graphJobs) {
const lanes = new Map();
for (const job of graphJobs) {
const lane = lanes.get(job.priority) ?? {
priority: job.priority,
jobIds: [],
rootJobIds: [],
};
lane.jobIds.push(job.id);
if (job.root) {
lane.rootJobIds.push(job.id);
}
lanes.set(job.priority, lane);
}
return Object.freeze(
[...lanes.values()]
.sort((left, right) => right.priority - left.priority)
.map((lane) =>
Object.freeze({
priority: lane.priority,
jobIds: Object.freeze([...lane.jobIds]),
rootJobIds: Object.freeze([...lane.rootJobIds]),
jobCount: lane.jobIds.length,
rootCount: lane.rootJobIds.length,
})
)
);
}
async function loadWgslAsset(assetUrl, options = {}) {

@@ -197,2 +228,4 @@ const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};

dependencyCount: job.dependencies.length,
unresolvedDependencyCount: job.dependencies.length,
dependentCount: (dependentsById.get(job.id) ?? []).length,
root: job.dependencies.length === 0,

@@ -202,2 +235,4 @@ })

const priorityLanes = buildPriorityLanes(graphJobs);
return Object.freeze({

@@ -207,4 +242,6 @@ mode: "dag",

maxPriority,
jobIds: Object.freeze(graphJobs.map((job) => job.id)),
roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),
topologicalOrder: Object.freeze(topo),
priorityLanes,
jobs: Object.freeze(graphJobs),

@@ -211,0 +248,0 @@ });

@@ -69,2 +69,33 @@ export const queueWgslUrl = new URL("./queue.wgsl", import.meta.url);

function buildPriorityLanes(graphJobs) {
const lanes = new Map();
for (const job of graphJobs) {
const lane = lanes.get(job.priority) ?? {
priority: job.priority,
jobIds: [],
rootJobIds: [],
};
lane.jobIds.push(job.id);
if (job.root) {
lane.rootJobIds.push(job.id);
}
lanes.set(job.priority, lane);
}
return Object.freeze(
[...lanes.values()]
.sort((left, right) => right.priority - left.priority)
.map((lane) =>
Object.freeze({
priority: lane.priority,
jobIds: Object.freeze([...lane.jobIds]),
rootJobIds: Object.freeze([...lane.rootJobIds]),
jobCount: lane.jobIds.length,
rootCount: lane.rootJobIds.length,
})
)
);
}
async function loadWgslAsset(assetUrl, options = {}) {

@@ -199,2 +230,4 @@ const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};

dependencyCount: job.dependencies.length,
unresolvedDependencyCount: job.dependencies.length,
dependentCount: (dependentsById.get(job.id) ?? []).length,
root: job.dependencies.length === 0,

@@ -204,2 +237,4 @@ })

const priorityLanes = buildPriorityLanes(graphJobs);
return Object.freeze({

@@ -209,6 +244,8 @@ mode: "dag",

maxPriority,
jobIds: Object.freeze(graphJobs.map((job) => job.id)),
roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),
topologicalOrder: Object.freeze(topo),
priorityLanes,
jobs: Object.freeze(graphJobs),
});
}