@iqlabs-official/git-sdk 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.js +24 -10
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.d.ts +1 -1
- package/dist/node/index.js +24 -10
- package/dist/node/index.js.map +1 -1
- package/dist/shared/index.d.ts +21 -1
- package/dist/shared/index.js +24 -10
- package/dist/shared/index.js.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { Commit, FileTree, GitClient, GitClientConfig, IQGIT_ROOT_ID, REGISTRY_HINT, RegistryEntry, Repository, bootstrapRegistry, commitTableHint, loadBlob, loadTree, readCommitHistory, readLatestCommit, readOwnerRepos, readRegistryPage, repoListHint } from '../shared/index.js';
|
|
1
|
+
export { Commit, FileTree, GitClient, GitClientConfig, IQGIT_ROOT_ID, REGISTRY_HINT, RegistryEntry, Repository, WriteEvent, bootstrapRegistry, commitTableHint, loadBlob, loadTree, readCommitHistory, readLatestCommit, readOwnerRepos, readRegistryPage, repoListHint } from '../shared/index.js';
|
|
2
2
|
export { SignerInput } from '@iqlabs-official/solana-sdk/utils';
|
|
3
3
|
import '@solana/web3.js';
|
package/dist/browser/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createTable, writeRow, codeIn as codeIn$1 } from '@iqlabs-official/solana-sdk/writer';
|
|
2
1
|
import { SystemProgram, Transaction, Keypair } from '@solana/web3.js';
|
|
3
2
|
import { getDbRootPda, createInstructionBuilder, initializeDbRootInstruction, getTablePda } from '@iqlabs-official/solana-sdk/contract';
|
|
4
3
|
import { readCodeIn, readTableRows } from '@iqlabs-official/solana-sdk/reader';
|
|
5
4
|
import { toSeedBytes } from '@iqlabs-official/solana-sdk/utils';
|
|
5
|
+
import { createTable, writeRow, codeIn as codeIn$1 } from '@iqlabs-official/solana-sdk/writer';
|
|
6
6
|
|
|
7
7
|
// src/core/hash.ts
|
|
8
8
|
var impl;
|
|
@@ -96,8 +96,6 @@ async function accountExists(connection, pda) {
|
|
|
96
96
|
function tablePda(hint) {
|
|
97
97
|
return getTablePda(DB_ROOT, toSeedBytes(hint));
|
|
98
98
|
}
|
|
99
|
-
|
|
100
|
-
// src/layers/commit.ts
|
|
101
99
|
var COMMIT_COLUMNS = [
|
|
102
100
|
"id",
|
|
103
101
|
"message",
|
|
@@ -146,6 +144,7 @@ var REGISTRY_COLUMNS = ["owner", "repo", "description", "timestamp"];
|
|
|
146
144
|
async function createRepo(connection, signer, meta) {
|
|
147
145
|
const owner = signer.publicKey.toBase58();
|
|
148
146
|
const listHint = repoListHint(owner);
|
|
147
|
+
const writes = [];
|
|
149
148
|
if (!await accountExists(connection, tablePda(listHint))) {
|
|
150
149
|
await createTable(
|
|
151
150
|
connection,
|
|
@@ -161,7 +160,8 @@ async function createRepo(connection, signer, meta) {
|
|
|
161
160
|
listHint
|
|
162
161
|
);
|
|
163
162
|
}
|
|
164
|
-
await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));
|
|
163
|
+
const sig = await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));
|
|
164
|
+
writes.push({ tableHint: listHint, sig, row: meta });
|
|
165
165
|
if (meta.isPublic) {
|
|
166
166
|
const entry = {
|
|
167
167
|
owner,
|
|
@@ -169,8 +169,10 @@ async function createRepo(connection, signer, meta) {
|
|
|
169
169
|
description: meta.description,
|
|
170
170
|
timestamp: meta.timestamp
|
|
171
171
|
};
|
|
172
|
-
await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));
|
|
172
|
+
const regSig = await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));
|
|
173
|
+
writes.push({ tableHint: REGISTRY_HINT, sig: regSig, row: entry });
|
|
173
174
|
}
|
|
175
|
+
return writes;
|
|
174
176
|
}
|
|
175
177
|
async function readOwnerRepos(_connection, owner) {
|
|
176
178
|
return await readRows(repoListHint(owner));
|
|
@@ -203,12 +205,12 @@ async function uploadBlob(connection, signer, relativePath, base64Content, reuse
|
|
|
203
205
|
const hash = await sha256Hex(base64Content);
|
|
204
206
|
const prior = reuse[relativePath];
|
|
205
207
|
if (prior && prior.hash === hash) return prior;
|
|
206
|
-
const
|
|
208
|
+
const basename = relativePath.split("/").pop() || relativePath;
|
|
207
209
|
const txId = await codeIn(
|
|
208
210
|
connection,
|
|
209
211
|
signer,
|
|
210
212
|
base64Content,
|
|
211
|
-
|
|
213
|
+
`iqgit-blob:${basename}`,
|
|
212
214
|
"application/octet-stream",
|
|
213
215
|
onProgress
|
|
214
216
|
);
|
|
@@ -219,7 +221,7 @@ async function uploadTree(connection, signer, tree) {
|
|
|
219
221
|
connection,
|
|
220
222
|
signer,
|
|
221
223
|
JSON.stringify(tree),
|
|
222
|
-
"tree
|
|
224
|
+
"iqgit-tree",
|
|
223
225
|
"application/json"
|
|
224
226
|
);
|
|
225
227
|
}
|
|
@@ -251,8 +253,9 @@ var GitClient = class {
|
|
|
251
253
|
*/
|
|
252
254
|
async createRepo(meta) {
|
|
253
255
|
const { connection, signer } = this.cfg;
|
|
254
|
-
await createRepo(connection, signer, meta);
|
|
256
|
+
const writes = await createRepo(connection, signer, meta);
|
|
255
257
|
await ensureCommitTable(connection, signer, meta.name);
|
|
258
|
+
this.fireOnWrite(writes);
|
|
256
259
|
}
|
|
257
260
|
/**
|
|
258
261
|
* Incremental commit against a scanned directory snapshot.
|
|
@@ -285,9 +288,20 @@ var GitClient = class {
|
|
|
285
288
|
timestamp: Date.now(),
|
|
286
289
|
author: owner
|
|
287
290
|
};
|
|
288
|
-
await writeCommit(connection, signer, repoName, commit);
|
|
291
|
+
const sig = await writeCommit(connection, signer, repoName, commit);
|
|
292
|
+
this.fireOnWrite([{ tableHint: commitTableHint(owner, repoName), sig, row: commit }]);
|
|
289
293
|
return commit;
|
|
290
294
|
}
|
|
295
|
+
/** Resolve the table hint to a PDA and forward each write to `onWrite`.
|
|
296
|
+
* Wrapped in try/catch per call so a misbehaving notify can't fail the
|
|
297
|
+
* whole batch — but we still surface the error so the consumer knows. */
|
|
298
|
+
fireOnWrite(writes) {
|
|
299
|
+
if (!this.cfg.onWrite) return;
|
|
300
|
+
for (const w of writes) {
|
|
301
|
+
const tablePda2 = tablePda(w.tableHint).toBase58();
|
|
302
|
+
this.cfg.onWrite({ tablePda: tablePda2, tableHint: w.tableHint, sig: w.sig, row: w.row });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
291
305
|
/**
|
|
292
306
|
* Restore a commit's files into a caller-provided sink. Runtime-neutral
|
|
293
307
|
* shape mirrors `commit`: the caller decides how to write bytes to disk /
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/hash.ts","../../src/platform/hash-browser.ts","../../src/core/seed.ts","../../src/layers/chain.ts","../../src/layers/commit.ts","../../src/layers/repo.ts","../../src/layers/storage.ts","../../src/layers/client.ts","../../src/browser.ts"],"names":["sdkCodeIn","createTable","writeRow"],"mappings":"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","file":"index.js","sourcesContent":["// SHA-256 content hashing for the blob-dedup index. Runtime-specific\n// implementations live in `platform/hash-node.ts` and `platform/hash-browser.ts`;\n// this module just declares the interface the rest of the SDK uses.\n//\n// Do NOT use this for deriving table seeds — that path is owned by\n// `iqlabs.utils.toSeedBytes` (keccak) and must stay aligned with the SDK.\n\n/**\n * SHA-256 of a UTF-8 or base64 string, returned as a lowercase hex digest.\n * Implementation is injected by the platform entry (node.ts / browser.ts).\n */\nexport type Sha256Hex = (input: string) => Promise<string>;\n\n// Platform entries (src/node.ts, src/browser.ts) will call `setSha256(...)`\n// during module init so downstream layers never care which runtime they are in.\n// This indirection is the one place where a wrapper is justified (CODE-RULES §1)\n// because the behavior genuinely differs per runtime.\nlet impl: Sha256Hex | undefined;\n\nexport function setSha256(fn: Sha256Hex): void {\n if (impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 implementation already installed. Import exactly one of '@iqlabs-official/git-sdk/node' or '@iqlabs-official/git-sdk/browser'.\",\n );\n }\n impl = fn;\n}\n\nexport async function sha256Hex(input: string): Promise<string> {\n if (!impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 not installed. Import '@iqlabs-official/git-sdk/node' (Node) or '@iqlabs-official/git-sdk/browser' (browser) before using the SDK.\",\n );\n }\n return impl(input);\n}\n","// Browser SHA-256 implementation. Kept as a pure export; `src/browser.ts`\n// installs it via `setSha256` during its own module init.\n\nimport type { Sha256Hex } from \"../core/hash\";\n\nexport const hashBrowser: Sha256Hex = async (input) => {\n const bytes = new TextEncoder().encode(input);\n const digest = await crypto.subtle.digest(\n \"SHA-256\",\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer,\n );\n return Array.from(new Uint8Array(digest))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n};\n","// The single source of truth for table_hint strings.\n//\n// Readers re-derive PDAs with `iqlabs.utils.toSeedBytes(hint)` →\n// `iqlabs.contract.getTablePda(...)`; writers pass the same hint into\n// `iqlabs.writer.createTable`. Keeping the naming convention in one place\n// prevents silent drift between writer and reader. CODE-RULES §2 — if any\n// caller ever wants to build one of these strings inline, route it here\n// instead.\n\n/** DbRoot id for every iq-git table. Bootstrap and every caller share this. */\nexport const IQGIT_ROOT_ID = \"iq-git-v1\";\n\n/** `git_repos:all` — open-writers registry that drives the public gallery. */\nexport const REGISTRY_HINT = \"git_repos:all\";\n\n/**\n * Hint for the per-owner personal repo list.\n * input: owner wallet base58\n * output: \"git_repos_v2_<owner>\"\n */\nexport function repoListHint(owner: string): string {\n return `git_repos_v2_${owner}`;\n}\n\n/**\n * Hint for the per-repo commit table.\n * input: owner wallet base58, repo name (any characters — SDK keccak-hashes)\n * output: \"git_commits:<owner>:<repo>\"\n */\nexport function commitTableHint(owner: string, repo: string): string {\n return `git_commits:${owner}:${repo}`;\n}\n","// L1 — chain primitives shared by L2/L3. Kept tiny by design: each function\n// here either (a) hides PDA derivation so callers only deal with hints, or\n// (b) wraps a single iqlabs-sdk call to keep an iq-git-specific default\n// (chunk speed, root id, ...). Pure passthroughs like `createTable` /\n// `writeRow` live at the call site instead — wrapping them once more would\n// just bury the iqlabs-sdk surface (CODE-RULES §1).\n\nimport {\n Keypair,\n SystemProgram,\n Transaction,\n type Connection,\n type PublicKey,\n} from \"@solana/web3.js\";\nimport {\n createInstructionBuilder,\n getDbRootPda,\n getTablePda,\n initializeDbRootInstruction,\n} from \"@iqlabs-official/solana-sdk/contract\";\nimport { readCodeIn, readTableRows } from \"@iqlabs-official/solana-sdk/reader\";\nimport { toSeedBytes, type SignerInput, type WalletSigner } from \"@iqlabs-official/solana-sdk/utils\";\nimport { codeIn as sdkCodeIn } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID } from \"../core/seed\";\n\n/** DbRoot PDA for the `iq-git-v1` namespace — derived once, reused everywhere. */\nexport const DB_ROOT_SEED = toSeedBytes(IQGIT_ROOT_ID);\nexport const DB_ROOT = getDbRootPda(DB_ROOT_SEED);\n\n/**\n * Initialize the `iq-git-v1` DbRoot account if it doesn't exist. First-call\n * cost on a fresh network. Idempotent: returns null if already initialized.\n * Accepts any SignerInput so an admin can run it from a wallet, not just\n * from a Keypair.\n */\nexport async function ensureDbRoot(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n if (await accountExists(connection, DB_ROOT)) return null;\n const builder = createInstructionBuilder();\n const ix = initializeDbRootInstruction(\n builder,\n {\n db_root: DB_ROOT,\n signer: signer.publicKey,\n system_program: SystemProgram.programId,\n },\n { db_root_id: DB_ROOT_SEED },\n );\n const tx = new Transaction().add(ix);\n const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();\n tx.recentBlockhash = blockhash;\n tx.feePayer = signer.publicKey;\n const signed = await signTx(signer, tx);\n const signature = await connection.sendRawTransaction(signed.serialize());\n await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });\n return signature;\n}\n\n// Sign a single Transaction with whatever shape the signer takes. Keypair\n// has a `secretKey`; wallet adapters expose `signTransaction`. iqlabs-sdk's\n// own `sendTx` uses the same dispatch; we replicate it here so the helper\n// stays self-contained.\nasync function signTx(signer: SignerInput, tx: Transaction): Promise<Transaction> {\n if (signer instanceof Keypair || \"secretKey\" in signer) {\n tx.partialSign(signer as Keypair);\n return tx;\n }\n return (signer as WalletSigner).signTransaction(tx);\n}\n\n/**\n * Read rows from a table. Translates our hint into the PDA and forwards to\n * `iqlabs.reader.readTableRows`.\n */\nexport async function readRows(\n hint: string,\n options?: { limit?: number; before?: string },\n): Promise<Array<Record<string, unknown>>> {\n return readTableRows(tablePda(hint), options);\n}\n\n/**\n * Fetch just the latest row of a table — the fast path for \"what is the\n * current commit\" and \"what is the pinned deploy\".\n */\nexport async function readLatestRow(\n hint: string,\n): Promise<Record<string, unknown> | null> {\n const rows = await readRows(hint, { limit: 1 });\n return rows[0] ?? null;\n}\n\n/**\n * Upload a blob via `iqlabs.writer.codeIn`. The SDK chunks internally when\n * `data` is a plain string. We default speed to \"light\" because that's the\n * Helius-friendly setting for git workloads (per-file uploads are bursty).\n */\nexport async function codeIn(\n connection: Connection,\n signer: SignerInput,\n data: string | string[],\n filename: string,\n filetype: string,\n onProgress?: (percent: number) => void,\n speed: \"light\" | \"medium\" | \"fast\" = \"light\",\n): Promise<string> {\n return sdkCodeIn(\n { connection, signer },\n data,\n filename,\n 0,\n filetype,\n onProgress,\n speed,\n );\n}\n\nexport { readCodeIn };\n\n/** Cheap existence check for a PDA. */\nexport async function accountExists(\n connection: Connection,\n pda: PublicKey,\n): Promise<boolean> {\n return (await connection.getAccountInfo(pda)) !== null;\n}\n\n/** Resolve a hint to its PDA. */\nexport function tablePda(hint: string): PublicKey {\n return getTablePda(DB_ROOT, toSeedBytes(hint));\n}\n","// L3 — per-repo commit table.\n//\n// v2's core invariant: each repo has its own table at hint\n// `git_commits:<owner>:<repo>`, with writers locked to [owner]. That means\n// \"the most recent successful tx in this table = the latest commit\", and we\n// read it as a single-row query (limit: 1).\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, commitTableHint } from \"../core/seed\";\nimport type { Commit } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst COMMIT_COLUMNS = [\n \"id\",\n \"message\",\n \"treeTxId\",\n \"parentCommitId\",\n \"timestamp\",\n \"author\",\n];\n\n/**\n * Ensure the per-repo commit table exists with writers = [owner]. No-op if\n * it already exists.\n */\nexport async function ensureCommitTable(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n): Promise<string | null> {\n const hint = commitTableHint(signer.publicKey.toBase58(), repo);\n if (await chain.accountExists(connection, chain.tablePda(hint))) return null;\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n hint,\n hint,\n COMMIT_COLUMNS,\n \"id\",\n [],\n undefined,\n [signer.publicKey],\n hint,\n );\n}\n\n/**\n * Append one commit row. Callers (workflow-level code) are responsible for\n * setting parentCommitId — the SDK does not auto-chain.\n */\nexport async function writeCommit(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n commit: Commit,\n): Promise<string> {\n const owner = signer.publicKey.toBase58();\n return writeRow(\n connection,\n signer,\n IQGIT_ROOT_ID,\n commitTableHint(owner, repo),\n JSON.stringify(commit),\n );\n}\n\n/** Latest commit. Single-row, O(1) RPC path. */\nexport async function readLatestCommit(\n _connection: Connection,\n owner: string,\n repo: string,\n): Promise<Commit | null> {\n const row = await chain.readLatestRow(commitTableHint(owner, repo));\n return row as unknown as Commit | null;\n}\n\n/** Full commit history, newest first. */\nexport async function readCommitHistory(\n _connection: Connection,\n owner: string,\n repo: string,\n options?: { limit?: number; before?: string },\n): Promise<Commit[]> {\n const rows = await chain.readRows(commitTableHint(owner, repo), options);\n return rows as unknown as Commit[];\n}\n","// L3 — repo list and public registry.\n//\n// Covers the two \"directory\" tables:\n// • git_repos_v2_<owner> — owner's personal repo list (writers = [owner])\n// • git_repos:all — public gallery registry (writers = [])\n//\n// Commit tables are a separate concern — see `commit.ts`.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, REGISTRY_HINT, repoListHint } from \"../core/seed\";\nimport type { RegistryEntry, Repository } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst REPO_COLUMNS = [\"name\", \"description\", \"isPublic\", \"timestamp\"];\nconst REGISTRY_COLUMNS = [\"owner\", \"repo\", \"description\", \"timestamp\"];\n\n/**\n * Create a repo in the owner's personal list, and (if public) also register\n * it in the public gallery. Two transactions — the contract does not support\n * writing two tables atomically.\n */\nexport async function createRepo(\n connection: Connection,\n signer: SignerInput,\n meta: Repository,\n): Promise<void> {\n const owner = signer.publicKey.toBase58();\n const listHint = repoListHint(owner);\n\n if (!(await chain.accountExists(connection, chain.tablePda(listHint)))) {\n await createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n listHint,\n listHint,\n REPO_COLUMNS,\n \"name\",\n [],\n undefined,\n [signer.publicKey],\n listHint,\n );\n }\n\n await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));\n\n if (meta.isPublic) {\n const entry: RegistryEntry = {\n owner,\n repo: meta.name,\n description: meta.description,\n timestamp: meta.timestamp,\n };\n await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));\n }\n}\n\n/** List all repos owned by `owner`. */\nexport async function readOwnerRepos(\n _connection: Connection,\n owner: string,\n): Promise<Repository[]> {\n return (await chain.readRows(repoListHint(owner))) as unknown as Repository[];\n}\n\n/**\n * One page of the public-gallery registry. Callers should still check\n * `row.owner` shape before trusting it in UI; we do not filter at the SDK\n * boundary.\n */\nexport async function readRegistryPage(\n _connection: Connection,\n options?: { limit?: number; before?: string },\n): Promise<RegistryEntry[]> {\n return (await chain.readRows(REGISTRY_HINT, options)) as unknown as RegistryEntry[];\n}\n\n/**\n * One-time global bootstrap of the `git_repos:all` table. Run once per\n * network from an admin key; subsequent calls short-circuit because the\n * account already exists.\n */\nexport async function bootstrapRegistry(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n await chain.ensureDbRoot(connection, signer);\n if (await chain.accountExists(connection, chain.tablePda(REGISTRY_HINT))) {\n return null;\n }\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n REGISTRY_HINT,\n REGISTRY_HINT,\n REGISTRY_COLUMNS,\n \"owner\",\n [],\n undefined,\n undefined,\n REGISTRY_HINT,\n );\n}\n","// L2 — blob + tree storage.\n//\n// `uploadBlob` is the file-level \"don't re-upload what's already on-chain\"\n// primitive. Content is always a base64 string (matching iq-git v1) so the\n// hash / dedup comparison stays byte-for-byte compatible across commits.\n// The reuse-map is just a plain object so callers can build it any way they\n// like (path→entry, or hash→txId for rename-aware dedup — CODE-RULES §3\n// keeps this inlined at the call site instead of a new type).\n//\n// `uploadTree` / `loadTree` serialize FileTree <-> on-chain tree.json as raw\n// JSON (filetype `application/json`), again matching v1.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport type { FileTree } from \"../core/types\";\nimport * as chain from \"./chain\";\n\n/**\n * Upload one file unless an identical hash is already in `reuse` (blob dedup).\n *\n * input: connection, signer, relativePath, base64 content, reuse map, optional onProgress\n * output: { txId, hash } — either reused or freshly uploaded\n */\nexport async function uploadBlob(\n connection: Connection,\n signer: SignerInput,\n relativePath: string,\n base64Content: string,\n reuse: FileTree,\n onProgress?: (percent: number) => void,\n): Promise<{ txId: string; hash: string }> {\n const hash = await sha256Hex(base64Content);\n const prior = reuse[relativePath];\n if (prior && prior.hash === hash) return prior;\n\n const filename = relativePath.split(\"/\").pop() || relativePath;\n const txId = await chain.codeIn(\n connection,\n signer,\n base64Content,\n filename,\n \"application/octet-stream\",\n onProgress,\n );\n return { txId, hash };\n}\n\n/**\n * Serialize a FileTree and upload it as one `tree.json` blob.\n */\nexport async function uploadTree(\n connection: Connection,\n signer: SignerInput,\n tree: FileTree,\n): Promise<string> {\n return chain.codeIn(\n connection,\n signer,\n JSON.stringify(tree),\n \"tree.json\",\n \"application/json\",\n );\n}\n\n/**\n * Fetch and parse a `tree.json` blob by its tx signature.\n */\nexport async function loadTree(treeTxId: string): Promise<FileTree> {\n const { data } = await chain.readCodeIn(treeTxId);\n if (data === null) {\n throw new Error(`tree.json not found for tx ${treeTxId}`);\n }\n return JSON.parse(data) as FileTree;\n}\n\n/**\n * Retrieve a blob's bytes by its txId. Returns the raw string that was\n * uploaded — for blobs this is base64; the caller decodes.\n */\nexport async function loadBlob(txId: string): Promise<string> {\n const { data } = await chain.readCodeIn(txId);\n if (data === null) {\n throw new Error(`blob not found for tx ${txId}`);\n }\n return data;\n}\n","// L4 — GitClient facade. High-level workflows built by composing the\n// layers below. This is the surface consumers (CLI, frontend, migrator) use;\n// they should not reach into `chain` / `storage` / `repo` / `commit` directly.\n//\n// Keep workflow logic here, not inside lower layers (CODE-RULES §5). When a\n// command needs scanning, hashing, uploading, row writing — this file\n// orchestrates them all.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport type { Commit, FileTree, Repository } from \"../core/types\";\nimport * as commitLayer from \"./commit\";\nimport * as repoLayer from \"./repo\";\nimport * as storage from \"./storage\";\n\nexport interface GitClientConfig {\n connection: Connection;\n signer: SignerInput;\n}\n\nexport class GitClient {\n // CODE-RULES §3 — only one small shape; inlined rather than aliased.\n constructor(private readonly cfg: GitClientConfig) {}\n\n /**\n * Create a new repo. Wraps repo.createRepo + pre-creating the commit table\n * so the first `commit()` call doesn't need to pay createTable cost.\n */\n async createRepo(meta: Repository): Promise<void> {\n const { connection, signer } = this.cfg;\n await repoLayer.createRepo(connection, signer, meta);\n await commitLayer.ensureCommitTable(connection, signer, meta.name);\n }\n\n /**\n * Incremental commit against a scanned directory snapshot.\n *\n * input: repoName, message, scan — a `{ [path]: base64Content }` map that\n * the caller produced (Node: fs walk + base64; browser: File input).\n * output: newly written Commit\n */\n async commit(\n repoName: string,\n message: string,\n scan: Record<string, string>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n const latest = await commitLayer.readLatestCommit(connection, owner, repoName);\n const oldTree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const newTree: FileTree = {};\n for (const [path, content] of Object.entries(scan)) {\n newTree[path] = await storage.uploadBlob(\n connection,\n signer,\n path,\n content,\n oldTree,\n );\n }\n\n const treeTxId = await storage.uploadTree(connection, signer, newTree);\n\n const commit: Commit = {\n id: crypto.randomUUID(),\n message,\n treeTxId,\n parentCommitId: latest?.id,\n timestamp: Date.now(),\n author: owner,\n };\n await commitLayer.writeCommit(connection, signer, repoName, commit);\n return commit;\n }\n\n /**\n * Restore a commit's files into a caller-provided sink. Runtime-neutral\n * shape mirrors `commit`: the caller decides how to write bytes to disk /\n * to a File System Access API handle / anywhere else. `content` is the\n * raw base64 string — the sink decodes.\n */\n async checkout(\n repoName: string,\n commitId: string | \"latest\",\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n let target: Commit | null;\n if (commitId === \"latest\") {\n target = await commitLayer.readLatestCommit(connection, owner, repoName);\n } else {\n const history = await commitLayer.readCommitHistory(connection, owner, repoName);\n target = history.find((c) => c.id === commitId) ?? null;\n }\n if (!target) {\n throw new Error(`commit not found: ${commitId} in ${owner}/${repoName}`);\n }\n\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /**\n * Whole-repo snapshot download — convenience on top of checkout(\"latest\")\n * but reading from someone else's `owner`. We do not require signer to\n * equal owner for reads.\n */\n async clone(\n repoName: string,\n owner: string,\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection } = this.cfg;\n const target = await commitLayer.readLatestCommit(connection, owner, repoName);\n if (!target) {\n throw new Error(`no commits in ${owner}/${repoName}`);\n }\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /** Commit history for a repo. */\n async log(\n owner: string,\n repoName: string,\n options?: { limit?: number; before?: string },\n ): Promise<Commit[]> {\n return commitLayer.readCommitHistory(\n this.cfg.connection,\n owner,\n repoName,\n options,\n );\n }\n\n /**\n * Compare a current directory snapshot to the latest commit's tree.\n */\n async status(\n owner: string,\n repoName: string,\n scan: Record<string, string>,\n ): Promise<{ added: string[]; modified: string[]; unchanged: string[] }> {\n const latest = await commitLayer.readLatestCommit(\n this.cfg.connection,\n owner,\n repoName,\n );\n const tree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const added: string[] = [];\n const modified: string[] = [];\n const unchanged: string[] = [];\n for (const [path, content] of Object.entries(scan)) {\n const prior = tree[path];\n if (!prior) {\n added.push(path);\n continue;\n }\n const hash = await sha256Hex(content);\n (prior.hash === hash ? unchanged : modified).push(path);\n }\n return { added, modified, unchanged };\n }\n}\n","// Browser entry. Installs the Web-Crypto SHA-256 hasher, then re-exports\n// everything from the shared root.\n\nimport { setSha256 } from \"./core/hash\";\nimport { hashBrowser } from \"./platform/hash-browser\";\n\nsetSha256(hashBrowser);\n\nexport * from \"./index\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/hash.ts","../../src/platform/hash-browser.ts","../../src/core/seed.ts","../../src/layers/chain.ts","../../src/layers/commit.ts","../../src/layers/repo.ts","../../src/layers/storage.ts","../../src/layers/client.ts","../../src/browser.ts"],"names":["sdkCodeIn","createTable","writeRow","tablePda"],"mappings":"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","file":"index.js","sourcesContent":["// SHA-256 content hashing for the blob-dedup index. Runtime-specific\n// implementations live in `platform/hash-node.ts` and `platform/hash-browser.ts`;\n// this module just declares the interface the rest of the SDK uses.\n//\n// Do NOT use this for deriving table seeds — that path is owned by\n// `iqlabs.utils.toSeedBytes` (keccak) and must stay aligned with the SDK.\n\n/**\n * SHA-256 of a UTF-8 or base64 string, returned as a lowercase hex digest.\n * Implementation is injected by the platform entry (node.ts / browser.ts).\n */\nexport type Sha256Hex = (input: string) => Promise<string>;\n\n// Platform entries (src/node.ts, src/browser.ts) will call `setSha256(...)`\n// during module init so downstream layers never care which runtime they are in.\n// This indirection is the one place where a wrapper is justified (CODE-RULES §1)\n// because the behavior genuinely differs per runtime.\nlet impl: Sha256Hex | undefined;\n\nexport function setSha256(fn: Sha256Hex): void {\n if (impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 implementation already installed. Import exactly one of '@iqlabs-official/git-sdk/node' or '@iqlabs-official/git-sdk/browser'.\",\n );\n }\n impl = fn;\n}\n\nexport async function sha256Hex(input: string): Promise<string> {\n if (!impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 not installed. Import '@iqlabs-official/git-sdk/node' (Node) or '@iqlabs-official/git-sdk/browser' (browser) before using the SDK.\",\n );\n }\n return impl(input);\n}\n","// Browser SHA-256 implementation. Kept as a pure export; `src/browser.ts`\n// installs it via `setSha256` during its own module init.\n\nimport type { Sha256Hex } from \"../core/hash\";\n\nexport const hashBrowser: Sha256Hex = async (input) => {\n const bytes = new TextEncoder().encode(input);\n const digest = await crypto.subtle.digest(\n \"SHA-256\",\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer,\n );\n return Array.from(new Uint8Array(digest))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n};\n","// The single source of truth for table_hint strings.\n//\n// Readers re-derive PDAs with `iqlabs.utils.toSeedBytes(hint)` →\n// `iqlabs.contract.getTablePda(...)`; writers pass the same hint into\n// `iqlabs.writer.createTable`. Keeping the naming convention in one place\n// prevents silent drift between writer and reader. CODE-RULES §2 — if any\n// caller ever wants to build one of these strings inline, route it here\n// instead.\n\n/** DbRoot id for every iq-git table. Bootstrap and every caller share this. */\nexport const IQGIT_ROOT_ID = \"iq-git-v1\";\n\n/** `git_repos:all` — open-writers registry that drives the public gallery. */\nexport const REGISTRY_HINT = \"git_repos:all\";\n\n/**\n * Hint for the per-owner personal repo list.\n * input: owner wallet base58\n * output: \"git_repos_v2_<owner>\"\n */\nexport function repoListHint(owner: string): string {\n return `git_repos_v2_${owner}`;\n}\n\n/**\n * Hint for the per-repo commit table.\n * input: owner wallet base58, repo name (any characters — SDK keccak-hashes)\n * output: \"git_commits:<owner>:<repo>\"\n */\nexport function commitTableHint(owner: string, repo: string): string {\n return `git_commits:${owner}:${repo}`;\n}\n","// L1 — chain primitives shared by L2/L3. Kept tiny by design: each function\n// here either (a) hides PDA derivation so callers only deal with hints, or\n// (b) wraps a single iqlabs-sdk call to keep an iq-git-specific default\n// (chunk speed, root id, ...). Pure passthroughs like `createTable` /\n// `writeRow` live at the call site instead — wrapping them once more would\n// just bury the iqlabs-sdk surface (CODE-RULES §1).\n\nimport {\n Keypair,\n SystemProgram,\n Transaction,\n type Connection,\n type PublicKey,\n} from \"@solana/web3.js\";\nimport {\n createInstructionBuilder,\n getDbRootPda,\n getTablePda,\n initializeDbRootInstruction,\n} from \"@iqlabs-official/solana-sdk/contract\";\nimport { readCodeIn, readTableRows } from \"@iqlabs-official/solana-sdk/reader\";\nimport { toSeedBytes, type SignerInput, type WalletSigner } from \"@iqlabs-official/solana-sdk/utils\";\nimport { codeIn as sdkCodeIn } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID } from \"../core/seed\";\n\n/** DbRoot PDA for the `iq-git-v1` namespace — derived once, reused everywhere. */\nexport const DB_ROOT_SEED = toSeedBytes(IQGIT_ROOT_ID);\nexport const DB_ROOT = getDbRootPda(DB_ROOT_SEED);\n\n/**\n * Initialize the `iq-git-v1` DbRoot account if it doesn't exist. First-call\n * cost on a fresh network. Idempotent: returns null if already initialized.\n * Accepts any SignerInput so an admin can run it from a wallet, not just\n * from a Keypair.\n */\nexport async function ensureDbRoot(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n if (await accountExists(connection, DB_ROOT)) return null;\n const builder = createInstructionBuilder();\n const ix = initializeDbRootInstruction(\n builder,\n {\n db_root: DB_ROOT,\n signer: signer.publicKey,\n system_program: SystemProgram.programId,\n },\n { db_root_id: DB_ROOT_SEED },\n );\n const tx = new Transaction().add(ix);\n const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();\n tx.recentBlockhash = blockhash;\n tx.feePayer = signer.publicKey;\n const signed = await signTx(signer, tx);\n const signature = await connection.sendRawTransaction(signed.serialize());\n await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });\n return signature;\n}\n\n// Sign a single Transaction with whatever shape the signer takes. Keypair\n// has a `secretKey`; wallet adapters expose `signTransaction`. iqlabs-sdk's\n// own `sendTx` uses the same dispatch; we replicate it here so the helper\n// stays self-contained.\nasync function signTx(signer: SignerInput, tx: Transaction): Promise<Transaction> {\n if (signer instanceof Keypair || \"secretKey\" in signer) {\n tx.partialSign(signer as Keypair);\n return tx;\n }\n return (signer as WalletSigner).signTransaction(tx);\n}\n\n/**\n * Read rows from a table. Translates our hint into the PDA and forwards to\n * `iqlabs.reader.readTableRows`.\n */\nexport async function readRows(\n hint: string,\n options?: { limit?: number; before?: string },\n): Promise<Array<Record<string, unknown>>> {\n return readTableRows(tablePda(hint), options);\n}\n\n/**\n * Fetch just the latest row of a table — the fast path for \"what is the\n * current commit\" and \"what is the pinned deploy\".\n */\nexport async function readLatestRow(\n hint: string,\n): Promise<Record<string, unknown> | null> {\n const rows = await readRows(hint, { limit: 1 });\n return rows[0] ?? null;\n}\n\n/**\n * Upload a blob via `iqlabs.writer.codeIn`. The SDK chunks internally when\n * `data` is a plain string. We default speed to \"light\" because that's the\n * Helius-friendly setting for git workloads (per-file uploads are bursty).\n */\nexport async function codeIn(\n connection: Connection,\n signer: SignerInput,\n data: string | string[],\n filename: string,\n filetype: string,\n onProgress?: (percent: number) => void,\n speed: \"light\" | \"medium\" | \"fast\" = \"light\",\n): Promise<string> {\n return sdkCodeIn(\n { connection, signer },\n data,\n filename,\n 0,\n filetype,\n onProgress,\n speed,\n );\n}\n\nexport { readCodeIn };\n\n/** Cheap existence check for a PDA. */\nexport async function accountExists(\n connection: Connection,\n pda: PublicKey,\n): Promise<boolean> {\n return (await connection.getAccountInfo(pda)) !== null;\n}\n\n/** Resolve a hint to its PDA. */\nexport function tablePda(hint: string): PublicKey {\n return getTablePda(DB_ROOT, toSeedBytes(hint));\n}\n","// L3 — per-repo commit table.\n//\n// v2's core invariant: each repo has its own table at hint\n// `git_commits:<owner>:<repo>`, with writers locked to [owner]. That means\n// \"the most recent successful tx in this table = the latest commit\", and we\n// read it as a single-row query (limit: 1).\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, commitTableHint } from \"../core/seed\";\nimport type { Commit } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst COMMIT_COLUMNS = [\n \"id\",\n \"message\",\n \"treeTxId\",\n \"parentCommitId\",\n \"timestamp\",\n \"author\",\n];\n\n/**\n * Ensure the per-repo commit table exists with writers = [owner]. No-op if\n * it already exists.\n */\nexport async function ensureCommitTable(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n): Promise<string | null> {\n const hint = commitTableHint(signer.publicKey.toBase58(), repo);\n if (await chain.accountExists(connection, chain.tablePda(hint))) return null;\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n hint,\n hint,\n COMMIT_COLUMNS,\n \"id\",\n [],\n undefined,\n [signer.publicKey],\n hint,\n );\n}\n\n/**\n * Append one commit row. Callers (workflow-level code) are responsible for\n * setting parentCommitId — the SDK does not auto-chain.\n */\nexport async function writeCommit(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n commit: Commit,\n): Promise<string> {\n const owner = signer.publicKey.toBase58();\n return writeRow(\n connection,\n signer,\n IQGIT_ROOT_ID,\n commitTableHint(owner, repo),\n JSON.stringify(commit),\n );\n}\n\n/** Latest commit. Single-row, O(1) RPC path. */\nexport async function readLatestCommit(\n _connection: Connection,\n owner: string,\n repo: string,\n): Promise<Commit | null> {\n const row = await chain.readLatestRow(commitTableHint(owner, repo));\n return row as unknown as Commit | null;\n}\n\n/** Full commit history, newest first. */\nexport async function readCommitHistory(\n _connection: Connection,\n owner: string,\n repo: string,\n options?: { limit?: number; before?: string },\n): Promise<Commit[]> {\n const rows = await chain.readRows(commitTableHint(owner, repo), options);\n return rows as unknown as Commit[];\n}\n","// L3 — repo list and public registry.\n//\n// Covers the two \"directory\" tables:\n// • git_repos_v2_<owner> — owner's personal repo list (writers = [owner])\n// • git_repos:all — public gallery registry (writers = [])\n//\n// Commit tables are a separate concern — see `commit.ts`.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, REGISTRY_HINT, repoListHint } from \"../core/seed\";\nimport type { RegistryEntry, Repository } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst REPO_COLUMNS = [\"name\", \"description\", \"isPublic\", \"timestamp\"];\nconst REGISTRY_COLUMNS = [\"owner\", \"repo\", \"description\", \"timestamp\"];\n\n/**\n * Create a repo in the owner's personal list, and (if public) also register\n * it in the public gallery. Two transactions — the contract does not support\n * writing two tables atomically.\n *\n * Returns the writeRow tx signatures (and the table hint each one wrote to)\n * so a caller can notify gateways / build SSE streams for those tables. The\n * returned shape is `[]` from `createTable` calls because those don't write\n * a row, just allocate the account.\n */\nexport async function createRepo(\n connection: Connection,\n signer: SignerInput,\n meta: Repository,\n): Promise<Array<{ tableHint: string; sig: string; row: object }>> {\n const owner = signer.publicKey.toBase58();\n const listHint = repoListHint(owner);\n const writes: Array<{ tableHint: string; sig: string; row: object }> = [];\n\n if (!(await chain.accountExists(connection, chain.tablePda(listHint)))) {\n await createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n listHint,\n listHint,\n REPO_COLUMNS,\n \"name\",\n [],\n undefined,\n [signer.publicKey],\n listHint,\n );\n }\n\n const sig = await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));\n writes.push({ tableHint: listHint, sig, row: meta });\n\n if (meta.isPublic) {\n const entry: RegistryEntry = {\n owner,\n repo: meta.name,\n description: meta.description,\n timestamp: meta.timestamp,\n };\n const regSig = await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));\n writes.push({ tableHint: REGISTRY_HINT, sig: regSig, row: entry });\n }\n\n return writes;\n}\n\n/** List all repos owned by `owner`. */\nexport async function readOwnerRepos(\n _connection: Connection,\n owner: string,\n): Promise<Repository[]> {\n return (await chain.readRows(repoListHint(owner))) as unknown as Repository[];\n}\n\n/**\n * One page of the public-gallery registry. Callers should still check\n * `row.owner` shape before trusting it in UI; we do not filter at the SDK\n * boundary.\n */\nexport async function readRegistryPage(\n _connection: Connection,\n options?: { limit?: number; before?: string },\n): Promise<RegistryEntry[]> {\n return (await chain.readRows(REGISTRY_HINT, options)) as unknown as RegistryEntry[];\n}\n\n/**\n * One-time global bootstrap of the `git_repos:all` table. Run once per\n * network from an admin key; subsequent calls short-circuit because the\n * account already exists.\n */\nexport async function bootstrapRegistry(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n await chain.ensureDbRoot(connection, signer);\n if (await chain.accountExists(connection, chain.tablePda(REGISTRY_HINT))) {\n return null;\n }\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n REGISTRY_HINT,\n REGISTRY_HINT,\n REGISTRY_COLUMNS,\n \"owner\",\n [],\n undefined,\n undefined,\n REGISTRY_HINT,\n );\n}\n","// L2 — blob + tree storage.\n//\n// `uploadBlob` is the file-level \"don't re-upload what's already on-chain\"\n// primitive. Content is always a base64 string (matching iq-git v1) so the\n// hash / dedup comparison stays byte-for-byte compatible across commits.\n// The reuse-map is just a plain object so callers can build it any way they\n// like (path→entry, or hash→txId for rename-aware dedup — CODE-RULES §3\n// keeps this inlined at the call site instead of a new type).\n//\n// `uploadTree` / `loadTree` serialize FileTree <-> on-chain tree.json as raw\n// JSON (filetype `application/json`), again matching v1.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport type { FileTree } from \"../core/types\";\nimport * as chain from \"./chain\";\n\n/**\n * Upload one file unless an identical hash is already in `reuse` (blob dedup).\n *\n * input: connection, signer, relativePath, base64 content, reuse map, optional onProgress\n * output: { txId, hash } — either reused or freshly uploaded\n */\nexport async function uploadBlob(\n connection: Connection,\n signer: SignerInput,\n relativePath: string,\n base64Content: string,\n reuse: FileTree,\n onProgress?: (percent: number) => void,\n): Promise<{ txId: string; hash: string }> {\n const hash = await sha256Hex(base64Content);\n const prior = reuse[relativePath];\n if (prior && prior.hash === hash) return prior;\n\n // Tag the codeIn filename with our app marker so non-inline inventory\n // entries can be classified by inspecting metadata.filename alone (no row\n // body fetch needed). Format: \"iqgit-blob:<basename>\". Same scheme used\n // for trees (\"iqgit-tree\") and other writes across the IQ ecosystem.\n const basename = relativePath.split(\"/\").pop() || relativePath;\n const txId = await chain.codeIn(\n connection,\n signer,\n base64Content,\n `iqgit-blob:${basename}`,\n \"application/octet-stream\",\n onProgress,\n );\n return { txId, hash };\n}\n\n/**\n * Serialize a FileTree and upload it as one `tree.json` blob.\n */\nexport async function uploadTree(\n connection: Connection,\n signer: SignerInput,\n tree: FileTree,\n): Promise<string> {\n return chain.codeIn(\n connection,\n signer,\n JSON.stringify(tree),\n \"iqgit-tree\",\n \"application/json\",\n );\n}\n\n/**\n * Fetch and parse a `tree.json` blob by its tx signature.\n */\nexport async function loadTree(treeTxId: string): Promise<FileTree> {\n const { data } = await chain.readCodeIn(treeTxId);\n if (data === null) {\n throw new Error(`tree.json not found for tx ${treeTxId}`);\n }\n return JSON.parse(data) as FileTree;\n}\n\n/**\n * Retrieve a blob's bytes by its txId. Returns the raw string that was\n * uploaded — for blobs this is base64; the caller decodes.\n */\nexport async function loadBlob(txId: string): Promise<string> {\n const { data } = await chain.readCodeIn(txId);\n if (data === null) {\n throw new Error(`blob not found for tx ${txId}`);\n }\n return data;\n}\n","// L4 — GitClient facade. High-level workflows built by composing the\n// layers below. This is the surface consumers (CLI, frontend, migrator) use;\n// they should not reach into `chain` / `storage` / `repo` / `commit` directly.\n//\n// Keep workflow logic here, not inside lower layers (CODE-RULES §5). When a\n// command needs scanning, hashing, uploading, row writing — this file\n// orchestrates them all.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport { commitTableHint } from \"../core/seed\";\nimport type { Commit, FileTree, Repository } from \"../core/types\";\nimport * as chain from \"./chain\";\nimport * as commitLayer from \"./commit\";\nimport * as repoLayer from \"./repo\";\nimport * as storage from \"./storage\";\n\n/** Information surfaced to the `onWrite` callback for every successful row\n * write the SDK performs. Consumers wire this to their gateway notify so\n * the gateway updates its cache / SSE stream immediately. */\nexport interface WriteEvent {\n /** Resolved table PDA, base58 — pass directly to gateway notify. */\n tablePda: string;\n /** The hint that derived the PDA, in case callers want to log it. */\n tableHint: string;\n /** writeRow tx signature. */\n sig: string;\n /** The row object that was written (already JSON.stringify-able). */\n row: object;\n}\n\nexport interface GitClientConfig {\n connection: Connection;\n signer: SignerInput;\n /** Fires after each successful row write inside the SDK. Fire-and-forget\n * callback for gateway notifies; throwing here will surface to the caller. */\n onWrite?: (event: WriteEvent) => void;\n}\n\nexport class GitClient {\n // CODE-RULES §3 — only one small shape; inlined rather than aliased.\n constructor(private readonly cfg: GitClientConfig) {}\n\n /**\n * Create a new repo. Wraps repo.createRepo + pre-creating the commit table\n * so the first `commit()` call doesn't need to pay createTable cost.\n */\n async createRepo(meta: Repository): Promise<void> {\n const { connection, signer } = this.cfg;\n const writes = await repoLayer.createRepo(connection, signer, meta);\n await commitLayer.ensureCommitTable(connection, signer, meta.name);\n this.fireOnWrite(writes);\n }\n\n /**\n * Incremental commit against a scanned directory snapshot.\n *\n * input: repoName, message, scan — a `{ [path]: base64Content }` map that\n * the caller produced (Node: fs walk + base64; browser: File input).\n * output: newly written Commit\n */\n async commit(\n repoName: string,\n message: string,\n scan: Record<string, string>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n const latest = await commitLayer.readLatestCommit(connection, owner, repoName);\n const oldTree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const newTree: FileTree = {};\n for (const [path, content] of Object.entries(scan)) {\n newTree[path] = await storage.uploadBlob(\n connection,\n signer,\n path,\n content,\n oldTree,\n );\n }\n\n const treeTxId = await storage.uploadTree(connection, signer, newTree);\n\n const commit: Commit = {\n id: crypto.randomUUID(),\n message,\n treeTxId,\n parentCommitId: latest?.id,\n timestamp: Date.now(),\n author: owner,\n };\n const sig = await commitLayer.writeCommit(connection, signer, repoName, commit);\n this.fireOnWrite([{ tableHint: commitTableHint(owner, repoName), sig, row: commit }]);\n return commit;\n }\n\n /** Resolve the table hint to a PDA and forward each write to `onWrite`.\n * Wrapped in try/catch per call so a misbehaving notify can't fail the\n * whole batch — but we still surface the error so the consumer knows. */\n private fireOnWrite(writes: Array<{ tableHint: string; sig: string; row: object }>) {\n if (!this.cfg.onWrite) return;\n for (const w of writes) {\n const tablePda = chain.tablePda(w.tableHint).toBase58();\n this.cfg.onWrite({ tablePda, tableHint: w.tableHint, sig: w.sig, row: w.row });\n }\n }\n\n /**\n * Restore a commit's files into a caller-provided sink. Runtime-neutral\n * shape mirrors `commit`: the caller decides how to write bytes to disk /\n * to a File System Access API handle / anywhere else. `content` is the\n * raw base64 string — the sink decodes.\n */\n async checkout(\n repoName: string,\n commitId: string | \"latest\",\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n let target: Commit | null;\n if (commitId === \"latest\") {\n target = await commitLayer.readLatestCommit(connection, owner, repoName);\n } else {\n const history = await commitLayer.readCommitHistory(connection, owner, repoName);\n target = history.find((c) => c.id === commitId) ?? null;\n }\n if (!target) {\n throw new Error(`commit not found: ${commitId} in ${owner}/${repoName}`);\n }\n\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /**\n * Whole-repo snapshot download — convenience on top of checkout(\"latest\")\n * but reading from someone else's `owner`. We do not require signer to\n * equal owner for reads.\n */\n async clone(\n repoName: string,\n owner: string,\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection } = this.cfg;\n const target = await commitLayer.readLatestCommit(connection, owner, repoName);\n if (!target) {\n throw new Error(`no commits in ${owner}/${repoName}`);\n }\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /** Commit history for a repo. */\n async log(\n owner: string,\n repoName: string,\n options?: { limit?: number; before?: string },\n ): Promise<Commit[]> {\n return commitLayer.readCommitHistory(\n this.cfg.connection,\n owner,\n repoName,\n options,\n );\n }\n\n /**\n * Compare a current directory snapshot to the latest commit's tree.\n */\n async status(\n owner: string,\n repoName: string,\n scan: Record<string, string>,\n ): Promise<{ added: string[]; modified: string[]; unchanged: string[] }> {\n const latest = await commitLayer.readLatestCommit(\n this.cfg.connection,\n owner,\n repoName,\n );\n const tree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const added: string[] = [];\n const modified: string[] = [];\n const unchanged: string[] = [];\n for (const [path, content] of Object.entries(scan)) {\n const prior = tree[path];\n if (!prior) {\n added.push(path);\n continue;\n }\n const hash = await sha256Hex(content);\n (prior.hash === hash ? unchanged : modified).push(path);\n }\n return { added, modified, unchanged };\n }\n}\n","// Browser entry. Installs the Web-Crypto SHA-256 hasher, then re-exports\n// everything from the shared root.\n\nimport { setSha256 } from \"./core/hash\";\nimport { hashBrowser } from \"./platform/hash-browser\";\n\nsetSha256(hashBrowser);\n\nexport * from \"./index\";\n"]}
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { Commit, FileTree, GitClient, GitClientConfig, IQGIT_ROOT_ID, REGISTRY_HINT, RegistryEntry, Repository, bootstrapRegistry, commitTableHint, loadBlob, loadTree, readCommitHistory, readLatestCommit, readOwnerRepos, readRegistryPage, repoListHint } from '../shared/index.js';
|
|
1
|
+
export { Commit, FileTree, GitClient, GitClientConfig, IQGIT_ROOT_ID, REGISTRY_HINT, RegistryEntry, Repository, WriteEvent, bootstrapRegistry, commitTableHint, loadBlob, loadTree, readCommitHistory, readLatestCommit, readOwnerRepos, readRegistryPage, repoListHint } from '../shared/index.js';
|
|
2
2
|
export { SignerInput } from '@iqlabs-official/solana-sdk/utils';
|
|
3
3
|
import '@solana/web3.js';
|
package/dist/node/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
|
-
import { createTable, writeRow, codeIn as codeIn$1 } from '@iqlabs-official/solana-sdk/writer';
|
|
3
2
|
import { SystemProgram, Transaction, Keypair } from '@solana/web3.js';
|
|
4
3
|
import { getDbRootPda, createInstructionBuilder, initializeDbRootInstruction, getTablePda } from '@iqlabs-official/solana-sdk/contract';
|
|
5
4
|
import { readCodeIn, readTableRows } from '@iqlabs-official/solana-sdk/reader';
|
|
6
5
|
import { toSeedBytes } from '@iqlabs-official/solana-sdk/utils';
|
|
6
|
+
import { createTable, writeRow, codeIn as codeIn$1 } from '@iqlabs-official/solana-sdk/writer';
|
|
7
7
|
|
|
8
8
|
// src/core/hash.ts
|
|
9
9
|
var impl;
|
|
@@ -88,8 +88,6 @@ async function accountExists(connection, pda) {
|
|
|
88
88
|
function tablePda(hint) {
|
|
89
89
|
return getTablePda(DB_ROOT, toSeedBytes(hint));
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
// src/layers/commit.ts
|
|
93
91
|
var COMMIT_COLUMNS = [
|
|
94
92
|
"id",
|
|
95
93
|
"message",
|
|
@@ -138,6 +136,7 @@ var REGISTRY_COLUMNS = ["owner", "repo", "description", "timestamp"];
|
|
|
138
136
|
async function createRepo(connection, signer, meta) {
|
|
139
137
|
const owner = signer.publicKey.toBase58();
|
|
140
138
|
const listHint = repoListHint(owner);
|
|
139
|
+
const writes = [];
|
|
141
140
|
if (!await accountExists(connection, tablePda(listHint))) {
|
|
142
141
|
await createTable(
|
|
143
142
|
connection,
|
|
@@ -153,7 +152,8 @@ async function createRepo(connection, signer, meta) {
|
|
|
153
152
|
listHint
|
|
154
153
|
);
|
|
155
154
|
}
|
|
156
|
-
await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));
|
|
155
|
+
const sig = await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));
|
|
156
|
+
writes.push({ tableHint: listHint, sig, row: meta });
|
|
157
157
|
if (meta.isPublic) {
|
|
158
158
|
const entry = {
|
|
159
159
|
owner,
|
|
@@ -161,8 +161,10 @@ async function createRepo(connection, signer, meta) {
|
|
|
161
161
|
description: meta.description,
|
|
162
162
|
timestamp: meta.timestamp
|
|
163
163
|
};
|
|
164
|
-
await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));
|
|
164
|
+
const regSig = await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));
|
|
165
|
+
writes.push({ tableHint: REGISTRY_HINT, sig: regSig, row: entry });
|
|
165
166
|
}
|
|
167
|
+
return writes;
|
|
166
168
|
}
|
|
167
169
|
async function readOwnerRepos(_connection, owner) {
|
|
168
170
|
return await readRows(repoListHint(owner));
|
|
@@ -195,12 +197,12 @@ async function uploadBlob(connection, signer, relativePath, base64Content, reuse
|
|
|
195
197
|
const hash = await sha256Hex(base64Content);
|
|
196
198
|
const prior = reuse[relativePath];
|
|
197
199
|
if (prior && prior.hash === hash) return prior;
|
|
198
|
-
const
|
|
200
|
+
const basename = relativePath.split("/").pop() || relativePath;
|
|
199
201
|
const txId = await codeIn(
|
|
200
202
|
connection,
|
|
201
203
|
signer,
|
|
202
204
|
base64Content,
|
|
203
|
-
|
|
205
|
+
`iqgit-blob:${basename}`,
|
|
204
206
|
"application/octet-stream",
|
|
205
207
|
onProgress
|
|
206
208
|
);
|
|
@@ -211,7 +213,7 @@ async function uploadTree(connection, signer, tree) {
|
|
|
211
213
|
connection,
|
|
212
214
|
signer,
|
|
213
215
|
JSON.stringify(tree),
|
|
214
|
-
"tree
|
|
216
|
+
"iqgit-tree",
|
|
215
217
|
"application/json"
|
|
216
218
|
);
|
|
217
219
|
}
|
|
@@ -243,8 +245,9 @@ var GitClient = class {
|
|
|
243
245
|
*/
|
|
244
246
|
async createRepo(meta) {
|
|
245
247
|
const { connection, signer } = this.cfg;
|
|
246
|
-
await createRepo(connection, signer, meta);
|
|
248
|
+
const writes = await createRepo(connection, signer, meta);
|
|
247
249
|
await ensureCommitTable(connection, signer, meta.name);
|
|
250
|
+
this.fireOnWrite(writes);
|
|
248
251
|
}
|
|
249
252
|
/**
|
|
250
253
|
* Incremental commit against a scanned directory snapshot.
|
|
@@ -277,9 +280,20 @@ var GitClient = class {
|
|
|
277
280
|
timestamp: Date.now(),
|
|
278
281
|
author: owner
|
|
279
282
|
};
|
|
280
|
-
await writeCommit(connection, signer, repoName, commit);
|
|
283
|
+
const sig = await writeCommit(connection, signer, repoName, commit);
|
|
284
|
+
this.fireOnWrite([{ tableHint: commitTableHint(owner, repoName), sig, row: commit }]);
|
|
281
285
|
return commit;
|
|
282
286
|
}
|
|
287
|
+
/** Resolve the table hint to a PDA and forward each write to `onWrite`.
|
|
288
|
+
* Wrapped in try/catch per call so a misbehaving notify can't fail the
|
|
289
|
+
* whole batch — but we still surface the error so the consumer knows. */
|
|
290
|
+
fireOnWrite(writes) {
|
|
291
|
+
if (!this.cfg.onWrite) return;
|
|
292
|
+
for (const w of writes) {
|
|
293
|
+
const tablePda2 = tablePda(w.tableHint).toBase58();
|
|
294
|
+
this.cfg.onWrite({ tablePda: tablePda2, tableHint: w.tableHint, sig: w.sig, row: w.row });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
283
297
|
/**
|
|
284
298
|
* Restore a commit's files into a caller-provided sink. Runtime-neutral
|
|
285
299
|
* shape mirrors `commit`: the caller decides how to write bytes to disk /
|
package/dist/node/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/hash.ts","../../src/platform/hash-node.ts","../../src/core/seed.ts","../../src/layers/chain.ts","../../src/layers/commit.ts","../../src/layers/repo.ts","../../src/layers/storage.ts","../../src/layers/client.ts","../../src/node.ts"],"names":["sdkCodeIn","createTable","writeRow"],"mappings":"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","file":"index.js","sourcesContent":["// SHA-256 content hashing for the blob-dedup index. Runtime-specific\n// implementations live in `platform/hash-node.ts` and `platform/hash-browser.ts`;\n// this module just declares the interface the rest of the SDK uses.\n//\n// Do NOT use this for deriving table seeds — that path is owned by\n// `iqlabs.utils.toSeedBytes` (keccak) and must stay aligned with the SDK.\n\n/**\n * SHA-256 of a UTF-8 or base64 string, returned as a lowercase hex digest.\n * Implementation is injected by the platform entry (node.ts / browser.ts).\n */\nexport type Sha256Hex = (input: string) => Promise<string>;\n\n// Platform entries (src/node.ts, src/browser.ts) will call `setSha256(...)`\n// during module init so downstream layers never care which runtime they are in.\n// This indirection is the one place where a wrapper is justified (CODE-RULES §1)\n// because the behavior genuinely differs per runtime.\nlet impl: Sha256Hex | undefined;\n\nexport function setSha256(fn: Sha256Hex): void {\n if (impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 implementation already installed. Import exactly one of '@iqlabs-official/git-sdk/node' or '@iqlabs-official/git-sdk/browser'.\",\n );\n }\n impl = fn;\n}\n\nexport async function sha256Hex(input: string): Promise<string> {\n if (!impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 not installed. Import '@iqlabs-official/git-sdk/node' (Node) or '@iqlabs-official/git-sdk/browser' (browser) before using the SDK.\",\n );\n }\n return impl(input);\n}\n","// Node SHA-256 implementation. Kept as a pure export; `src/node.ts` installs\n// it via `setSha256` during its own module init.\n\nimport { createHash } from \"node:crypto\";\nimport type { Sha256Hex } from \"../core/hash\";\n\nexport const hashNode: Sha256Hex = async (input) =>\n createHash(\"sha256\").update(input).digest(\"hex\");\n","// The single source of truth for table_hint strings.\n//\n// Readers re-derive PDAs with `iqlabs.utils.toSeedBytes(hint)` →\n// `iqlabs.contract.getTablePda(...)`; writers pass the same hint into\n// `iqlabs.writer.createTable`. Keeping the naming convention in one place\n// prevents silent drift between writer and reader. CODE-RULES §2 — if any\n// caller ever wants to build one of these strings inline, route it here\n// instead.\n\n/** DbRoot id for every iq-git table. Bootstrap and every caller share this. */\nexport const IQGIT_ROOT_ID = \"iq-git-v1\";\n\n/** `git_repos:all` — open-writers registry that drives the public gallery. */\nexport const REGISTRY_HINT = \"git_repos:all\";\n\n/**\n * Hint for the per-owner personal repo list.\n * input: owner wallet base58\n * output: \"git_repos_v2_<owner>\"\n */\nexport function repoListHint(owner: string): string {\n return `git_repos_v2_${owner}`;\n}\n\n/**\n * Hint for the per-repo commit table.\n * input: owner wallet base58, repo name (any characters — SDK keccak-hashes)\n * output: \"git_commits:<owner>:<repo>\"\n */\nexport function commitTableHint(owner: string, repo: string): string {\n return `git_commits:${owner}:${repo}`;\n}\n","// L1 — chain primitives shared by L2/L3. Kept tiny by design: each function\n// here either (a) hides PDA derivation so callers only deal with hints, or\n// (b) wraps a single iqlabs-sdk call to keep an iq-git-specific default\n// (chunk speed, root id, ...). Pure passthroughs like `createTable` /\n// `writeRow` live at the call site instead — wrapping them once more would\n// just bury the iqlabs-sdk surface (CODE-RULES §1).\n\nimport {\n Keypair,\n SystemProgram,\n Transaction,\n type Connection,\n type PublicKey,\n} from \"@solana/web3.js\";\nimport {\n createInstructionBuilder,\n getDbRootPda,\n getTablePda,\n initializeDbRootInstruction,\n} from \"@iqlabs-official/solana-sdk/contract\";\nimport { readCodeIn, readTableRows } from \"@iqlabs-official/solana-sdk/reader\";\nimport { toSeedBytes, type SignerInput, type WalletSigner } from \"@iqlabs-official/solana-sdk/utils\";\nimport { codeIn as sdkCodeIn } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID } from \"../core/seed\";\n\n/** DbRoot PDA for the `iq-git-v1` namespace — derived once, reused everywhere. */\nexport const DB_ROOT_SEED = toSeedBytes(IQGIT_ROOT_ID);\nexport const DB_ROOT = getDbRootPda(DB_ROOT_SEED);\n\n/**\n * Initialize the `iq-git-v1` DbRoot account if it doesn't exist. First-call\n * cost on a fresh network. Idempotent: returns null if already initialized.\n * Accepts any SignerInput so an admin can run it from a wallet, not just\n * from a Keypair.\n */\nexport async function ensureDbRoot(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n if (await accountExists(connection, DB_ROOT)) return null;\n const builder = createInstructionBuilder();\n const ix = initializeDbRootInstruction(\n builder,\n {\n db_root: DB_ROOT,\n signer: signer.publicKey,\n system_program: SystemProgram.programId,\n },\n { db_root_id: DB_ROOT_SEED },\n );\n const tx = new Transaction().add(ix);\n const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();\n tx.recentBlockhash = blockhash;\n tx.feePayer = signer.publicKey;\n const signed = await signTx(signer, tx);\n const signature = await connection.sendRawTransaction(signed.serialize());\n await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });\n return signature;\n}\n\n// Sign a single Transaction with whatever shape the signer takes. Keypair\n// has a `secretKey`; wallet adapters expose `signTransaction`. iqlabs-sdk's\n// own `sendTx` uses the same dispatch; we replicate it here so the helper\n// stays self-contained.\nasync function signTx(signer: SignerInput, tx: Transaction): Promise<Transaction> {\n if (signer instanceof Keypair || \"secretKey\" in signer) {\n tx.partialSign(signer as Keypair);\n return tx;\n }\n return (signer as WalletSigner).signTransaction(tx);\n}\n\n/**\n * Read rows from a table. Translates our hint into the PDA and forwards to\n * `iqlabs.reader.readTableRows`.\n */\nexport async function readRows(\n hint: string,\n options?: { limit?: number; before?: string },\n): Promise<Array<Record<string, unknown>>> {\n return readTableRows(tablePda(hint), options);\n}\n\n/**\n * Fetch just the latest row of a table — the fast path for \"what is the\n * current commit\" and \"what is the pinned deploy\".\n */\nexport async function readLatestRow(\n hint: string,\n): Promise<Record<string, unknown> | null> {\n const rows = await readRows(hint, { limit: 1 });\n return rows[0] ?? null;\n}\n\n/**\n * Upload a blob via `iqlabs.writer.codeIn`. The SDK chunks internally when\n * `data` is a plain string. We default speed to \"light\" because that's the\n * Helius-friendly setting for git workloads (per-file uploads are bursty).\n */\nexport async function codeIn(\n connection: Connection,\n signer: SignerInput,\n data: string | string[],\n filename: string,\n filetype: string,\n onProgress?: (percent: number) => void,\n speed: \"light\" | \"medium\" | \"fast\" = \"light\",\n): Promise<string> {\n return sdkCodeIn(\n { connection, signer },\n data,\n filename,\n 0,\n filetype,\n onProgress,\n speed,\n );\n}\n\nexport { readCodeIn };\n\n/** Cheap existence check for a PDA. */\nexport async function accountExists(\n connection: Connection,\n pda: PublicKey,\n): Promise<boolean> {\n return (await connection.getAccountInfo(pda)) !== null;\n}\n\n/** Resolve a hint to its PDA. */\nexport function tablePda(hint: string): PublicKey {\n return getTablePda(DB_ROOT, toSeedBytes(hint));\n}\n","// L3 — per-repo commit table.\n//\n// v2's core invariant: each repo has its own table at hint\n// `git_commits:<owner>:<repo>`, with writers locked to [owner]. That means\n// \"the most recent successful tx in this table = the latest commit\", and we\n// read it as a single-row query (limit: 1).\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, commitTableHint } from \"../core/seed\";\nimport type { Commit } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst COMMIT_COLUMNS = [\n \"id\",\n \"message\",\n \"treeTxId\",\n \"parentCommitId\",\n \"timestamp\",\n \"author\",\n];\n\n/**\n * Ensure the per-repo commit table exists with writers = [owner]. No-op if\n * it already exists.\n */\nexport async function ensureCommitTable(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n): Promise<string | null> {\n const hint = commitTableHint(signer.publicKey.toBase58(), repo);\n if (await chain.accountExists(connection, chain.tablePda(hint))) return null;\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n hint,\n hint,\n COMMIT_COLUMNS,\n \"id\",\n [],\n undefined,\n [signer.publicKey],\n hint,\n );\n}\n\n/**\n * Append one commit row. Callers (workflow-level code) are responsible for\n * setting parentCommitId — the SDK does not auto-chain.\n */\nexport async function writeCommit(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n commit: Commit,\n): Promise<string> {\n const owner = signer.publicKey.toBase58();\n return writeRow(\n connection,\n signer,\n IQGIT_ROOT_ID,\n commitTableHint(owner, repo),\n JSON.stringify(commit),\n );\n}\n\n/** Latest commit. Single-row, O(1) RPC path. */\nexport async function readLatestCommit(\n _connection: Connection,\n owner: string,\n repo: string,\n): Promise<Commit | null> {\n const row = await chain.readLatestRow(commitTableHint(owner, repo));\n return row as unknown as Commit | null;\n}\n\n/** Full commit history, newest first. */\nexport async function readCommitHistory(\n _connection: Connection,\n owner: string,\n repo: string,\n options?: { limit?: number; before?: string },\n): Promise<Commit[]> {\n const rows = await chain.readRows(commitTableHint(owner, repo), options);\n return rows as unknown as Commit[];\n}\n","// L3 — repo list and public registry.\n//\n// Covers the two \"directory\" tables:\n// • git_repos_v2_<owner> — owner's personal repo list (writers = [owner])\n// • git_repos:all — public gallery registry (writers = [])\n//\n// Commit tables are a separate concern — see `commit.ts`.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, REGISTRY_HINT, repoListHint } from \"../core/seed\";\nimport type { RegistryEntry, Repository } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst REPO_COLUMNS = [\"name\", \"description\", \"isPublic\", \"timestamp\"];\nconst REGISTRY_COLUMNS = [\"owner\", \"repo\", \"description\", \"timestamp\"];\n\n/**\n * Create a repo in the owner's personal list, and (if public) also register\n * it in the public gallery. Two transactions — the contract does not support\n * writing two tables atomically.\n */\nexport async function createRepo(\n connection: Connection,\n signer: SignerInput,\n meta: Repository,\n): Promise<void> {\n const owner = signer.publicKey.toBase58();\n const listHint = repoListHint(owner);\n\n if (!(await chain.accountExists(connection, chain.tablePda(listHint)))) {\n await createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n listHint,\n listHint,\n REPO_COLUMNS,\n \"name\",\n [],\n undefined,\n [signer.publicKey],\n listHint,\n );\n }\n\n await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));\n\n if (meta.isPublic) {\n const entry: RegistryEntry = {\n owner,\n repo: meta.name,\n description: meta.description,\n timestamp: meta.timestamp,\n };\n await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));\n }\n}\n\n/** List all repos owned by `owner`. */\nexport async function readOwnerRepos(\n _connection: Connection,\n owner: string,\n): Promise<Repository[]> {\n return (await chain.readRows(repoListHint(owner))) as unknown as Repository[];\n}\n\n/**\n * One page of the public-gallery registry. Callers should still check\n * `row.owner` shape before trusting it in UI; we do not filter at the SDK\n * boundary.\n */\nexport async function readRegistryPage(\n _connection: Connection,\n options?: { limit?: number; before?: string },\n): Promise<RegistryEntry[]> {\n return (await chain.readRows(REGISTRY_HINT, options)) as unknown as RegistryEntry[];\n}\n\n/**\n * One-time global bootstrap of the `git_repos:all` table. Run once per\n * network from an admin key; subsequent calls short-circuit because the\n * account already exists.\n */\nexport async function bootstrapRegistry(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n await chain.ensureDbRoot(connection, signer);\n if (await chain.accountExists(connection, chain.tablePda(REGISTRY_HINT))) {\n return null;\n }\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n REGISTRY_HINT,\n REGISTRY_HINT,\n REGISTRY_COLUMNS,\n \"owner\",\n [],\n undefined,\n undefined,\n REGISTRY_HINT,\n );\n}\n","// L2 — blob + tree storage.\n//\n// `uploadBlob` is the file-level \"don't re-upload what's already on-chain\"\n// primitive. Content is always a base64 string (matching iq-git v1) so the\n// hash / dedup comparison stays byte-for-byte compatible across commits.\n// The reuse-map is just a plain object so callers can build it any way they\n// like (path→entry, or hash→txId for rename-aware dedup — CODE-RULES §3\n// keeps this inlined at the call site instead of a new type).\n//\n// `uploadTree` / `loadTree` serialize FileTree <-> on-chain tree.json as raw\n// JSON (filetype `application/json`), again matching v1.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport type { FileTree } from \"../core/types\";\nimport * as chain from \"./chain\";\n\n/**\n * Upload one file unless an identical hash is already in `reuse` (blob dedup).\n *\n * input: connection, signer, relativePath, base64 content, reuse map, optional onProgress\n * output: { txId, hash } — either reused or freshly uploaded\n */\nexport async function uploadBlob(\n connection: Connection,\n signer: SignerInput,\n relativePath: string,\n base64Content: string,\n reuse: FileTree,\n onProgress?: (percent: number) => void,\n): Promise<{ txId: string; hash: string }> {\n const hash = await sha256Hex(base64Content);\n const prior = reuse[relativePath];\n if (prior && prior.hash === hash) return prior;\n\n const filename = relativePath.split(\"/\").pop() || relativePath;\n const txId = await chain.codeIn(\n connection,\n signer,\n base64Content,\n filename,\n \"application/octet-stream\",\n onProgress,\n );\n return { txId, hash };\n}\n\n/**\n * Serialize a FileTree and upload it as one `tree.json` blob.\n */\nexport async function uploadTree(\n connection: Connection,\n signer: SignerInput,\n tree: FileTree,\n): Promise<string> {\n return chain.codeIn(\n connection,\n signer,\n JSON.stringify(tree),\n \"tree.json\",\n \"application/json\",\n );\n}\n\n/**\n * Fetch and parse a `tree.json` blob by its tx signature.\n */\nexport async function loadTree(treeTxId: string): Promise<FileTree> {\n const { data } = await chain.readCodeIn(treeTxId);\n if (data === null) {\n throw new Error(`tree.json not found for tx ${treeTxId}`);\n }\n return JSON.parse(data) as FileTree;\n}\n\n/**\n * Retrieve a blob's bytes by its txId. Returns the raw string that was\n * uploaded — for blobs this is base64; the caller decodes.\n */\nexport async function loadBlob(txId: string): Promise<string> {\n const { data } = await chain.readCodeIn(txId);\n if (data === null) {\n throw new Error(`blob not found for tx ${txId}`);\n }\n return data;\n}\n","// L4 — GitClient facade. High-level workflows built by composing the\n// layers below. This is the surface consumers (CLI, frontend, migrator) use;\n// they should not reach into `chain` / `storage` / `repo` / `commit` directly.\n//\n// Keep workflow logic here, not inside lower layers (CODE-RULES §5). When a\n// command needs scanning, hashing, uploading, row writing — this file\n// orchestrates them all.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport type { Commit, FileTree, Repository } from \"../core/types\";\nimport * as commitLayer from \"./commit\";\nimport * as repoLayer from \"./repo\";\nimport * as storage from \"./storage\";\n\nexport interface GitClientConfig {\n connection: Connection;\n signer: SignerInput;\n}\n\nexport class GitClient {\n // CODE-RULES §3 — only one small shape; inlined rather than aliased.\n constructor(private readonly cfg: GitClientConfig) {}\n\n /**\n * Create a new repo. Wraps repo.createRepo + pre-creating the commit table\n * so the first `commit()` call doesn't need to pay createTable cost.\n */\n async createRepo(meta: Repository): Promise<void> {\n const { connection, signer } = this.cfg;\n await repoLayer.createRepo(connection, signer, meta);\n await commitLayer.ensureCommitTable(connection, signer, meta.name);\n }\n\n /**\n * Incremental commit against a scanned directory snapshot.\n *\n * input: repoName, message, scan — a `{ [path]: base64Content }` map that\n * the caller produced (Node: fs walk + base64; browser: File input).\n * output: newly written Commit\n */\n async commit(\n repoName: string,\n message: string,\n scan: Record<string, string>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n const latest = await commitLayer.readLatestCommit(connection, owner, repoName);\n const oldTree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const newTree: FileTree = {};\n for (const [path, content] of Object.entries(scan)) {\n newTree[path] = await storage.uploadBlob(\n connection,\n signer,\n path,\n content,\n oldTree,\n );\n }\n\n const treeTxId = await storage.uploadTree(connection, signer, newTree);\n\n const commit: Commit = {\n id: crypto.randomUUID(),\n message,\n treeTxId,\n parentCommitId: latest?.id,\n timestamp: Date.now(),\n author: owner,\n };\n await commitLayer.writeCommit(connection, signer, repoName, commit);\n return commit;\n }\n\n /**\n * Restore a commit's files into a caller-provided sink. Runtime-neutral\n * shape mirrors `commit`: the caller decides how to write bytes to disk /\n * to a File System Access API handle / anywhere else. `content` is the\n * raw base64 string — the sink decodes.\n */\n async checkout(\n repoName: string,\n commitId: string | \"latest\",\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n let target: Commit | null;\n if (commitId === \"latest\") {\n target = await commitLayer.readLatestCommit(connection, owner, repoName);\n } else {\n const history = await commitLayer.readCommitHistory(connection, owner, repoName);\n target = history.find((c) => c.id === commitId) ?? null;\n }\n if (!target) {\n throw new Error(`commit not found: ${commitId} in ${owner}/${repoName}`);\n }\n\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /**\n * Whole-repo snapshot download — convenience on top of checkout(\"latest\")\n * but reading from someone else's `owner`. We do not require signer to\n * equal owner for reads.\n */\n async clone(\n repoName: string,\n owner: string,\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection } = this.cfg;\n const target = await commitLayer.readLatestCommit(connection, owner, repoName);\n if (!target) {\n throw new Error(`no commits in ${owner}/${repoName}`);\n }\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /** Commit history for a repo. */\n async log(\n owner: string,\n repoName: string,\n options?: { limit?: number; before?: string },\n ): Promise<Commit[]> {\n return commitLayer.readCommitHistory(\n this.cfg.connection,\n owner,\n repoName,\n options,\n );\n }\n\n /**\n * Compare a current directory snapshot to the latest commit's tree.\n */\n async status(\n owner: string,\n repoName: string,\n scan: Record<string, string>,\n ): Promise<{ added: string[]; modified: string[]; unchanged: string[] }> {\n const latest = await commitLayer.readLatestCommit(\n this.cfg.connection,\n owner,\n repoName,\n );\n const tree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const added: string[] = [];\n const modified: string[] = [];\n const unchanged: string[] = [];\n for (const [path, content] of Object.entries(scan)) {\n const prior = tree[path];\n if (!prior) {\n added.push(path);\n continue;\n }\n const hash = await sha256Hex(content);\n (prior.hash === hash ? unchanged : modified).push(path);\n }\n return { added, modified, unchanged };\n }\n}\n","// Node entry. Installs the node:crypto SHA-256 hasher, then re-exports\n// everything from the shared root. CLI / migrator / test harnesses all go\n// through here.\n\nimport { setSha256 } from \"./core/hash\";\nimport { hashNode } from \"./platform/hash-node\";\n\nsetSha256(hashNode);\n\nexport * from \"./index\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/hash.ts","../../src/platform/hash-node.ts","../../src/core/seed.ts","../../src/layers/chain.ts","../../src/layers/commit.ts","../../src/layers/repo.ts","../../src/layers/storage.ts","../../src/layers/client.ts","../../src/node.ts"],"names":["sdkCodeIn","createTable","writeRow","tablePda"],"mappings":"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","file":"index.js","sourcesContent":["// SHA-256 content hashing for the blob-dedup index. Runtime-specific\n// implementations live in `platform/hash-node.ts` and `platform/hash-browser.ts`;\n// this module just declares the interface the rest of the SDK uses.\n//\n// Do NOT use this for deriving table seeds — that path is owned by\n// `iqlabs.utils.toSeedBytes` (keccak) and must stay aligned with the SDK.\n\n/**\n * SHA-256 of a UTF-8 or base64 string, returned as a lowercase hex digest.\n * Implementation is injected by the platform entry (node.ts / browser.ts).\n */\nexport type Sha256Hex = (input: string) => Promise<string>;\n\n// Platform entries (src/node.ts, src/browser.ts) will call `setSha256(...)`\n// during module init so downstream layers never care which runtime they are in.\n// This indirection is the one place where a wrapper is justified (CODE-RULES §1)\n// because the behavior genuinely differs per runtime.\nlet impl: Sha256Hex | undefined;\n\nexport function setSha256(fn: Sha256Hex): void {\n if (impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 implementation already installed. Import exactly one of '@iqlabs-official/git-sdk/node' or '@iqlabs-official/git-sdk/browser'.\",\n );\n }\n impl = fn;\n}\n\nexport async function sha256Hex(input: string): Promise<string> {\n if (!impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 not installed. Import '@iqlabs-official/git-sdk/node' (Node) or '@iqlabs-official/git-sdk/browser' (browser) before using the SDK.\",\n );\n }\n return impl(input);\n}\n","// Node SHA-256 implementation. Kept as a pure export; `src/node.ts` installs\n// it via `setSha256` during its own module init.\n\nimport { createHash } from \"node:crypto\";\nimport type { Sha256Hex } from \"../core/hash\";\n\nexport const hashNode: Sha256Hex = async (input) =>\n createHash(\"sha256\").update(input).digest(\"hex\");\n","// The single source of truth for table_hint strings.\n//\n// Readers re-derive PDAs with `iqlabs.utils.toSeedBytes(hint)` →\n// `iqlabs.contract.getTablePda(...)`; writers pass the same hint into\n// `iqlabs.writer.createTable`. Keeping the naming convention in one place\n// prevents silent drift between writer and reader. CODE-RULES §2 — if any\n// caller ever wants to build one of these strings inline, route it here\n// instead.\n\n/** DbRoot id for every iq-git table. Bootstrap and every caller share this. */\nexport const IQGIT_ROOT_ID = \"iq-git-v1\";\n\n/** `git_repos:all` — open-writers registry that drives the public gallery. */\nexport const REGISTRY_HINT = \"git_repos:all\";\n\n/**\n * Hint for the per-owner personal repo list.\n * input: owner wallet base58\n * output: \"git_repos_v2_<owner>\"\n */\nexport function repoListHint(owner: string): string {\n return `git_repos_v2_${owner}`;\n}\n\n/**\n * Hint for the per-repo commit table.\n * input: owner wallet base58, repo name (any characters — SDK keccak-hashes)\n * output: \"git_commits:<owner>:<repo>\"\n */\nexport function commitTableHint(owner: string, repo: string): string {\n return `git_commits:${owner}:${repo}`;\n}\n","// L1 — chain primitives shared by L2/L3. Kept tiny by design: each function\n// here either (a) hides PDA derivation so callers only deal with hints, or\n// (b) wraps a single iqlabs-sdk call to keep an iq-git-specific default\n// (chunk speed, root id, ...). Pure passthroughs like `createTable` /\n// `writeRow` live at the call site instead — wrapping them once more would\n// just bury the iqlabs-sdk surface (CODE-RULES §1).\n\nimport {\n Keypair,\n SystemProgram,\n Transaction,\n type Connection,\n type PublicKey,\n} from \"@solana/web3.js\";\nimport {\n createInstructionBuilder,\n getDbRootPda,\n getTablePda,\n initializeDbRootInstruction,\n} from \"@iqlabs-official/solana-sdk/contract\";\nimport { readCodeIn, readTableRows } from \"@iqlabs-official/solana-sdk/reader\";\nimport { toSeedBytes, type SignerInput, type WalletSigner } from \"@iqlabs-official/solana-sdk/utils\";\nimport { codeIn as sdkCodeIn } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID } from \"../core/seed\";\n\n/** DbRoot PDA for the `iq-git-v1` namespace — derived once, reused everywhere. */\nexport const DB_ROOT_SEED = toSeedBytes(IQGIT_ROOT_ID);\nexport const DB_ROOT = getDbRootPda(DB_ROOT_SEED);\n\n/**\n * Initialize the `iq-git-v1` DbRoot account if it doesn't exist. First-call\n * cost on a fresh network. Idempotent: returns null if already initialized.\n * Accepts any SignerInput so an admin can run it from a wallet, not just\n * from a Keypair.\n */\nexport async function ensureDbRoot(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n if (await accountExists(connection, DB_ROOT)) return null;\n const builder = createInstructionBuilder();\n const ix = initializeDbRootInstruction(\n builder,\n {\n db_root: DB_ROOT,\n signer: signer.publicKey,\n system_program: SystemProgram.programId,\n },\n { db_root_id: DB_ROOT_SEED },\n );\n const tx = new Transaction().add(ix);\n const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();\n tx.recentBlockhash = blockhash;\n tx.feePayer = signer.publicKey;\n const signed = await signTx(signer, tx);\n const signature = await connection.sendRawTransaction(signed.serialize());\n await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });\n return signature;\n}\n\n// Sign a single Transaction with whatever shape the signer takes. Keypair\n// has a `secretKey`; wallet adapters expose `signTransaction`. iqlabs-sdk's\n// own `sendTx` uses the same dispatch; we replicate it here so the helper\n// stays self-contained.\nasync function signTx(signer: SignerInput, tx: Transaction): Promise<Transaction> {\n if (signer instanceof Keypair || \"secretKey\" in signer) {\n tx.partialSign(signer as Keypair);\n return tx;\n }\n return (signer as WalletSigner).signTransaction(tx);\n}\n\n/**\n * Read rows from a table. Translates our hint into the PDA and forwards to\n * `iqlabs.reader.readTableRows`.\n */\nexport async function readRows(\n hint: string,\n options?: { limit?: number; before?: string },\n): Promise<Array<Record<string, unknown>>> {\n return readTableRows(tablePda(hint), options);\n}\n\n/**\n * Fetch just the latest row of a table — the fast path for \"what is the\n * current commit\" and \"what is the pinned deploy\".\n */\nexport async function readLatestRow(\n hint: string,\n): Promise<Record<string, unknown> | null> {\n const rows = await readRows(hint, { limit: 1 });\n return rows[0] ?? null;\n}\n\n/**\n * Upload a blob via `iqlabs.writer.codeIn`. The SDK chunks internally when\n * `data` is a plain string. We default speed to \"light\" because that's the\n * Helius-friendly setting for git workloads (per-file uploads are bursty).\n */\nexport async function codeIn(\n connection: Connection,\n signer: SignerInput,\n data: string | string[],\n filename: string,\n filetype: string,\n onProgress?: (percent: number) => void,\n speed: \"light\" | \"medium\" | \"fast\" = \"light\",\n): Promise<string> {\n return sdkCodeIn(\n { connection, signer },\n data,\n filename,\n 0,\n filetype,\n onProgress,\n speed,\n );\n}\n\nexport { readCodeIn };\n\n/** Cheap existence check for a PDA. */\nexport async function accountExists(\n connection: Connection,\n pda: PublicKey,\n): Promise<boolean> {\n return (await connection.getAccountInfo(pda)) !== null;\n}\n\n/** Resolve a hint to its PDA. */\nexport function tablePda(hint: string): PublicKey {\n return getTablePda(DB_ROOT, toSeedBytes(hint));\n}\n","// L3 — per-repo commit table.\n//\n// v2's core invariant: each repo has its own table at hint\n// `git_commits:<owner>:<repo>`, with writers locked to [owner]. That means\n// \"the most recent successful tx in this table = the latest commit\", and we\n// read it as a single-row query (limit: 1).\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, commitTableHint } from \"../core/seed\";\nimport type { Commit } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst COMMIT_COLUMNS = [\n \"id\",\n \"message\",\n \"treeTxId\",\n \"parentCommitId\",\n \"timestamp\",\n \"author\",\n];\n\n/**\n * Ensure the per-repo commit table exists with writers = [owner]. No-op if\n * it already exists.\n */\nexport async function ensureCommitTable(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n): Promise<string | null> {\n const hint = commitTableHint(signer.publicKey.toBase58(), repo);\n if (await chain.accountExists(connection, chain.tablePda(hint))) return null;\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n hint,\n hint,\n COMMIT_COLUMNS,\n \"id\",\n [],\n undefined,\n [signer.publicKey],\n hint,\n );\n}\n\n/**\n * Append one commit row. Callers (workflow-level code) are responsible for\n * setting parentCommitId — the SDK does not auto-chain.\n */\nexport async function writeCommit(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n commit: Commit,\n): Promise<string> {\n const owner = signer.publicKey.toBase58();\n return writeRow(\n connection,\n signer,\n IQGIT_ROOT_ID,\n commitTableHint(owner, repo),\n JSON.stringify(commit),\n );\n}\n\n/** Latest commit. Single-row, O(1) RPC path. */\nexport async function readLatestCommit(\n _connection: Connection,\n owner: string,\n repo: string,\n): Promise<Commit | null> {\n const row = await chain.readLatestRow(commitTableHint(owner, repo));\n return row as unknown as Commit | null;\n}\n\n/** Full commit history, newest first. */\nexport async function readCommitHistory(\n _connection: Connection,\n owner: string,\n repo: string,\n options?: { limit?: number; before?: string },\n): Promise<Commit[]> {\n const rows = await chain.readRows(commitTableHint(owner, repo), options);\n return rows as unknown as Commit[];\n}\n","// L3 — repo list and public registry.\n//\n// Covers the two \"directory\" tables:\n// • git_repos_v2_<owner> — owner's personal repo list (writers = [owner])\n// • git_repos:all — public gallery registry (writers = [])\n//\n// Commit tables are a separate concern — see `commit.ts`.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, REGISTRY_HINT, repoListHint } from \"../core/seed\";\nimport type { RegistryEntry, Repository } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst REPO_COLUMNS = [\"name\", \"description\", \"isPublic\", \"timestamp\"];\nconst REGISTRY_COLUMNS = [\"owner\", \"repo\", \"description\", \"timestamp\"];\n\n/**\n * Create a repo in the owner's personal list, and (if public) also register\n * it in the public gallery. Two transactions — the contract does not support\n * writing two tables atomically.\n *\n * Returns the writeRow tx signatures (and the table hint each one wrote to)\n * so a caller can notify gateways / build SSE streams for those tables. The\n * returned shape is `[]` from `createTable` calls because those don't write\n * a row, just allocate the account.\n */\nexport async function createRepo(\n connection: Connection,\n signer: SignerInput,\n meta: Repository,\n): Promise<Array<{ tableHint: string; sig: string; row: object }>> {\n const owner = signer.publicKey.toBase58();\n const listHint = repoListHint(owner);\n const writes: Array<{ tableHint: string; sig: string; row: object }> = [];\n\n if (!(await chain.accountExists(connection, chain.tablePda(listHint)))) {\n await createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n listHint,\n listHint,\n REPO_COLUMNS,\n \"name\",\n [],\n undefined,\n [signer.publicKey],\n listHint,\n );\n }\n\n const sig = await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));\n writes.push({ tableHint: listHint, sig, row: meta });\n\n if (meta.isPublic) {\n const entry: RegistryEntry = {\n owner,\n repo: meta.name,\n description: meta.description,\n timestamp: meta.timestamp,\n };\n const regSig = await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));\n writes.push({ tableHint: REGISTRY_HINT, sig: regSig, row: entry });\n }\n\n return writes;\n}\n\n/** List all repos owned by `owner`. */\nexport async function readOwnerRepos(\n _connection: Connection,\n owner: string,\n): Promise<Repository[]> {\n return (await chain.readRows(repoListHint(owner))) as unknown as Repository[];\n}\n\n/**\n * One page of the public-gallery registry. Callers should still check\n * `row.owner` shape before trusting it in UI; we do not filter at the SDK\n * boundary.\n */\nexport async function readRegistryPage(\n _connection: Connection,\n options?: { limit?: number; before?: string },\n): Promise<RegistryEntry[]> {\n return (await chain.readRows(REGISTRY_HINT, options)) as unknown as RegistryEntry[];\n}\n\n/**\n * One-time global bootstrap of the `git_repos:all` table. Run once per\n * network from an admin key; subsequent calls short-circuit because the\n * account already exists.\n */\nexport async function bootstrapRegistry(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n await chain.ensureDbRoot(connection, signer);\n if (await chain.accountExists(connection, chain.tablePda(REGISTRY_HINT))) {\n return null;\n }\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n REGISTRY_HINT,\n REGISTRY_HINT,\n REGISTRY_COLUMNS,\n \"owner\",\n [],\n undefined,\n undefined,\n REGISTRY_HINT,\n );\n}\n","// L2 — blob + tree storage.\n//\n// `uploadBlob` is the file-level \"don't re-upload what's already on-chain\"\n// primitive. Content is always a base64 string (matching iq-git v1) so the\n// hash / dedup comparison stays byte-for-byte compatible across commits.\n// The reuse-map is just a plain object so callers can build it any way they\n// like (path→entry, or hash→txId for rename-aware dedup — CODE-RULES §3\n// keeps this inlined at the call site instead of a new type).\n//\n// `uploadTree` / `loadTree` serialize FileTree <-> on-chain tree.json as raw\n// JSON (filetype `application/json`), again matching v1.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport type { FileTree } from \"../core/types\";\nimport * as chain from \"./chain\";\n\n/**\n * Upload one file unless an identical hash is already in `reuse` (blob dedup).\n *\n * input: connection, signer, relativePath, base64 content, reuse map, optional onProgress\n * output: { txId, hash } — either reused or freshly uploaded\n */\nexport async function uploadBlob(\n connection: Connection,\n signer: SignerInput,\n relativePath: string,\n base64Content: string,\n reuse: FileTree,\n onProgress?: (percent: number) => void,\n): Promise<{ txId: string; hash: string }> {\n const hash = await sha256Hex(base64Content);\n const prior = reuse[relativePath];\n if (prior && prior.hash === hash) return prior;\n\n // Tag the codeIn filename with our app marker so non-inline inventory\n // entries can be classified by inspecting metadata.filename alone (no row\n // body fetch needed). Format: \"iqgit-blob:<basename>\". Same scheme used\n // for trees (\"iqgit-tree\") and other writes across the IQ ecosystem.\n const basename = relativePath.split(\"/\").pop() || relativePath;\n const txId = await chain.codeIn(\n connection,\n signer,\n base64Content,\n `iqgit-blob:${basename}`,\n \"application/octet-stream\",\n onProgress,\n );\n return { txId, hash };\n}\n\n/**\n * Serialize a FileTree and upload it as one `tree.json` blob.\n */\nexport async function uploadTree(\n connection: Connection,\n signer: SignerInput,\n tree: FileTree,\n): Promise<string> {\n return chain.codeIn(\n connection,\n signer,\n JSON.stringify(tree),\n \"iqgit-tree\",\n \"application/json\",\n );\n}\n\n/**\n * Fetch and parse a `tree.json` blob by its tx signature.\n */\nexport async function loadTree(treeTxId: string): Promise<FileTree> {\n const { data } = await chain.readCodeIn(treeTxId);\n if (data === null) {\n throw new Error(`tree.json not found for tx ${treeTxId}`);\n }\n return JSON.parse(data) as FileTree;\n}\n\n/**\n * Retrieve a blob's bytes by its txId. Returns the raw string that was\n * uploaded — for blobs this is base64; the caller decodes.\n */\nexport async function loadBlob(txId: string): Promise<string> {\n const { data } = await chain.readCodeIn(txId);\n if (data === null) {\n throw new Error(`blob not found for tx ${txId}`);\n }\n return data;\n}\n","// L4 — GitClient facade. High-level workflows built by composing the\n// layers below. This is the surface consumers (CLI, frontend, migrator) use;\n// they should not reach into `chain` / `storage` / `repo` / `commit` directly.\n//\n// Keep workflow logic here, not inside lower layers (CODE-RULES §5). When a\n// command needs scanning, hashing, uploading, row writing — this file\n// orchestrates them all.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport { commitTableHint } from \"../core/seed\";\nimport type { Commit, FileTree, Repository } from \"../core/types\";\nimport * as chain from \"./chain\";\nimport * as commitLayer from \"./commit\";\nimport * as repoLayer from \"./repo\";\nimport * as storage from \"./storage\";\n\n/** Information surfaced to the `onWrite` callback for every successful row\n * write the SDK performs. Consumers wire this to their gateway notify so\n * the gateway updates its cache / SSE stream immediately. */\nexport interface WriteEvent {\n /** Resolved table PDA, base58 — pass directly to gateway notify. */\n tablePda: string;\n /** The hint that derived the PDA, in case callers want to log it. */\n tableHint: string;\n /** writeRow tx signature. */\n sig: string;\n /** The row object that was written (already JSON.stringify-able). */\n row: object;\n}\n\nexport interface GitClientConfig {\n connection: Connection;\n signer: SignerInput;\n /** Fires after each successful row write inside the SDK. Fire-and-forget\n * callback for gateway notifies; throwing here will surface to the caller. */\n onWrite?: (event: WriteEvent) => void;\n}\n\nexport class GitClient {\n // CODE-RULES §3 — only one small shape; inlined rather than aliased.\n constructor(private readonly cfg: GitClientConfig) {}\n\n /**\n * Create a new repo. Wraps repo.createRepo + pre-creating the commit table\n * so the first `commit()` call doesn't need to pay createTable cost.\n */\n async createRepo(meta: Repository): Promise<void> {\n const { connection, signer } = this.cfg;\n const writes = await repoLayer.createRepo(connection, signer, meta);\n await commitLayer.ensureCommitTable(connection, signer, meta.name);\n this.fireOnWrite(writes);\n }\n\n /**\n * Incremental commit against a scanned directory snapshot.\n *\n * input: repoName, message, scan — a `{ [path]: base64Content }` map that\n * the caller produced (Node: fs walk + base64; browser: File input).\n * output: newly written Commit\n */\n async commit(\n repoName: string,\n message: string,\n scan: Record<string, string>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n const latest = await commitLayer.readLatestCommit(connection, owner, repoName);\n const oldTree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const newTree: FileTree = {};\n for (const [path, content] of Object.entries(scan)) {\n newTree[path] = await storage.uploadBlob(\n connection,\n signer,\n path,\n content,\n oldTree,\n );\n }\n\n const treeTxId = await storage.uploadTree(connection, signer, newTree);\n\n const commit: Commit = {\n id: crypto.randomUUID(),\n message,\n treeTxId,\n parentCommitId: latest?.id,\n timestamp: Date.now(),\n author: owner,\n };\n const sig = await commitLayer.writeCommit(connection, signer, repoName, commit);\n this.fireOnWrite([{ tableHint: commitTableHint(owner, repoName), sig, row: commit }]);\n return commit;\n }\n\n /** Resolve the table hint to a PDA and forward each write to `onWrite`.\n * Wrapped in try/catch per call so a misbehaving notify can't fail the\n * whole batch — but we still surface the error so the consumer knows. */\n private fireOnWrite(writes: Array<{ tableHint: string; sig: string; row: object }>) {\n if (!this.cfg.onWrite) return;\n for (const w of writes) {\n const tablePda = chain.tablePda(w.tableHint).toBase58();\n this.cfg.onWrite({ tablePda, tableHint: w.tableHint, sig: w.sig, row: w.row });\n }\n }\n\n /**\n * Restore a commit's files into a caller-provided sink. Runtime-neutral\n * shape mirrors `commit`: the caller decides how to write bytes to disk /\n * to a File System Access API handle / anywhere else. `content` is the\n * raw base64 string — the sink decodes.\n */\n async checkout(\n repoName: string,\n commitId: string | \"latest\",\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n let target: Commit | null;\n if (commitId === \"latest\") {\n target = await commitLayer.readLatestCommit(connection, owner, repoName);\n } else {\n const history = await commitLayer.readCommitHistory(connection, owner, repoName);\n target = history.find((c) => c.id === commitId) ?? null;\n }\n if (!target) {\n throw new Error(`commit not found: ${commitId} in ${owner}/${repoName}`);\n }\n\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /**\n * Whole-repo snapshot download — convenience on top of checkout(\"latest\")\n * but reading from someone else's `owner`. We do not require signer to\n * equal owner for reads.\n */\n async clone(\n repoName: string,\n owner: string,\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection } = this.cfg;\n const target = await commitLayer.readLatestCommit(connection, owner, repoName);\n if (!target) {\n throw new Error(`no commits in ${owner}/${repoName}`);\n }\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /** Commit history for a repo. */\n async log(\n owner: string,\n repoName: string,\n options?: { limit?: number; before?: string },\n ): Promise<Commit[]> {\n return commitLayer.readCommitHistory(\n this.cfg.connection,\n owner,\n repoName,\n options,\n );\n }\n\n /**\n * Compare a current directory snapshot to the latest commit's tree.\n */\n async status(\n owner: string,\n repoName: string,\n scan: Record<string, string>,\n ): Promise<{ added: string[]; modified: string[]; unchanged: string[] }> {\n const latest = await commitLayer.readLatestCommit(\n this.cfg.connection,\n owner,\n repoName,\n );\n const tree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const added: string[] = [];\n const modified: string[] = [];\n const unchanged: string[] = [];\n for (const [path, content] of Object.entries(scan)) {\n const prior = tree[path];\n if (!prior) {\n added.push(path);\n continue;\n }\n const hash = await sha256Hex(content);\n (prior.hash === hash ? unchanged : modified).push(path);\n }\n return { added, modified, unchanged };\n }\n}\n","// Node entry. Installs the node:crypto SHA-256 hasher, then re-exports\n// everything from the shared root. CLI / migrator / test harnesses all go\n// through here.\n\nimport { setSha256 } from \"./core/hash\";\nimport { hashNode } from \"./platform/hash-node\";\n\nsetSha256(hashNode);\n\nexport * from \"./index\";\n"]}
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -48,9 +48,25 @@ declare function repoListHint(owner: string): string;
|
|
|
48
48
|
*/
|
|
49
49
|
declare function commitTableHint(owner: string, repo: string): string;
|
|
50
50
|
|
|
51
|
+
/** Information surfaced to the `onWrite` callback for every successful row
|
|
52
|
+
* write the SDK performs. Consumers wire this to their gateway notify so
|
|
53
|
+
* the gateway updates its cache / SSE stream immediately. */
|
|
54
|
+
interface WriteEvent {
|
|
55
|
+
/** Resolved table PDA, base58 — pass directly to gateway notify. */
|
|
56
|
+
tablePda: string;
|
|
57
|
+
/** The hint that derived the PDA, in case callers want to log it. */
|
|
58
|
+
tableHint: string;
|
|
59
|
+
/** writeRow tx signature. */
|
|
60
|
+
sig: string;
|
|
61
|
+
/** The row object that was written (already JSON.stringify-able). */
|
|
62
|
+
row: object;
|
|
63
|
+
}
|
|
51
64
|
interface GitClientConfig {
|
|
52
65
|
connection: Connection;
|
|
53
66
|
signer: SignerInput;
|
|
67
|
+
/** Fires after each successful row write inside the SDK. Fire-and-forget
|
|
68
|
+
* callback for gateway notifies; throwing here will surface to the caller. */
|
|
69
|
+
onWrite?: (event: WriteEvent) => void;
|
|
54
70
|
}
|
|
55
71
|
declare class GitClient {
|
|
56
72
|
private readonly cfg;
|
|
@@ -68,6 +84,10 @@ declare class GitClient {
|
|
|
68
84
|
* output: newly written Commit
|
|
69
85
|
*/
|
|
70
86
|
commit(repoName: string, message: string, scan: Record<string, string>): Promise<Commit>;
|
|
87
|
+
/** Resolve the table hint to a PDA and forward each write to `onWrite`.
|
|
88
|
+
* Wrapped in try/catch per call so a misbehaving notify can't fail the
|
|
89
|
+
* whole batch — but we still surface the error so the consumer knows. */
|
|
90
|
+
private fireOnWrite;
|
|
71
91
|
/**
|
|
72
92
|
* Restore a commit's files into a caller-provided sink. Runtime-neutral
|
|
73
93
|
* shape mirrors `commit`: the caller decides how to write bytes to disk /
|
|
@@ -132,4 +152,4 @@ declare function loadTree(treeTxId: string): Promise<FileTree>;
|
|
|
132
152
|
*/
|
|
133
153
|
declare function loadBlob(txId: string): Promise<string>;
|
|
134
154
|
|
|
135
|
-
export { type Commit, type FileTree, GitClient, type GitClientConfig, IQGIT_ROOT_ID, REGISTRY_HINT, type RegistryEntry, type Repository, bootstrapRegistry, commitTableHint, loadBlob, loadTree, readCommitHistory, readLatestCommit, readOwnerRepos, readRegistryPage, repoListHint };
|
|
155
|
+
export { type Commit, type FileTree, GitClient, type GitClientConfig, IQGIT_ROOT_ID, REGISTRY_HINT, type RegistryEntry, type Repository, type WriteEvent, bootstrapRegistry, commitTableHint, loadBlob, loadTree, readCommitHistory, readLatestCommit, readOwnerRepos, readRegistryPage, repoListHint };
|
package/dist/shared/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createTable, writeRow, codeIn as codeIn$1 } from '@iqlabs-official/solana-sdk/writer';
|
|
2
1
|
import { SystemProgram, Transaction, Keypair } from '@solana/web3.js';
|
|
3
2
|
import { getDbRootPda, createInstructionBuilder, initializeDbRootInstruction, getTablePda } from '@iqlabs-official/solana-sdk/contract';
|
|
4
3
|
import { readCodeIn, readTableRows } from '@iqlabs-official/solana-sdk/reader';
|
|
5
4
|
import { toSeedBytes } from '@iqlabs-official/solana-sdk/utils';
|
|
5
|
+
import { createTable, writeRow, codeIn as codeIn$1 } from '@iqlabs-official/solana-sdk/writer';
|
|
6
6
|
|
|
7
7
|
// src/core/seed.ts
|
|
8
8
|
var IQGIT_ROOT_ID = "iq-git-v1";
|
|
@@ -74,8 +74,6 @@ async function accountExists(connection, pda) {
|
|
|
74
74
|
function tablePda(hint) {
|
|
75
75
|
return getTablePda(DB_ROOT, toSeedBytes(hint));
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
// src/layers/commit.ts
|
|
79
77
|
var COMMIT_COLUMNS = [
|
|
80
78
|
"id",
|
|
81
79
|
"message",
|
|
@@ -124,6 +122,7 @@ var REGISTRY_COLUMNS = ["owner", "repo", "description", "timestamp"];
|
|
|
124
122
|
async function createRepo(connection, signer, meta) {
|
|
125
123
|
const owner = signer.publicKey.toBase58();
|
|
126
124
|
const listHint = repoListHint(owner);
|
|
125
|
+
const writes = [];
|
|
127
126
|
if (!await accountExists(connection, tablePda(listHint))) {
|
|
128
127
|
await createTable(
|
|
129
128
|
connection,
|
|
@@ -139,7 +138,8 @@ async function createRepo(connection, signer, meta) {
|
|
|
139
138
|
listHint
|
|
140
139
|
);
|
|
141
140
|
}
|
|
142
|
-
await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));
|
|
141
|
+
const sig = await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));
|
|
142
|
+
writes.push({ tableHint: listHint, sig, row: meta });
|
|
143
143
|
if (meta.isPublic) {
|
|
144
144
|
const entry = {
|
|
145
145
|
owner,
|
|
@@ -147,8 +147,10 @@ async function createRepo(connection, signer, meta) {
|
|
|
147
147
|
description: meta.description,
|
|
148
148
|
timestamp: meta.timestamp
|
|
149
149
|
};
|
|
150
|
-
await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));
|
|
150
|
+
const regSig = await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));
|
|
151
|
+
writes.push({ tableHint: REGISTRY_HINT, sig: regSig, row: entry });
|
|
151
152
|
}
|
|
153
|
+
return writes;
|
|
152
154
|
}
|
|
153
155
|
async function readOwnerRepos(_connection, owner) {
|
|
154
156
|
return await readRows(repoListHint(owner));
|
|
@@ -181,12 +183,12 @@ async function uploadBlob(connection, signer, relativePath, base64Content, reuse
|
|
|
181
183
|
const hash = await sha256Hex();
|
|
182
184
|
const prior = reuse[relativePath];
|
|
183
185
|
if (prior && prior.hash === hash) return prior;
|
|
184
|
-
const
|
|
186
|
+
const basename = relativePath.split("/").pop() || relativePath;
|
|
185
187
|
const txId = await codeIn(
|
|
186
188
|
connection,
|
|
187
189
|
signer,
|
|
188
190
|
base64Content,
|
|
189
|
-
|
|
191
|
+
`iqgit-blob:${basename}`,
|
|
190
192
|
"application/octet-stream",
|
|
191
193
|
onProgress
|
|
192
194
|
);
|
|
@@ -197,7 +199,7 @@ async function uploadTree(connection, signer, tree) {
|
|
|
197
199
|
connection,
|
|
198
200
|
signer,
|
|
199
201
|
JSON.stringify(tree),
|
|
200
|
-
"tree
|
|
202
|
+
"iqgit-tree",
|
|
201
203
|
"application/json"
|
|
202
204
|
);
|
|
203
205
|
}
|
|
@@ -229,8 +231,9 @@ var GitClient = class {
|
|
|
229
231
|
*/
|
|
230
232
|
async createRepo(meta) {
|
|
231
233
|
const { connection, signer } = this.cfg;
|
|
232
|
-
await createRepo(connection, signer, meta);
|
|
234
|
+
const writes = await createRepo(connection, signer, meta);
|
|
233
235
|
await ensureCommitTable(connection, signer, meta.name);
|
|
236
|
+
this.fireOnWrite(writes);
|
|
234
237
|
}
|
|
235
238
|
/**
|
|
236
239
|
* Incremental commit against a scanned directory snapshot.
|
|
@@ -263,9 +266,20 @@ var GitClient = class {
|
|
|
263
266
|
timestamp: Date.now(),
|
|
264
267
|
author: owner
|
|
265
268
|
};
|
|
266
|
-
await writeCommit(connection, signer, repoName, commit);
|
|
269
|
+
const sig = await writeCommit(connection, signer, repoName, commit);
|
|
270
|
+
this.fireOnWrite([{ tableHint: commitTableHint(owner, repoName), sig, row: commit }]);
|
|
267
271
|
return commit;
|
|
268
272
|
}
|
|
273
|
+
/** Resolve the table hint to a PDA and forward each write to `onWrite`.
|
|
274
|
+
* Wrapped in try/catch per call so a misbehaving notify can't fail the
|
|
275
|
+
* whole batch — but we still surface the error so the consumer knows. */
|
|
276
|
+
fireOnWrite(writes) {
|
|
277
|
+
if (!this.cfg.onWrite) return;
|
|
278
|
+
for (const w of writes) {
|
|
279
|
+
const tablePda2 = tablePda(w.tableHint).toBase58();
|
|
280
|
+
this.cfg.onWrite({ tablePda: tablePda2, tableHint: w.tableHint, sig: w.sig, row: w.row });
|
|
281
|
+
}
|
|
282
|
+
}
|
|
269
283
|
/**
|
|
270
284
|
* Restore a commit's files into a caller-provided sink. Runtime-neutral
|
|
271
285
|
* shape mirrors `commit`: the caller decides how to write bytes to disk /
|
package/dist/shared/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/seed.ts","../../src/core/hash.ts","../../src/layers/chain.ts","../../src/layers/commit.ts","../../src/layers/repo.ts","../../src/layers/storage.ts","../../src/layers/client.ts"],"names":["sdkCodeIn","createTable","writeRow"],"mappings":";;;;;;;AAUO,IAAM,aAAA,GAAgB;AAGtB,IAAM,aAAA,GAAgB;AAOtB,SAAS,aAAa,KAAA,EAAuB;AAClD,EAAA,OAAO,gBAAgB,KAAK,CAAA,CAAA;AAC9B;AAOO,SAAS,eAAA,CAAgB,OAAe,IAAA,EAAsB;AACnE,EAAA,OAAO,CAAA,YAAA,EAAe,KAAK,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AACrC;ACHA,eAAsB,UAAU,KAAA,EAAgC;AAC9D,EAAW;AACT,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAEF;ACTO,IAAM,YAAA,GAAe,YAAY,aAAa,CAAA;AAC9C,IAAM,OAAA,GAAU,aAAa,YAAY,CAAA;AAQhD,eAAsB,YAAA,CACpB,YACA,MAAA,EACwB;AACxB,EAAA,IAAI,MAAM,aAAA,CAAc,UAAA,EAAY,OAAO,GAAG,OAAO,IAAA;AACrD,EAAA,MAAM,UAAU,wBAAA,EAAyB;AACzC,EAAA,MAAM,EAAA,GAAK,2BAAA;AAAA,IACT,OAAA;AAAA,IACA;AAAA,MACE,OAAA,EAAS,OAAA;AAAA,MACT,QAAQ,MAAA,CAAO,SAAA;AAAA,MACf,gBAAgB,aAAA,CAAc;AAAA,KAChC;AAAA,IACA,EAAE,YAAY,YAAA;AAAa,GAC7B;AACA,EAAA,MAAM,EAAA,GAAK,IAAI,WAAA,EAAY,CAAE,IAAI,EAAE,CAAA;AACnC,EAAA,MAAM,EAAE,SAAA,EAAW,oBAAA,EAAqB,GAAI,MAAM,WAAW,kBAAA,EAAmB;AAChF,EAAA,EAAA,CAAG,eAAA,GAAkB,SAAA;AACrB,EAAA,EAAA,CAAG,WAAW,MAAA,CAAO,SAAA;AACrB,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,MAAA,EAAQ,EAAE,CAAA;AACtC,EAAA,MAAM,YAAY,MAAM,UAAA,CAAW,kBAAA,CAAmB,MAAA,CAAO,WAAW,CAAA;AACxE,EAAA,MAAM,WAAW,kBAAA,CAAmB,EAAE,SAAA,EAAW,SAAA,EAAW,sBAAsB,CAAA;AAClF,EAAA,OAAO,SAAA;AACT;AAMA,eAAe,MAAA,CAAO,QAAqB,EAAA,EAAuC;AAChF,EAAA,IAAI,MAAA,YAAkB,OAAA,IAAW,WAAA,IAAe,MAAA,EAAQ;AACtD,IAAA,EAAA,CAAG,YAAY,MAAiB,CAAA;AAChC,IAAA,OAAO,EAAA;AAAA,EACT;AACA,EAAA,OAAQ,MAAA,CAAwB,gBAAgB,EAAE,CAAA;AACpD;AAMA,eAAsB,QAAA,CACpB,MACA,OAAA,EACyC;AACzC,EAAA,OAAO,aAAA,CAAc,QAAA,CAAS,IAAI,CAAA,EAAG,OAAO,CAAA;AAC9C;AAMA,eAAsB,cACpB,IAAA,EACyC;AACzC,EAAA,MAAM,OAAO,MAAM,QAAA,CAAS,MAAM,EAAE,KAAA,EAAO,GAAG,CAAA;AAC9C,EAAA,OAAO,IAAA,CAAK,CAAC,CAAA,IAAK,IAAA;AACpB;AAOA,eAAsB,MAAA,CACpB,YACA,MAAA,EACA,IAAA,EACA,UACA,QAAA,EACA,UAAA,EACA,QAAqC,OAAA,EACpB;AACjB,EAAA,OAAOA,QAAA;AAAA,IACL,EAAE,YAAY,MAAA,EAAO;AAAA,IACrB,IAAA;AAAA,IACA,QAAA;AAAA,IACA,CAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACF;AACF;AAKA,eAAsB,aAAA,CACpB,YACA,GAAA,EACkB;AAClB,EAAA,OAAQ,MAAM,UAAA,CAAW,cAAA,CAAe,GAAG,CAAA,KAAO,IAAA;AACpD;AAGO,SAAS,SAAS,IAAA,EAAyB;AAChD,EAAA,OAAO,WAAA,CAAY,OAAA,EAAS,WAAA,CAAY,IAAI,CAAC,CAAA;AAC/C;;;ACtHA,IAAM,cAAA,GAAiB;AAAA,EACrB,IAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA;AAAA,EACA,gBAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAAA;AAMA,eAAsB,iBAAA,CACpB,UAAA,EACA,MAAA,EACA,IAAA,EACwB;AACxB,EAAA,MAAM,OAAO,eAAA,CAAgB,MAAA,CAAO,SAAA,CAAU,QAAA,IAAY,IAAI,CAAA;AAC9D,EAAA,IAAI,MAAY,aAAA,CAAc,UAAA,EAAkB,SAAS,IAAI,CAAC,GAAG,OAAO,IAAA;AACxE,EAAA,OAAO,WAAA;AAAA,IACL,UAAA;AAAA,IACA,MAAA;AAAA,IACA,aAAA;AAAA,IACA,IAAA;AAAA,IACA,IAAA;AAAA,IACA,cAAA;AAAA,IACA,IAAA;AAAA,IACA,EAAC;AAAA,IACD,MAAA;AAAA,IACA,CAAC,OAAO,SAAS,CAAA;AAAA,IACjB;AAAA,GACF;AACF;AAMA,eAAsB,WAAA,CACpB,UAAA,EACA,MAAA,EACA,IAAA,EACA,MAAA,EACiB;AACjB,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,SAAA,CAAU,QAAA,EAAS;AACxC,EAAA,OAAO,QAAA;AAAA,IACL,UAAA;AAAA,IACA,MAAA;AAAA,IACA,aAAA;AAAA,IACA,eAAA,CAAgB,OAAO,IAAI,CAAA;AAAA,IAC3B,IAAA,CAAK,UAAU,MAAM;AAAA,GACvB;AACF;AAGA,eAAsB,gBAAA,CACpB,WAAA,EACA,KAAA,EACA,IAAA,EACwB;AACxB,EAAA,MAAM,MAAM,MAAY,aAAA,CAAc,eAAA,CAAgB,KAAA,EAAO,IAAI,CAAC,CAAA;AAClE,EAAA,OAAO,GAAA;AACT;AAGA,eAAsB,iBAAA,CACpB,WAAA,EACA,KAAA,EACA,IAAA,EACA,OAAA,EACmB;AACnB,EAAA,MAAM,OAAO,MAAY,QAAA,CAAS,gBAAgB,KAAA,EAAO,IAAI,GAAG,OAAO,CAAA;AACvE,EAAA,OAAO,IAAA;AACT;ACzEA,IAAM,YAAA,GAAe,CAAC,MAAA,EAAQ,aAAA,EAAe,YAAY,WAAW,CAAA;AACpE,IAAM,gBAAA,GAAmB,CAAC,OAAA,EAAS,MAAA,EAAQ,eAAe,WAAW,CAAA;AAOrE,eAAsB,UAAA,CACpB,UAAA,EACA,MAAA,EACA,IAAA,EACe;AACf,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,SAAA,CAAU,QAAA,EAAS;AACxC,EAAA,MAAM,QAAA,GAAW,aAAa,KAAK,CAAA;AAEnC,EAAA,IAAI,CAAE,MAAY,aAAA,CAAc,YAAkB,QAAA,CAAS,QAAQ,CAAC,CAAA,EAAI;AACtE,IAAA,MAAMC,WAAAA;AAAA,MACJ,UAAA;AAAA,MACA,MAAA;AAAA,MACA,aAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAA;AAAA,MACA,YAAA;AAAA,MACA,MAAA;AAAA,MACA,EAAC;AAAA,MACD,MAAA;AAAA,MACA,CAAC,OAAO,SAAS,CAAA;AAAA,MACjB;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAMC,QAAAA,CAAS,YAAY,MAAA,EAAQ,aAAA,EAAe,UAAU,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA;AAEhF,EAAA,IAAI,KAAK,QAAA,EAAU;AACjB,IAAA,MAAM,KAAA,GAAuB;AAAA,MAC3B,KAAA;AAAA,MACA,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,aAAa,IAAA,CAAK,WAAA;AAAA,MAClB,WAAW,IAAA,CAAK;AAAA,KAClB;AACA,IAAA,MAAMA,QAAAA,CAAS,YAAY,MAAA,EAAQ,aAAA,EAAe,eAAe,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,EACxF;AACF;AAGA,eAAsB,cAAA,CACpB,aACA,KAAA,EACuB;AACvB,EAAA,OAAQ,MAAY,QAAA,CAAS,YAAA,CAAa,KAAK,CAAC,CAAA;AAClD;AAOA,eAAsB,gBAAA,CACpB,aACA,OAAA,EAC0B;AAC1B,EAAA,OAAQ,MAAY,QAAA,CAAS,aAAA,EAAe,OAAO,CAAA;AACrD;AAOA,eAAsB,iBAAA,CACpB,YACA,MAAA,EACwB;AACxB,EAAA,MAAY,YAAA,CAAa,YAAY,MAAM,CAAA;AAC3C,EAAA,IAAI,MAAY,aAAA,CAAc,UAAA,EAAkB,QAAA,CAAS,aAAa,CAAC,CAAA,EAAG;AACxE,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAOD,WAAAA;AAAA,IACL,UAAA;AAAA,IACA,MAAA;AAAA,IACA,aAAA;AAAA,IACA,aAAA;AAAA,IACA,aAAA;AAAA,IACA,gBAAA;AAAA,IACA,OAAA;AAAA,IACA,EAAC;AAAA,IACD,MAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AACF;;;AClFA,eAAsB,WACpB,UAAA,EACA,MAAA,EACA,YAAA,EACA,aAAA,EACA,OACA,UAAA,EACyC;AACzC,EAAA,MAAM,IAAA,GAAO,MAAM,SAAA,CAAuB,CAAA;AAC1C,EAAA,MAAM,KAAA,GAAQ,MAAM,YAAY,CAAA;AAChC,EAAA,IAAI,KAAA,IAAS,KAAA,CAAM,IAAA,KAAS,IAAA,EAAM,OAAO,KAAA;AAEzC,EAAA,MAAM,WAAW,YAAA,CAAa,KAAA,CAAM,GAAG,CAAA,CAAE,KAAI,IAAK,YAAA;AAClD,EAAA,MAAM,OAAO,MAAY,MAAA;AAAA,IACvB,UAAA;AAAA,IACA,MAAA;AAAA,IACA,aAAA;AAAA,IACA,QAAA;AAAA,IACA,0BAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,OAAO,EAAE,MAAM,IAAA,EAAK;AACtB;AAKA,eAAsB,UAAA,CACpB,UAAA,EACA,MAAA,EACA,IAAA,EACiB;AACjB,EAAA,OAAa,MAAA;AAAA,IACX,UAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA,CAAK,UAAU,IAAI,CAAA;AAAA,IACnB,WAAA;AAAA,IACA;AAAA,GACF;AACF;AAKA,eAAsB,SAAS,QAAA,EAAqC;AAClE,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAY,WAAW,QAAQ,CAAA;AAChD,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,2BAAA,EAA8B,QAAQ,CAAA,CAAE,CAAA;AAAA,EAC1D;AACA,EAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AACxB;AAMA,eAAsB,SAAS,IAAA,EAA+B;AAC5D,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAY,WAAW,IAAI,CAAA;AAC5C,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,IAAI,CAAA,CAAE,CAAA;AAAA,EACjD;AACA,EAAA,OAAO,IAAA;AACT;;;ACjEO,IAAM,YAAN,MAAgB;AAAA;AAAA,EAErB,YAA6B,GAAA,EAAsB;AAAtB,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EAAuB;AAAA,EAAvB,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,MAAM,WAAW,IAAA,EAAiC;AAChD,IAAA,MAAM,EAAE,UAAA,EAAY,MAAA,EAAO,GAAI,IAAA,CAAK,GAAA;AACpC,IAAA,MAAgB,UAAA,CAAW,UAAA,EAAY,MAAA,EAAQ,IAAI,CAAA;AACnD,IAAA,MAAkB,iBAAA,CAAkB,UAAA,EAAY,MAAA,EAAQ,IAAA,CAAK,IAAI,CAAA;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,MAAA,CACJ,QAAA,EACA,OAAA,EACA,IAAA,EACiB;AACjB,IAAA,MAAM,EAAE,UAAA,EAAY,MAAA,EAAO,GAAI,IAAA,CAAK,GAAA;AACpC,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,SAAA,CAAU,QAAA,EAAS;AAExC,IAAA,MAAM,MAAA,GAAS,MAAkB,gBAAA,CAAiB,UAAA,EAAY,OAAO,QAAQ,CAAA;AAC7E,IAAA,MAAM,UAAoB,MAAA,GAAS,MAAc,SAAS,MAAA,CAAO,QAAQ,IAAI,EAAC;AAE9E,IAAA,MAAM,UAAoB,EAAC;AAC3B,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,OAAO,KAAK,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAG;AAClD,MAAA,OAAA,CAAQ,IAAI,IAAI,MAAc,UAAA;AAAA,QAC5B,UAAA;AAAA,QACA,MAAA;AAAA,QACA,IAAA;AAAA,QACA,OAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAW,MAAc,UAAA,CAAW,UAAA,EAAY,QAAQ,OAAO,CAAA;AAErE,IAAA,MAAM,MAAA,GAAiB;AAAA,MACrB,EAAA,EAAI,OAAO,UAAA,EAAW;AAAA,MACtB,OAAA;AAAA,MACA,QAAA;AAAA,MACA,gBAAgB,MAAA,EAAQ,EAAA;AAAA,MACxB,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,MACpB,MAAA,EAAQ;AAAA,KACV;AACA,IAAA,MAAkB,WAAA,CAAY,UAAA,EAAY,MAAA,EAAQ,QAAA,EAAU,MAAM,CAAA;AAClE,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,QAAA,CACJ,QAAA,EACA,QAAA,EACA,IAAA,EACiB;AACjB,IAAA,MAAM,EAAE,UAAA,EAAY,MAAA,EAAO,GAAI,IAAA,CAAK,GAAA;AACpC,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,SAAA,CAAU,QAAA,EAAS;AAExC,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI,aAAa,QAAA,EAAU;AACzB,MAAA,MAAA,GAAS,MAAkB,gBAAA,CAAiB,UAAA,EAAY,KAAA,EAAO,QAAQ,CAAA;AAAA,IACzE,CAAA,MAAO;AACL,MAAA,MAAM,OAAA,GAAU,MAAkB,iBAAA,CAAkB,UAAA,EAAY,OAAO,QAAQ,CAAA;AAC/E,MAAA,MAAA,GAAS,QAAQ,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,QAAQ,CAAA,IAAK,IAAA;AAAA,IACrD;AACA,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAI,MAAM,CAAA,kBAAA,EAAqB,QAAQ,OAAO,KAAK,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAE,CAAA;AAAA,IACzE;AAEA,IAAA,MAAM,IAAA,GAAO,MAAc,QAAA,CAAS,MAAA,CAAO,QAAQ,CAAA;AACnD,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAG;AAChD,MAAA,MAAM,OAAA,GAAU,MAAc,QAAA,CAAS,KAAA,CAAM,IAAI,CAAA;AACjD,MAAA,MAAM,IAAA,CAAK,MAAM,OAAO,CAAA;AAAA,IAC1B;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KAAA,CACJ,QAAA,EACA,KAAA,EACA,IAAA,EACiB;AACjB,IAAA,MAAM,EAAE,UAAA,EAAW,GAAI,IAAA,CAAK,GAAA;AAC5B,IAAA,MAAM,MAAA,GAAS,MAAkB,gBAAA,CAAiB,UAAA,EAAY,OAAO,QAAQ,CAAA;AAC7E,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAE,CAAA;AAAA,IACtD;AACA,IAAA,MAAM,IAAA,GAAO,MAAc,QAAA,CAAS,MAAA,CAAO,QAAQ,CAAA;AACnD,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAG;AAChD,MAAA,MAAM,OAAA,GAAU,MAAc,QAAA,CAAS,KAAA,CAAM,IAAI,CAAA;AACjD,MAAA,MAAM,IAAA,CAAK,MAAM,OAAO,CAAA;AAAA,IAC1B;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,GAAA,CACJ,KAAA,EACA,QAAA,EACA,OAAA,EACmB;AACnB,IAAA,OAAmB,iBAAA;AAAA,MACjB,KAAK,GAAA,CAAI,UAAA;AAAA,MACT,KAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAA,CACJ,KAAA,EACA,QAAA,EACA,IAAA,EACuE;AACvE,IAAA,MAAM,SAAS,MAAkB,gBAAA;AAAA,MAC/B,KAAK,GAAA,CAAI,UAAA;AAAA,MACT,KAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,MAAM,OAAiB,MAAA,GAAS,MAAc,SAAS,MAAA,CAAO,QAAQ,IAAI,EAAC;AAE3E,IAAA,MAAM,QAAkB,EAAC;AACzB,IAAA,MAAM,WAAqB,EAAC;AAC5B,IAAA,MAAM,YAAsB,EAAC;AAC7B,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,OAAO,KAAK,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAG;AAClD,MAAA,MAAM,KAAA,GAAQ,KAAK,IAAI,CAAA;AACvB,MAAA,IAAI,CAAC,KAAA,EAAO;AACV,QAAA,KAAA,CAAM,KAAK,IAAI,CAAA;AACf,QAAA;AAAA,MACF;AACA,MAAA,MAAM,IAAA,GAAO,MAAM,SAAA,CAAiB,CAAA;AACpC,MAAA,CAAC,MAAM,IAAA,KAAS,IAAA,GAAO,SAAA,GAAY,QAAA,EAAU,KAAK,IAAI,CAAA;AAAA,IACxD;AACA,IAAA,OAAO,EAAE,KAAA,EAAO,QAAA,EAAU,SAAA,EAAU;AAAA,EACtC;AACF","file":"index.js","sourcesContent":["// The single source of truth for table_hint strings.\n//\n// Readers re-derive PDAs with `iqlabs.utils.toSeedBytes(hint)` →\n// `iqlabs.contract.getTablePda(...)`; writers pass the same hint into\n// `iqlabs.writer.createTable`. Keeping the naming convention in one place\n// prevents silent drift between writer and reader. CODE-RULES §2 — if any\n// caller ever wants to build one of these strings inline, route it here\n// instead.\n\n/** DbRoot id for every iq-git table. Bootstrap and every caller share this. */\nexport const IQGIT_ROOT_ID = \"iq-git-v1\";\n\n/** `git_repos:all` — open-writers registry that drives the public gallery. */\nexport const REGISTRY_HINT = \"git_repos:all\";\n\n/**\n * Hint for the per-owner personal repo list.\n * input: owner wallet base58\n * output: \"git_repos_v2_<owner>\"\n */\nexport function repoListHint(owner: string): string {\n return `git_repos_v2_${owner}`;\n}\n\n/**\n * Hint for the per-repo commit table.\n * input: owner wallet base58, repo name (any characters — SDK keccak-hashes)\n * output: \"git_commits:<owner>:<repo>\"\n */\nexport function commitTableHint(owner: string, repo: string): string {\n return `git_commits:${owner}:${repo}`;\n}\n","// SHA-256 content hashing for the blob-dedup index. Runtime-specific\n// implementations live in `platform/hash-node.ts` and `platform/hash-browser.ts`;\n// this module just declares the interface the rest of the SDK uses.\n//\n// Do NOT use this for deriving table seeds — that path is owned by\n// `iqlabs.utils.toSeedBytes` (keccak) and must stay aligned with the SDK.\n\n/**\n * SHA-256 of a UTF-8 or base64 string, returned as a lowercase hex digest.\n * Implementation is injected by the platform entry (node.ts / browser.ts).\n */\nexport type Sha256Hex = (input: string) => Promise<string>;\n\n// Platform entries (src/node.ts, src/browser.ts) will call `setSha256(...)`\n// during module init so downstream layers never care which runtime they are in.\n// This indirection is the one place where a wrapper is justified (CODE-RULES §1)\n// because the behavior genuinely differs per runtime.\nlet impl: Sha256Hex | undefined;\n\nexport function setSha256(fn: Sha256Hex): void {\n if (impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 implementation already installed. Import exactly one of '@iqlabs-official/git-sdk/node' or '@iqlabs-official/git-sdk/browser'.\",\n );\n }\n impl = fn;\n}\n\nexport async function sha256Hex(input: string): Promise<string> {\n if (!impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 not installed. Import '@iqlabs-official/git-sdk/node' (Node) or '@iqlabs-official/git-sdk/browser' (browser) before using the SDK.\",\n );\n }\n return impl(input);\n}\n","// L1 — chain primitives shared by L2/L3. Kept tiny by design: each function\n// here either (a) hides PDA derivation so callers only deal with hints, or\n// (b) wraps a single iqlabs-sdk call to keep an iq-git-specific default\n// (chunk speed, root id, ...). Pure passthroughs like `createTable` /\n// `writeRow` live at the call site instead — wrapping them once more would\n// just bury the iqlabs-sdk surface (CODE-RULES §1).\n\nimport {\n Keypair,\n SystemProgram,\n Transaction,\n type Connection,\n type PublicKey,\n} from \"@solana/web3.js\";\nimport {\n createInstructionBuilder,\n getDbRootPda,\n getTablePda,\n initializeDbRootInstruction,\n} from \"@iqlabs-official/solana-sdk/contract\";\nimport { readCodeIn, readTableRows } from \"@iqlabs-official/solana-sdk/reader\";\nimport { toSeedBytes, type SignerInput, type WalletSigner } from \"@iqlabs-official/solana-sdk/utils\";\nimport { codeIn as sdkCodeIn } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID } from \"../core/seed\";\n\n/** DbRoot PDA for the `iq-git-v1` namespace — derived once, reused everywhere. */\nexport const DB_ROOT_SEED = toSeedBytes(IQGIT_ROOT_ID);\nexport const DB_ROOT = getDbRootPda(DB_ROOT_SEED);\n\n/**\n * Initialize the `iq-git-v1` DbRoot account if it doesn't exist. First-call\n * cost on a fresh network. Idempotent: returns null if already initialized.\n * Accepts any SignerInput so an admin can run it from a wallet, not just\n * from a Keypair.\n */\nexport async function ensureDbRoot(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n if (await accountExists(connection, DB_ROOT)) return null;\n const builder = createInstructionBuilder();\n const ix = initializeDbRootInstruction(\n builder,\n {\n db_root: DB_ROOT,\n signer: signer.publicKey,\n system_program: SystemProgram.programId,\n },\n { db_root_id: DB_ROOT_SEED },\n );\n const tx = new Transaction().add(ix);\n const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();\n tx.recentBlockhash = blockhash;\n tx.feePayer = signer.publicKey;\n const signed = await signTx(signer, tx);\n const signature = await connection.sendRawTransaction(signed.serialize());\n await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });\n return signature;\n}\n\n// Sign a single Transaction with whatever shape the signer takes. Keypair\n// has a `secretKey`; wallet adapters expose `signTransaction`. iqlabs-sdk's\n// own `sendTx` uses the same dispatch; we replicate it here so the helper\n// stays self-contained.\nasync function signTx(signer: SignerInput, tx: Transaction): Promise<Transaction> {\n if (signer instanceof Keypair || \"secretKey\" in signer) {\n tx.partialSign(signer as Keypair);\n return tx;\n }\n return (signer as WalletSigner).signTransaction(tx);\n}\n\n/**\n * Read rows from a table. Translates our hint into the PDA and forwards to\n * `iqlabs.reader.readTableRows`.\n */\nexport async function readRows(\n hint: string,\n options?: { limit?: number; before?: string },\n): Promise<Array<Record<string, unknown>>> {\n return readTableRows(tablePda(hint), options);\n}\n\n/**\n * Fetch just the latest row of a table — the fast path for \"what is the\n * current commit\" and \"what is the pinned deploy\".\n */\nexport async function readLatestRow(\n hint: string,\n): Promise<Record<string, unknown> | null> {\n const rows = await readRows(hint, { limit: 1 });\n return rows[0] ?? null;\n}\n\n/**\n * Upload a blob via `iqlabs.writer.codeIn`. The SDK chunks internally when\n * `data` is a plain string. We default speed to \"light\" because that's the\n * Helius-friendly setting for git workloads (per-file uploads are bursty).\n */\nexport async function codeIn(\n connection: Connection,\n signer: SignerInput,\n data: string | string[],\n filename: string,\n filetype: string,\n onProgress?: (percent: number) => void,\n speed: \"light\" | \"medium\" | \"fast\" = \"light\",\n): Promise<string> {\n return sdkCodeIn(\n { connection, signer },\n data,\n filename,\n 0,\n filetype,\n onProgress,\n speed,\n );\n}\n\nexport { readCodeIn };\n\n/** Cheap existence check for a PDA. */\nexport async function accountExists(\n connection: Connection,\n pda: PublicKey,\n): Promise<boolean> {\n return (await connection.getAccountInfo(pda)) !== null;\n}\n\n/** Resolve a hint to its PDA. */\nexport function tablePda(hint: string): PublicKey {\n return getTablePda(DB_ROOT, toSeedBytes(hint));\n}\n","// L3 — per-repo commit table.\n//\n// v2's core invariant: each repo has its own table at hint\n// `git_commits:<owner>:<repo>`, with writers locked to [owner]. That means\n// \"the most recent successful tx in this table = the latest commit\", and we\n// read it as a single-row query (limit: 1).\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, commitTableHint } from \"../core/seed\";\nimport type { Commit } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst COMMIT_COLUMNS = [\n \"id\",\n \"message\",\n \"treeTxId\",\n \"parentCommitId\",\n \"timestamp\",\n \"author\",\n];\n\n/**\n * Ensure the per-repo commit table exists with writers = [owner]. No-op if\n * it already exists.\n */\nexport async function ensureCommitTable(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n): Promise<string | null> {\n const hint = commitTableHint(signer.publicKey.toBase58(), repo);\n if (await chain.accountExists(connection, chain.tablePda(hint))) return null;\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n hint,\n hint,\n COMMIT_COLUMNS,\n \"id\",\n [],\n undefined,\n [signer.publicKey],\n hint,\n );\n}\n\n/**\n * Append one commit row. Callers (workflow-level code) are responsible for\n * setting parentCommitId — the SDK does not auto-chain.\n */\nexport async function writeCommit(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n commit: Commit,\n): Promise<string> {\n const owner = signer.publicKey.toBase58();\n return writeRow(\n connection,\n signer,\n IQGIT_ROOT_ID,\n commitTableHint(owner, repo),\n JSON.stringify(commit),\n );\n}\n\n/** Latest commit. Single-row, O(1) RPC path. */\nexport async function readLatestCommit(\n _connection: Connection,\n owner: string,\n repo: string,\n): Promise<Commit | null> {\n const row = await chain.readLatestRow(commitTableHint(owner, repo));\n return row as unknown as Commit | null;\n}\n\n/** Full commit history, newest first. */\nexport async function readCommitHistory(\n _connection: Connection,\n owner: string,\n repo: string,\n options?: { limit?: number; before?: string },\n): Promise<Commit[]> {\n const rows = await chain.readRows(commitTableHint(owner, repo), options);\n return rows as unknown as Commit[];\n}\n","// L3 — repo list and public registry.\n//\n// Covers the two \"directory\" tables:\n// • git_repos_v2_<owner> — owner's personal repo list (writers = [owner])\n// • git_repos:all — public gallery registry (writers = [])\n//\n// Commit tables are a separate concern — see `commit.ts`.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, REGISTRY_HINT, repoListHint } from \"../core/seed\";\nimport type { RegistryEntry, Repository } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst REPO_COLUMNS = [\"name\", \"description\", \"isPublic\", \"timestamp\"];\nconst REGISTRY_COLUMNS = [\"owner\", \"repo\", \"description\", \"timestamp\"];\n\n/**\n * Create a repo in the owner's personal list, and (if public) also register\n * it in the public gallery. Two transactions — the contract does not support\n * writing two tables atomically.\n */\nexport async function createRepo(\n connection: Connection,\n signer: SignerInput,\n meta: Repository,\n): Promise<void> {\n const owner = signer.publicKey.toBase58();\n const listHint = repoListHint(owner);\n\n if (!(await chain.accountExists(connection, chain.tablePda(listHint)))) {\n await createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n listHint,\n listHint,\n REPO_COLUMNS,\n \"name\",\n [],\n undefined,\n [signer.publicKey],\n listHint,\n );\n }\n\n await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));\n\n if (meta.isPublic) {\n const entry: RegistryEntry = {\n owner,\n repo: meta.name,\n description: meta.description,\n timestamp: meta.timestamp,\n };\n await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));\n }\n}\n\n/** List all repos owned by `owner`. */\nexport async function readOwnerRepos(\n _connection: Connection,\n owner: string,\n): Promise<Repository[]> {\n return (await chain.readRows(repoListHint(owner))) as unknown as Repository[];\n}\n\n/**\n * One page of the public-gallery registry. Callers should still check\n * `row.owner` shape before trusting it in UI; we do not filter at the SDK\n * boundary.\n */\nexport async function readRegistryPage(\n _connection: Connection,\n options?: { limit?: number; before?: string },\n): Promise<RegistryEntry[]> {\n return (await chain.readRows(REGISTRY_HINT, options)) as unknown as RegistryEntry[];\n}\n\n/**\n * One-time global bootstrap of the `git_repos:all` table. Run once per\n * network from an admin key; subsequent calls short-circuit because the\n * account already exists.\n */\nexport async function bootstrapRegistry(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n await chain.ensureDbRoot(connection, signer);\n if (await chain.accountExists(connection, chain.tablePda(REGISTRY_HINT))) {\n return null;\n }\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n REGISTRY_HINT,\n REGISTRY_HINT,\n REGISTRY_COLUMNS,\n \"owner\",\n [],\n undefined,\n undefined,\n REGISTRY_HINT,\n );\n}\n","// L2 — blob + tree storage.\n//\n// `uploadBlob` is the file-level \"don't re-upload what's already on-chain\"\n// primitive. Content is always a base64 string (matching iq-git v1) so the\n// hash / dedup comparison stays byte-for-byte compatible across commits.\n// The reuse-map is just a plain object so callers can build it any way they\n// like (path→entry, or hash→txId for rename-aware dedup — CODE-RULES §3\n// keeps this inlined at the call site instead of a new type).\n//\n// `uploadTree` / `loadTree` serialize FileTree <-> on-chain tree.json as raw\n// JSON (filetype `application/json`), again matching v1.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport type { FileTree } from \"../core/types\";\nimport * as chain from \"./chain\";\n\n/**\n * Upload one file unless an identical hash is already in `reuse` (blob dedup).\n *\n * input: connection, signer, relativePath, base64 content, reuse map, optional onProgress\n * output: { txId, hash } — either reused or freshly uploaded\n */\nexport async function uploadBlob(\n connection: Connection,\n signer: SignerInput,\n relativePath: string,\n base64Content: string,\n reuse: FileTree,\n onProgress?: (percent: number) => void,\n): Promise<{ txId: string; hash: string }> {\n const hash = await sha256Hex(base64Content);\n const prior = reuse[relativePath];\n if (prior && prior.hash === hash) return prior;\n\n const filename = relativePath.split(\"/\").pop() || relativePath;\n const txId = await chain.codeIn(\n connection,\n signer,\n base64Content,\n filename,\n \"application/octet-stream\",\n onProgress,\n );\n return { txId, hash };\n}\n\n/**\n * Serialize a FileTree and upload it as one `tree.json` blob.\n */\nexport async function uploadTree(\n connection: Connection,\n signer: SignerInput,\n tree: FileTree,\n): Promise<string> {\n return chain.codeIn(\n connection,\n signer,\n JSON.stringify(tree),\n \"tree.json\",\n \"application/json\",\n );\n}\n\n/**\n * Fetch and parse a `tree.json` blob by its tx signature.\n */\nexport async function loadTree(treeTxId: string): Promise<FileTree> {\n const { data } = await chain.readCodeIn(treeTxId);\n if (data === null) {\n throw new Error(`tree.json not found for tx ${treeTxId}`);\n }\n return JSON.parse(data) as FileTree;\n}\n\n/**\n * Retrieve a blob's bytes by its txId. Returns the raw string that was\n * uploaded — for blobs this is base64; the caller decodes.\n */\nexport async function loadBlob(txId: string): Promise<string> {\n const { data } = await chain.readCodeIn(txId);\n if (data === null) {\n throw new Error(`blob not found for tx ${txId}`);\n }\n return data;\n}\n","// L4 — GitClient facade. High-level workflows built by composing the\n// layers below. This is the surface consumers (CLI, frontend, migrator) use;\n// they should not reach into `chain` / `storage` / `repo` / `commit` directly.\n//\n// Keep workflow logic here, not inside lower layers (CODE-RULES §5). When a\n// command needs scanning, hashing, uploading, row writing — this file\n// orchestrates them all.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport type { Commit, FileTree, Repository } from \"../core/types\";\nimport * as commitLayer from \"./commit\";\nimport * as repoLayer from \"./repo\";\nimport * as storage from \"./storage\";\n\nexport interface GitClientConfig {\n connection: Connection;\n signer: SignerInput;\n}\n\nexport class GitClient {\n // CODE-RULES §3 — only one small shape; inlined rather than aliased.\n constructor(private readonly cfg: GitClientConfig) {}\n\n /**\n * Create a new repo. Wraps repo.createRepo + pre-creating the commit table\n * so the first `commit()` call doesn't need to pay createTable cost.\n */\n async createRepo(meta: Repository): Promise<void> {\n const { connection, signer } = this.cfg;\n await repoLayer.createRepo(connection, signer, meta);\n await commitLayer.ensureCommitTable(connection, signer, meta.name);\n }\n\n /**\n * Incremental commit against a scanned directory snapshot.\n *\n * input: repoName, message, scan — a `{ [path]: base64Content }` map that\n * the caller produced (Node: fs walk + base64; browser: File input).\n * output: newly written Commit\n */\n async commit(\n repoName: string,\n message: string,\n scan: Record<string, string>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n const latest = await commitLayer.readLatestCommit(connection, owner, repoName);\n const oldTree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const newTree: FileTree = {};\n for (const [path, content] of Object.entries(scan)) {\n newTree[path] = await storage.uploadBlob(\n connection,\n signer,\n path,\n content,\n oldTree,\n );\n }\n\n const treeTxId = await storage.uploadTree(connection, signer, newTree);\n\n const commit: Commit = {\n id: crypto.randomUUID(),\n message,\n treeTxId,\n parentCommitId: latest?.id,\n timestamp: Date.now(),\n author: owner,\n };\n await commitLayer.writeCommit(connection, signer, repoName, commit);\n return commit;\n }\n\n /**\n * Restore a commit's files into a caller-provided sink. Runtime-neutral\n * shape mirrors `commit`: the caller decides how to write bytes to disk /\n * to a File System Access API handle / anywhere else. `content` is the\n * raw base64 string — the sink decodes.\n */\n async checkout(\n repoName: string,\n commitId: string | \"latest\",\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n let target: Commit | null;\n if (commitId === \"latest\") {\n target = await commitLayer.readLatestCommit(connection, owner, repoName);\n } else {\n const history = await commitLayer.readCommitHistory(connection, owner, repoName);\n target = history.find((c) => c.id === commitId) ?? null;\n }\n if (!target) {\n throw new Error(`commit not found: ${commitId} in ${owner}/${repoName}`);\n }\n\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /**\n * Whole-repo snapshot download — convenience on top of checkout(\"latest\")\n * but reading from someone else's `owner`. We do not require signer to\n * equal owner for reads.\n */\n async clone(\n repoName: string,\n owner: string,\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection } = this.cfg;\n const target = await commitLayer.readLatestCommit(connection, owner, repoName);\n if (!target) {\n throw new Error(`no commits in ${owner}/${repoName}`);\n }\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /** Commit history for a repo. */\n async log(\n owner: string,\n repoName: string,\n options?: { limit?: number; before?: string },\n ): Promise<Commit[]> {\n return commitLayer.readCommitHistory(\n this.cfg.connection,\n owner,\n repoName,\n options,\n );\n }\n\n /**\n * Compare a current directory snapshot to the latest commit's tree.\n */\n async status(\n owner: string,\n repoName: string,\n scan: Record<string, string>,\n ): Promise<{ added: string[]; modified: string[]; unchanged: string[] }> {\n const latest = await commitLayer.readLatestCommit(\n this.cfg.connection,\n owner,\n repoName,\n );\n const tree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const added: string[] = [];\n const modified: string[] = [];\n const unchanged: string[] = [];\n for (const [path, content] of Object.entries(scan)) {\n const prior = tree[path];\n if (!prior) {\n added.push(path);\n continue;\n }\n const hash = await sha256Hex(content);\n (prior.hash === hash ? unchanged : modified).push(path);\n }\n return { added, modified, unchanged };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/seed.ts","../../src/core/hash.ts","../../src/layers/chain.ts","../../src/layers/commit.ts","../../src/layers/repo.ts","../../src/layers/storage.ts","../../src/layers/client.ts"],"names":["sdkCodeIn","createTable","writeRow","tablePda"],"mappings":"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","file":"index.js","sourcesContent":["// The single source of truth for table_hint strings.\n//\n// Readers re-derive PDAs with `iqlabs.utils.toSeedBytes(hint)` →\n// `iqlabs.contract.getTablePda(...)`; writers pass the same hint into\n// `iqlabs.writer.createTable`. Keeping the naming convention in one place\n// prevents silent drift between writer and reader. CODE-RULES §2 — if any\n// caller ever wants to build one of these strings inline, route it here\n// instead.\n\n/** DbRoot id for every iq-git table. Bootstrap and every caller share this. */\nexport const IQGIT_ROOT_ID = \"iq-git-v1\";\n\n/** `git_repos:all` — open-writers registry that drives the public gallery. */\nexport const REGISTRY_HINT = \"git_repos:all\";\n\n/**\n * Hint for the per-owner personal repo list.\n * input: owner wallet base58\n * output: \"git_repos_v2_<owner>\"\n */\nexport function repoListHint(owner: string): string {\n return `git_repos_v2_${owner}`;\n}\n\n/**\n * Hint for the per-repo commit table.\n * input: owner wallet base58, repo name (any characters — SDK keccak-hashes)\n * output: \"git_commits:<owner>:<repo>\"\n */\nexport function commitTableHint(owner: string, repo: string): string {\n return `git_commits:${owner}:${repo}`;\n}\n","// SHA-256 content hashing for the blob-dedup index. Runtime-specific\n// implementations live in `platform/hash-node.ts` and `platform/hash-browser.ts`;\n// this module just declares the interface the rest of the SDK uses.\n//\n// Do NOT use this for deriving table seeds — that path is owned by\n// `iqlabs.utils.toSeedBytes` (keccak) and must stay aligned with the SDK.\n\n/**\n * SHA-256 of a UTF-8 or base64 string, returned as a lowercase hex digest.\n * Implementation is injected by the platform entry (node.ts / browser.ts).\n */\nexport type Sha256Hex = (input: string) => Promise<string>;\n\n// Platform entries (src/node.ts, src/browser.ts) will call `setSha256(...)`\n// during module init so downstream layers never care which runtime they are in.\n// This indirection is the one place where a wrapper is justified (CODE-RULES §1)\n// because the behavior genuinely differs per runtime.\nlet impl: Sha256Hex | undefined;\n\nexport function setSha256(fn: Sha256Hex): void {\n if (impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 implementation already installed. Import exactly one of '@iqlabs-official/git-sdk/node' or '@iqlabs-official/git-sdk/browser'.\",\n );\n }\n impl = fn;\n}\n\nexport async function sha256Hex(input: string): Promise<string> {\n if (!impl) {\n throw new Error(\n \"@iqlabs-official/git-sdk: sha256 not installed. Import '@iqlabs-official/git-sdk/node' (Node) or '@iqlabs-official/git-sdk/browser' (browser) before using the SDK.\",\n );\n }\n return impl(input);\n}\n","// L1 — chain primitives shared by L2/L3. Kept tiny by design: each function\n// here either (a) hides PDA derivation so callers only deal with hints, or\n// (b) wraps a single iqlabs-sdk call to keep an iq-git-specific default\n// (chunk speed, root id, ...). Pure passthroughs like `createTable` /\n// `writeRow` live at the call site instead — wrapping them once more would\n// just bury the iqlabs-sdk surface (CODE-RULES §1).\n\nimport {\n Keypair,\n SystemProgram,\n Transaction,\n type Connection,\n type PublicKey,\n} from \"@solana/web3.js\";\nimport {\n createInstructionBuilder,\n getDbRootPda,\n getTablePda,\n initializeDbRootInstruction,\n} from \"@iqlabs-official/solana-sdk/contract\";\nimport { readCodeIn, readTableRows } from \"@iqlabs-official/solana-sdk/reader\";\nimport { toSeedBytes, type SignerInput, type WalletSigner } from \"@iqlabs-official/solana-sdk/utils\";\nimport { codeIn as sdkCodeIn } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID } from \"../core/seed\";\n\n/** DbRoot PDA for the `iq-git-v1` namespace — derived once, reused everywhere. */\nexport const DB_ROOT_SEED = toSeedBytes(IQGIT_ROOT_ID);\nexport const DB_ROOT = getDbRootPda(DB_ROOT_SEED);\n\n/**\n * Initialize the `iq-git-v1` DbRoot account if it doesn't exist. First-call\n * cost on a fresh network. Idempotent: returns null if already initialized.\n * Accepts any SignerInput so an admin can run it from a wallet, not just\n * from a Keypair.\n */\nexport async function ensureDbRoot(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n if (await accountExists(connection, DB_ROOT)) return null;\n const builder = createInstructionBuilder();\n const ix = initializeDbRootInstruction(\n builder,\n {\n db_root: DB_ROOT,\n signer: signer.publicKey,\n system_program: SystemProgram.programId,\n },\n { db_root_id: DB_ROOT_SEED },\n );\n const tx = new Transaction().add(ix);\n const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();\n tx.recentBlockhash = blockhash;\n tx.feePayer = signer.publicKey;\n const signed = await signTx(signer, tx);\n const signature = await connection.sendRawTransaction(signed.serialize());\n await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });\n return signature;\n}\n\n// Sign a single Transaction with whatever shape the signer takes. Keypair\n// has a `secretKey`; wallet adapters expose `signTransaction`. iqlabs-sdk's\n// own `sendTx` uses the same dispatch; we replicate it here so the helper\n// stays self-contained.\nasync function signTx(signer: SignerInput, tx: Transaction): Promise<Transaction> {\n if (signer instanceof Keypair || \"secretKey\" in signer) {\n tx.partialSign(signer as Keypair);\n return tx;\n }\n return (signer as WalletSigner).signTransaction(tx);\n}\n\n/**\n * Read rows from a table. Translates our hint into the PDA and forwards to\n * `iqlabs.reader.readTableRows`.\n */\nexport async function readRows(\n hint: string,\n options?: { limit?: number; before?: string },\n): Promise<Array<Record<string, unknown>>> {\n return readTableRows(tablePda(hint), options);\n}\n\n/**\n * Fetch just the latest row of a table — the fast path for \"what is the\n * current commit\" and \"what is the pinned deploy\".\n */\nexport async function readLatestRow(\n hint: string,\n): Promise<Record<string, unknown> | null> {\n const rows = await readRows(hint, { limit: 1 });\n return rows[0] ?? null;\n}\n\n/**\n * Upload a blob via `iqlabs.writer.codeIn`. The SDK chunks internally when\n * `data` is a plain string. We default speed to \"light\" because that's the\n * Helius-friendly setting for git workloads (per-file uploads are bursty).\n */\nexport async function codeIn(\n connection: Connection,\n signer: SignerInput,\n data: string | string[],\n filename: string,\n filetype: string,\n onProgress?: (percent: number) => void,\n speed: \"light\" | \"medium\" | \"fast\" = \"light\",\n): Promise<string> {\n return sdkCodeIn(\n { connection, signer },\n data,\n filename,\n 0,\n filetype,\n onProgress,\n speed,\n );\n}\n\nexport { readCodeIn };\n\n/** Cheap existence check for a PDA. */\nexport async function accountExists(\n connection: Connection,\n pda: PublicKey,\n): Promise<boolean> {\n return (await connection.getAccountInfo(pda)) !== null;\n}\n\n/** Resolve a hint to its PDA. */\nexport function tablePda(hint: string): PublicKey {\n return getTablePda(DB_ROOT, toSeedBytes(hint));\n}\n","// L3 — per-repo commit table.\n//\n// v2's core invariant: each repo has its own table at hint\n// `git_commits:<owner>:<repo>`, with writers locked to [owner]. That means\n// \"the most recent successful tx in this table = the latest commit\", and we\n// read it as a single-row query (limit: 1).\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, commitTableHint } from \"../core/seed\";\nimport type { Commit } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst COMMIT_COLUMNS = [\n \"id\",\n \"message\",\n \"treeTxId\",\n \"parentCommitId\",\n \"timestamp\",\n \"author\",\n];\n\n/**\n * Ensure the per-repo commit table exists with writers = [owner]. No-op if\n * it already exists.\n */\nexport async function ensureCommitTable(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n): Promise<string | null> {\n const hint = commitTableHint(signer.publicKey.toBase58(), repo);\n if (await chain.accountExists(connection, chain.tablePda(hint))) return null;\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n hint,\n hint,\n COMMIT_COLUMNS,\n \"id\",\n [],\n undefined,\n [signer.publicKey],\n hint,\n );\n}\n\n/**\n * Append one commit row. Callers (workflow-level code) are responsible for\n * setting parentCommitId — the SDK does not auto-chain.\n */\nexport async function writeCommit(\n connection: Connection,\n signer: SignerInput,\n repo: string,\n commit: Commit,\n): Promise<string> {\n const owner = signer.publicKey.toBase58();\n return writeRow(\n connection,\n signer,\n IQGIT_ROOT_ID,\n commitTableHint(owner, repo),\n JSON.stringify(commit),\n );\n}\n\n/** Latest commit. Single-row, O(1) RPC path. */\nexport async function readLatestCommit(\n _connection: Connection,\n owner: string,\n repo: string,\n): Promise<Commit | null> {\n const row = await chain.readLatestRow(commitTableHint(owner, repo));\n return row as unknown as Commit | null;\n}\n\n/** Full commit history, newest first. */\nexport async function readCommitHistory(\n _connection: Connection,\n owner: string,\n repo: string,\n options?: { limit?: number; before?: string },\n): Promise<Commit[]> {\n const rows = await chain.readRows(commitTableHint(owner, repo), options);\n return rows as unknown as Commit[];\n}\n","// L3 — repo list and public registry.\n//\n// Covers the two \"directory\" tables:\n// • git_repos_v2_<owner> — owner's personal repo list (writers = [owner])\n// • git_repos:all — public gallery registry (writers = [])\n//\n// Commit tables are a separate concern — see `commit.ts`.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport { type SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { createTable, writeRow } from \"@iqlabs-official/solana-sdk/writer\";\nimport { IQGIT_ROOT_ID, REGISTRY_HINT, repoListHint } from \"../core/seed\";\nimport type { RegistryEntry, Repository } from \"../core/types\";\nimport * as chain from \"./chain\";\n\nconst REPO_COLUMNS = [\"name\", \"description\", \"isPublic\", \"timestamp\"];\nconst REGISTRY_COLUMNS = [\"owner\", \"repo\", \"description\", \"timestamp\"];\n\n/**\n * Create a repo in the owner's personal list, and (if public) also register\n * it in the public gallery. Two transactions — the contract does not support\n * writing two tables atomically.\n *\n * Returns the writeRow tx signatures (and the table hint each one wrote to)\n * so a caller can notify gateways / build SSE streams for those tables. The\n * returned shape is `[]` from `createTable` calls because those don't write\n * a row, just allocate the account.\n */\nexport async function createRepo(\n connection: Connection,\n signer: SignerInput,\n meta: Repository,\n): Promise<Array<{ tableHint: string; sig: string; row: object }>> {\n const owner = signer.publicKey.toBase58();\n const listHint = repoListHint(owner);\n const writes: Array<{ tableHint: string; sig: string; row: object }> = [];\n\n if (!(await chain.accountExists(connection, chain.tablePda(listHint)))) {\n await createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n listHint,\n listHint,\n REPO_COLUMNS,\n \"name\",\n [],\n undefined,\n [signer.publicKey],\n listHint,\n );\n }\n\n const sig = await writeRow(connection, signer, IQGIT_ROOT_ID, listHint, JSON.stringify(meta));\n writes.push({ tableHint: listHint, sig, row: meta });\n\n if (meta.isPublic) {\n const entry: RegistryEntry = {\n owner,\n repo: meta.name,\n description: meta.description,\n timestamp: meta.timestamp,\n };\n const regSig = await writeRow(connection, signer, IQGIT_ROOT_ID, REGISTRY_HINT, JSON.stringify(entry));\n writes.push({ tableHint: REGISTRY_HINT, sig: regSig, row: entry });\n }\n\n return writes;\n}\n\n/** List all repos owned by `owner`. */\nexport async function readOwnerRepos(\n _connection: Connection,\n owner: string,\n): Promise<Repository[]> {\n return (await chain.readRows(repoListHint(owner))) as unknown as Repository[];\n}\n\n/**\n * One page of the public-gallery registry. Callers should still check\n * `row.owner` shape before trusting it in UI; we do not filter at the SDK\n * boundary.\n */\nexport async function readRegistryPage(\n _connection: Connection,\n options?: { limit?: number; before?: string },\n): Promise<RegistryEntry[]> {\n return (await chain.readRows(REGISTRY_HINT, options)) as unknown as RegistryEntry[];\n}\n\n/**\n * One-time global bootstrap of the `git_repos:all` table. Run once per\n * network from an admin key; subsequent calls short-circuit because the\n * account already exists.\n */\nexport async function bootstrapRegistry(\n connection: Connection,\n signer: SignerInput,\n): Promise<string | null> {\n await chain.ensureDbRoot(connection, signer);\n if (await chain.accountExists(connection, chain.tablePda(REGISTRY_HINT))) {\n return null;\n }\n return createTable(\n connection,\n signer as never,\n IQGIT_ROOT_ID,\n REGISTRY_HINT,\n REGISTRY_HINT,\n REGISTRY_COLUMNS,\n \"owner\",\n [],\n undefined,\n undefined,\n REGISTRY_HINT,\n );\n}\n","// L2 — blob + tree storage.\n//\n// `uploadBlob` is the file-level \"don't re-upload what's already on-chain\"\n// primitive. Content is always a base64 string (matching iq-git v1) so the\n// hash / dedup comparison stays byte-for-byte compatible across commits.\n// The reuse-map is just a plain object so callers can build it any way they\n// like (path→entry, or hash→txId for rename-aware dedup — CODE-RULES §3\n// keeps this inlined at the call site instead of a new type).\n//\n// `uploadTree` / `loadTree` serialize FileTree <-> on-chain tree.json as raw\n// JSON (filetype `application/json`), again matching v1.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport type { FileTree } from \"../core/types\";\nimport * as chain from \"./chain\";\n\n/**\n * Upload one file unless an identical hash is already in `reuse` (blob dedup).\n *\n * input: connection, signer, relativePath, base64 content, reuse map, optional onProgress\n * output: { txId, hash } — either reused or freshly uploaded\n */\nexport async function uploadBlob(\n connection: Connection,\n signer: SignerInput,\n relativePath: string,\n base64Content: string,\n reuse: FileTree,\n onProgress?: (percent: number) => void,\n): Promise<{ txId: string; hash: string }> {\n const hash = await sha256Hex(base64Content);\n const prior = reuse[relativePath];\n if (prior && prior.hash === hash) return prior;\n\n // Tag the codeIn filename with our app marker so non-inline inventory\n // entries can be classified by inspecting metadata.filename alone (no row\n // body fetch needed). Format: \"iqgit-blob:<basename>\". Same scheme used\n // for trees (\"iqgit-tree\") and other writes across the IQ ecosystem.\n const basename = relativePath.split(\"/\").pop() || relativePath;\n const txId = await chain.codeIn(\n connection,\n signer,\n base64Content,\n `iqgit-blob:${basename}`,\n \"application/octet-stream\",\n onProgress,\n );\n return { txId, hash };\n}\n\n/**\n * Serialize a FileTree and upload it as one `tree.json` blob.\n */\nexport async function uploadTree(\n connection: Connection,\n signer: SignerInput,\n tree: FileTree,\n): Promise<string> {\n return chain.codeIn(\n connection,\n signer,\n JSON.stringify(tree),\n \"iqgit-tree\",\n \"application/json\",\n );\n}\n\n/**\n * Fetch and parse a `tree.json` blob by its tx signature.\n */\nexport async function loadTree(treeTxId: string): Promise<FileTree> {\n const { data } = await chain.readCodeIn(treeTxId);\n if (data === null) {\n throw new Error(`tree.json not found for tx ${treeTxId}`);\n }\n return JSON.parse(data) as FileTree;\n}\n\n/**\n * Retrieve a blob's bytes by its txId. Returns the raw string that was\n * uploaded — for blobs this is base64; the caller decodes.\n */\nexport async function loadBlob(txId: string): Promise<string> {\n const { data } = await chain.readCodeIn(txId);\n if (data === null) {\n throw new Error(`blob not found for tx ${txId}`);\n }\n return data;\n}\n","// L4 — GitClient facade. High-level workflows built by composing the\n// layers below. This is the surface consumers (CLI, frontend, migrator) use;\n// they should not reach into `chain` / `storage` / `repo` / `commit` directly.\n//\n// Keep workflow logic here, not inside lower layers (CODE-RULES §5). When a\n// command needs scanning, hashing, uploading, row writing — this file\n// orchestrates them all.\n\nimport type { Connection } from \"@solana/web3.js\";\nimport type { SignerInput } from \"@iqlabs-official/solana-sdk/utils\";\nimport { sha256Hex } from \"../core/hash\";\nimport { commitTableHint } from \"../core/seed\";\nimport type { Commit, FileTree, Repository } from \"../core/types\";\nimport * as chain from \"./chain\";\nimport * as commitLayer from \"./commit\";\nimport * as repoLayer from \"./repo\";\nimport * as storage from \"./storage\";\n\n/** Information surfaced to the `onWrite` callback for every successful row\n * write the SDK performs. Consumers wire this to their gateway notify so\n * the gateway updates its cache / SSE stream immediately. */\nexport interface WriteEvent {\n /** Resolved table PDA, base58 — pass directly to gateway notify. */\n tablePda: string;\n /** The hint that derived the PDA, in case callers want to log it. */\n tableHint: string;\n /** writeRow tx signature. */\n sig: string;\n /** The row object that was written (already JSON.stringify-able). */\n row: object;\n}\n\nexport interface GitClientConfig {\n connection: Connection;\n signer: SignerInput;\n /** Fires after each successful row write inside the SDK. Fire-and-forget\n * callback for gateway notifies; throwing here will surface to the caller. */\n onWrite?: (event: WriteEvent) => void;\n}\n\nexport class GitClient {\n // CODE-RULES §3 — only one small shape; inlined rather than aliased.\n constructor(private readonly cfg: GitClientConfig) {}\n\n /**\n * Create a new repo. Wraps repo.createRepo + pre-creating the commit table\n * so the first `commit()` call doesn't need to pay createTable cost.\n */\n async createRepo(meta: Repository): Promise<void> {\n const { connection, signer } = this.cfg;\n const writes = await repoLayer.createRepo(connection, signer, meta);\n await commitLayer.ensureCommitTable(connection, signer, meta.name);\n this.fireOnWrite(writes);\n }\n\n /**\n * Incremental commit against a scanned directory snapshot.\n *\n * input: repoName, message, scan — a `{ [path]: base64Content }` map that\n * the caller produced (Node: fs walk + base64; browser: File input).\n * output: newly written Commit\n */\n async commit(\n repoName: string,\n message: string,\n scan: Record<string, string>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n const latest = await commitLayer.readLatestCommit(connection, owner, repoName);\n const oldTree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const newTree: FileTree = {};\n for (const [path, content] of Object.entries(scan)) {\n newTree[path] = await storage.uploadBlob(\n connection,\n signer,\n path,\n content,\n oldTree,\n );\n }\n\n const treeTxId = await storage.uploadTree(connection, signer, newTree);\n\n const commit: Commit = {\n id: crypto.randomUUID(),\n message,\n treeTxId,\n parentCommitId: latest?.id,\n timestamp: Date.now(),\n author: owner,\n };\n const sig = await commitLayer.writeCommit(connection, signer, repoName, commit);\n this.fireOnWrite([{ tableHint: commitTableHint(owner, repoName), sig, row: commit }]);\n return commit;\n }\n\n /** Resolve the table hint to a PDA and forward each write to `onWrite`.\n * Wrapped in try/catch per call so a misbehaving notify can't fail the\n * whole batch — but we still surface the error so the consumer knows. */\n private fireOnWrite(writes: Array<{ tableHint: string; sig: string; row: object }>) {\n if (!this.cfg.onWrite) return;\n for (const w of writes) {\n const tablePda = chain.tablePda(w.tableHint).toBase58();\n this.cfg.onWrite({ tablePda, tableHint: w.tableHint, sig: w.sig, row: w.row });\n }\n }\n\n /**\n * Restore a commit's files into a caller-provided sink. Runtime-neutral\n * shape mirrors `commit`: the caller decides how to write bytes to disk /\n * to a File System Access API handle / anywhere else. `content` is the\n * raw base64 string — the sink decodes.\n */\n async checkout(\n repoName: string,\n commitId: string | \"latest\",\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection, signer } = this.cfg;\n const owner = signer.publicKey.toBase58();\n\n let target: Commit | null;\n if (commitId === \"latest\") {\n target = await commitLayer.readLatestCommit(connection, owner, repoName);\n } else {\n const history = await commitLayer.readCommitHistory(connection, owner, repoName);\n target = history.find((c) => c.id === commitId) ?? null;\n }\n if (!target) {\n throw new Error(`commit not found: ${commitId} in ${owner}/${repoName}`);\n }\n\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /**\n * Whole-repo snapshot download — convenience on top of checkout(\"latest\")\n * but reading from someone else's `owner`. We do not require signer to\n * equal owner for reads.\n */\n async clone(\n repoName: string,\n owner: string,\n sink: (path: string, content: string) => Promise<void>,\n ): Promise<Commit> {\n const { connection } = this.cfg;\n const target = await commitLayer.readLatestCommit(connection, owner, repoName);\n if (!target) {\n throw new Error(`no commits in ${owner}/${repoName}`);\n }\n const tree = await storage.loadTree(target.treeTxId);\n for (const [path, entry] of Object.entries(tree)) {\n const content = await storage.loadBlob(entry.txId);\n await sink(path, content);\n }\n return target;\n }\n\n /** Commit history for a repo. */\n async log(\n owner: string,\n repoName: string,\n options?: { limit?: number; before?: string },\n ): Promise<Commit[]> {\n return commitLayer.readCommitHistory(\n this.cfg.connection,\n owner,\n repoName,\n options,\n );\n }\n\n /**\n * Compare a current directory snapshot to the latest commit's tree.\n */\n async status(\n owner: string,\n repoName: string,\n scan: Record<string, string>,\n ): Promise<{ added: string[]; modified: string[]; unchanged: string[] }> {\n const latest = await commitLayer.readLatestCommit(\n this.cfg.connection,\n owner,\n repoName,\n );\n const tree: FileTree = latest ? await storage.loadTree(latest.treeTxId) : {};\n\n const added: string[] = [];\n const modified: string[] = [];\n const unchanged: string[] = [];\n for (const [path, content] of Object.entries(scan)) {\n const prior = tree[path];\n if (!prior) {\n added.push(path);\n continue;\n }\n const hash = await sha256Hex(content);\n (prior.hash === hash ? unchanged : modified).push(path);\n }\n return { added, modified, unchanged };\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iqlabs-official/git-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Embed on-chain Git into your Solana dApp. Browser SDK for letting users browse, create, and commit to repositories from your website, with a Node entry for tooling.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"solana",
|