@tiinex/app 0.1.1
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/LICENSE +201 -0
- package/NOTICE +31 -0
- package/README.md +26 -0
- package/docs/CONSUMER-CONTRACT.md +65 -0
- package/package.json +94 -0
- package/src/acceptance/knownScenarios.case.mjs +48 -0
- package/src/acceptance/knownScenarios.js +89 -0
- package/src/acceptance/postV423CanonicalTransitionProductVerticalSlice.case.mjs +774 -0
- package/src/acceptance/postV437M0ETimePortalProductParityClosure.case.mjs +51 -0
- package/src/acceptance/postV447M0FGithubSocialPublicationProductIntegration.case.mjs +33 -0
- package/src/acceptance/postV450M0ProductAcceptanceHardening.case.mjs +82 -0
- package/src/acceptance/postV470SchemaReadingContractMaterializationIdentitySourceCoalescingAuthorityCorrection.case.mjs +236 -0
- package/src/acceptance/recoveryAcceptance.case.mjs +196 -0
- package/src/adapters/adapter.definition.schema.json +33 -0
- package/src/adapters/archive/archive.adapter.case.mjs +212 -0
- package/src/adapters/export/export.adapter.js +79 -0
- package/src/adapters/git-native/git-native.adapter.js +43 -0
- package/src/adapters/github/github.adapter.case.mjs +843 -0
- package/src/adapters/github/github.adapter.js +395 -0
- package/src/adapters/github/github.issueEmbedded.js +262 -0
- package/src/adapters/github/github.issueMirror.js +309 -0
- package/src/adapters/github/github.issueSnapshot.js +475 -0
- package/src/adapters/github/github.issueSurface.js +188 -0
- package/src/adapters/github/github.issueTransport.js +237 -0
- package/src/adapters/github/github.publicationRead.js +24 -0
- package/src/adapters/github/github.readTransport.js +18 -0
- package/src/adapters/github/github.repoDiscovery.js +222 -0
- package/src/adapters/github/github.repoMirror.js +255 -0
- package/src/adapters/github/github.repoProxy.js +276 -0
- package/src/adapters/github/github.repoSurface.js +60 -0
- package/src/adapters/github/github.transportDiagnostics.js +68 -0
- package/src/adapters/local/local.adapter.case.mjs +72 -0
- package/src/adapters/local/local.adapter.js +188 -0
- package/src/adapters/registry.js +35 -0
- package/src/adapters/static/static.adapter.js +84 -0
- package/src/app/App.js +1 -0
- package/src/app/TiinexApp.jsx +822 -0
- package/src/app/appShell.views.jsx +58 -0
- package/src/app/artifactAuthoringCapability.js +49 -0
- package/src/app/boot/bootApp.js +19 -0
- package/src/app/boot/environment.js +1 -0
- package/src/app/canonicalAuthoringValues.js +4 -0
- package/src/app/canonicalCreationProductController.js +57 -0
- package/src/app/canonicalCreationProductSettlement.case.mjs +10 -0
- package/src/app/canonicalCreationProductSettlement.js +11 -0
- package/src/app/canonicalReferenceLocalCreateCommand.js +210 -0
- package/src/app/canonicalReferenceTargets.js +73 -0
- package/src/app/canonicalTransitionLocalCreateCommand.js +217 -0
- package/src/app/defaultWorkspaceStart.case.mjs +39 -0
- package/src/app/defaultWorkspaceStart.js +65 -0
- package/src/app/defaultWorkspaceStartCommand.js +55 -0
- package/src/app/durableLocalMutationPolicy.js +21 -0
- package/src/app/githubMaterializationSummary.js +69 -0
- package/src/app/githubProgress.js +25 -0
- package/src/app/githubSourceInput.js +47 -0
- package/src/app/githubSourceMaterializationCommand.js +97 -0
- package/src/app/githubSourceOperation.js +226 -0
- package/src/app/governanceDialogData.js +27 -0
- package/src/app/handoffPackageImportCommand.js +182 -0
- package/src/app/historyAuthority.js +53 -0
- package/src/app/initialWorkspaceBootstrapOperation.js +105 -0
- package/src/app/lineageCommand.js +118 -0
- package/src/app/lineageSourceRecovery.js +339 -0
- package/src/app/localDraftMutationCommand.js +174 -0
- package/src/app/localMaterialCommand.js +124 -0
- package/src/app/nodeGraphVerse.case.mjs +111 -0
- package/src/app/persistenceOwnership.js +92 -0
- package/src/app/productStateBoundary.js +7 -0
- package/src/app/providers/AppProviders.js +1 -0
- package/src/app/providers/LocaleProvider.js +1 -0
- package/src/app/providers/SchemaRegistryProvider.js +1 -0
- package/src/app/providers/WorkspaceProvider.js +1 -0
- package/src/app/publicTarget.js +251 -0
- package/src/app/publicTargetRestoreCommand.js +149 -0
- package/src/app/recordUi.cache.js +35 -0
- package/src/app/recordUi.js +11 -0
- package/src/app/routes/RouteSurface.js +1 -0
- package/src/app/routes/routes.js +1 -0
- package/src/app/runtimeState.js +54 -0
- package/src/app/schemaCurrentDeclaration.js +69 -0
- package/src/app/schemaFactoryLocalCreateCommand.js +101 -0
- package/src/app/schemaFactoryViewerProof.case.mjs +134 -0
- package/src/app/schemaFactoryViewerProof.js +20 -0
- package/src/app/schemaNavigationCommand.js +285 -0
- package/src/app/schemaNavigationMaterialization.js +171 -0
- package/src/app/schemaNavigationRuntimeCatalog.js +25 -0
- package/src/app/schemaNavigationState.js +66 -0
- package/src/app/schemaReadingContractQualification.js +91 -0
- package/src/app/schemaSourceRecovery.js +373 -0
- package/src/app/scrollPersistence.js +30 -0
- package/src/app/semanticOperationHistory.js +18 -0
- package/src/app/shareActionCommand.js +55 -0
- package/src/app/shareProjection.js +190 -0
- package/src/app/shell/AppShell.js +1 -0
- package/src/app/shell/Header.js +1 -0
- package/src/app/shell/Navigation.js +1 -0
- package/src/app/shell/SurfaceSwitcher.js +1 -0
- package/src/app/sourceTransportRefresh.js +176 -0
- package/src/app/startupOwnership.js +40 -0
- package/src/app/startupPresentation.case.mjs +34 -0
- package/src/app/startupPresentation.js +33 -0
- package/src/app/startupPresentation.views.jsx +21 -0
- package/src/app/startupRenderPhase.js +9 -0
- package/src/app/startupWorkspaceCommand.js +62 -0
- package/src/app/startupWorkspaceConfigApplication.js +49 -0
- package/src/app/statePersistenceScheduler.js +106 -0
- package/src/app/tiinexAppConfigPlan.js +108 -0
- package/src/app/tiinexAppConfigSource.js +349 -0
- package/src/app/tiinexAppStartupSource.js +79 -0
- package/src/app/tiinexHostedWorkspaceConventions.js +572 -0
- package/src/app/timePortalHistoricalRead.js +114 -0
- package/src/app/timePortalSnapshotResolution.js +53 -0
- package/src/app/urlMaterialCommand.js +24 -0
- package/src/app/useCanonicalPlacementSelectionOptions.js +9 -0
- package/src/app/useCanonicalReferenceSelectionOptions.js +8 -0
- package/src/app/useLocalMaterialIntake.js +68 -0
- package/src/app/useTimePortalProductController.jsx +111 -0
- package/src/app/useWorkspaceEntrypointIntake.js +77 -0
- package/src/app/useWorkspaceSelectionProductController.jsx +21 -0
- package/src/app/viewState.js +35 -0
- package/src/app/viewport.js +21 -0
- package/src/app/visualDormancy.js +286 -0
- package/src/app/workspaceContinuityNotices.js +32 -0
- package/src/app/workspaceDisplayCounts.js +9 -0
- package/src/app/workspaceEntrypointIntakeCommand.js +87 -0
- package/src/app/workspaceEntrypointProductRouting.case.mjs +20 -0
- package/src/app/workspaceExportCommand.js +21 -0
- package/src/app/workspaceExportDownload.js +23 -0
- package/src/app/workspaceExportExecutionCommand.js +10 -0
- package/src/app/workspaceGithubPublication.js +237 -0
- package/src/app/workspaceHandoffExport.js +56 -0
- package/src/app/workspaceHomeTarget.js +9 -0
- package/src/app/workspaceMulticolumn.js +113 -0
- package/src/app/workspaceNavigationParity.case.mjs +110 -0
- package/src/app/workspaceOpenProgress.js +26 -0
- package/src/app/workspacePlacementOptions.js +46 -0
- package/src/app/workspaceRecordActions.js +158 -0
- package/src/app/workspaceScopedInteraction.js +68 -0
- package/src/app/workspaceSelectionOriginContext.js +29 -0
- package/src/app/workspaceSelectionSession.js +54 -0
- package/src/app/workspaceSourceCloseCommand.js +25 -0
- package/src/app/workspaceSpineProductRouting.case.mjs +22 -0
- package/src/app/workspaceStartupTransition.js +32 -0
- package/src/app/workspaceWindow.js +85 -0
- package/src/artifacts/artifact.fixture.js +1 -0
- package/src/artifacts/artifact.identity.js +6 -0
- package/src/artifacts/artifact.identityAggregation.js +218 -0
- package/src/artifacts/artifact.model.js +2 -0
- package/src/artifacts/artifact.parse.case.mjs +196 -0
- package/src/artifacts/artifact.participantIndex.js +189 -0
- package/src/artifacts/fixtures/evidence.trace.md +50 -0
- package/src/artifacts/fixtures/topic.trace.md +33 -0
- package/src/artifacts/fixtures/unknown-schema.trace.md +25 -0
- package/src/audit/audit.findings.js +1 -0
- package/src/audit/audit.model.js +2 -0
- package/src/audit/audit.plan.js +13 -0
- package/src/audit/audit.report.js +12 -0
- package/src/audit/audit.severity.js +1 -0
- package/src/audit/audit.traverse.js +119 -0
- package/src/audit/integrity/auditIntegrity.checks.js +1 -0
- package/src/audit/integrity/auditIntegrity.compare.js +1 -0
- package/src/audit/lineage/auditLineage.load.js +1 -0
- package/src/audit/lineage/auditLineage.plan.js +1 -0
- package/src/audit/lineage/auditLineage.resolve.js +12 -0
- package/src/audit/sources/auditSources.boundaries.js +1 -0
- package/src/audit/sources/auditSources.checks.js +1 -0
- package/src/audit/validation/auditValidation.recheck.js +1 -0
- package/src/audit/validation/auditValidation.run.js +1 -0
- package/src/build.identity.js +15 -0
- package/src/commands/command.vocabulary.js +14 -0
- package/src/conformance/conformance.fixtures.js +151 -0
- package/src/conformance/conformance.run.js +238 -0
- package/src/data/applicationDataStore.js +23 -0
- package/src/data/companionAccess.js +35 -0
- package/src/diagnostics/events.js +1 -0
- package/src/diagnostics/invariant.js +1 -0
- package/src/diagnostics/schemaBindingChecks.js +1 -0
- package/src/diagnostics/schemaModuleChecks.js +1 -0
- package/src/diagnostics/sourceBoundaryChecks.js +5 -0
- package/src/diagnostics/sourceTransport.report.js +190 -0
- package/src/discovery/discovery.controls.js +7 -0
- package/src/export/export.plan.js +182 -0
- package/src/export/handoff.plan.js +50 -0
- package/src/export/package.builder.case.mjs +97 -0
- package/src/export/package.controlTopology.case.mjs +130 -0
- package/src/export/package.zip.case.mjs +33 -0
- package/src/graph/artifactGraph.model.js +344 -0
- package/src/graph/artifactGraph.project.js +139 -0
- package/src/graph/artifactGraph.worker.js +10 -0
- package/src/i18n/i18n.keys.js +1 -0
- package/src/i18n/i18n.provider.js +1 -0
- package/src/i18n/i18n.resolve.js +5 -0
- package/src/i18n/locales/en/common.json +4 -0
- package/src/i18n/locales/en/schemas/evidence.json +3 -0
- package/src/i18n/locales/en/schemas/presentation-surface.json +3 -0
- package/src/i18n/locales/en/schemas/preservation.json +3 -0
- package/src/i18n/locales/en/schemas/root.json +3 -0
- package/src/i18n/locales/en/schemas/schema-module.json +3 -0
- package/src/i18n/locales/en/schemas/topic.json +3 -0
- package/src/i18n/locales/en/surfaces/audit.json +3 -0
- package/src/i18n/locales/en/surfaces/create.json +3 -0
- package/src/i18n/locales/en/surfaces/detail.json +3 -0
- package/src/i18n/locales/en/surfaces/display-options.json +3 -0
- package/src/i18n/locales/en/surfaces/edit.json +3 -0
- package/src/i18n/locales/en/surfaces/feed.json +3 -0
- package/src/i18n/locales/en/surfaces/lineage.json +3 -0
- package/src/i18n/locales/en/surfaces/preview.json +3 -0
- package/src/i18n/locales/en/surfaces/share.json +3 -0
- package/src/i18n/locales/en/surfaces/source-settings.json +3 -0
- package/src/i18n/locales/en/surfaces/tree.json +3 -0
- package/src/i18n/locales/sv/common.json +4 -0
- package/src/i18n/locales/sv/schemas/evidence.json +3 -0
- package/src/i18n/locales/sv/schemas/presentation-surface.json +3 -0
- package/src/i18n/locales/sv/schemas/preservation.json +3 -0
- package/src/i18n/locales/sv/schemas/root.json +3 -0
- package/src/i18n/locales/sv/schemas/schema-module.json +3 -0
- package/src/i18n/locales/sv/schemas/topic.json +3 -0
- package/src/i18n/locales/sv/surfaces/audit.json +3 -0
- package/src/i18n/locales/sv/surfaces/create.json +3 -0
- package/src/i18n/locales/sv/surfaces/detail.json +3 -0
- package/src/i18n/locales/sv/surfaces/display-options.json +3 -0
- package/src/i18n/locales/sv/surfaces/edit.json +3 -0
- package/src/i18n/locales/sv/surfaces/feed.json +3 -0
- package/src/i18n/locales/sv/surfaces/lineage.json +3 -0
- package/src/i18n/locales/sv/surfaces/preview.json +3 -0
- package/src/i18n/locales/sv/surfaces/share.json +3 -0
- package/src/i18n/locales/sv/surfaces/source-settings.json +3 -0
- package/src/i18n/locales/sv/surfaces/tree.json +3 -0
- package/src/index.js +10 -0
- package/src/integrity/integrity.testFixture.js +9 -0
- package/src/interaction/interaction.spine.js +12 -0
- package/src/lineage/lineage.audit.js +1 -0
- package/src/lineage/lineage.edges.js +1 -0
- package/src/main.js +319 -0
- package/src/main.jsx +34 -0
- package/src/multiverse/multiverse.model.js +10 -0
- package/src/parity/poc.m1ParityScenarios.js +42 -0
- package/src/parity/poc.parityLedger.js +261 -0
- package/src/publication/publication.githubSocialContract.case.mjs +247 -0
- package/src/publication/publication.preflight.case.mjs +64 -0
- package/src/react/TiinexApplication.jsx +39 -0
- package/src/react/VerseHost.jsx +29 -0
- package/src/react/applicationDataContext.jsx +30 -0
- package/src/react.js +2 -0
- package/src/readers/modes/audit.reader.js +1 -0
- package/src/readers/modes/detail.reader.js +1 -0
- package/src/readers/modes/handover.reader.js +1 -0
- package/src/readers/modes/mobile.reader.js +1 -0
- package/src/readers/modes/print.reader.js +1 -0
- package/src/readers/modes/scan.reader.js +1 -0
- package/src/readers/reader.model.js +1 -0
- package/src/readers/reader.resolve.js +2 -0
- package/src/reingest/reingest.plan.case.mjs +65 -0
- package/src/runtime.js +54 -0
- package/src/schemas/README.md +99 -0
- package/src/schemas/coordination/handoff/tiinex.handoff.v1.schema.md +614 -0
- package/src/schemas/coordination/project/tiinex.project.v1.schema.md +311 -0
- package/src/schemas/core/decision/tiinex.decision.v1.schema.md +227 -0
- package/src/schemas/core/evidence/tiinex.evidence.v1.schema.md +318 -0
- package/src/schemas/core/feedback/tiinex.feedback.v1.schema.md +243 -0
- package/src/schemas/core/interpretation/.transitions/evidence-to-interpretation-transition-definition.trace.md +102 -0
- package/src/schemas/core/interpretation/tiinex.interpretation.v1.schema.md +308 -0
- package/src/schemas/core/preservation/tiinex.preservation.v1.schema.md +312 -0
- package/src/schemas/core/relation/.generation/reference-relation-generation-authority.trace.md +185 -0
- package/src/schemas/core/relation/.transitions/topic-references-task-transition-definition.trace.md +119 -0
- package/src/schemas/core/relation/tiinex.relation.v1.schema.md +240 -0
- package/src/schemas/core/relation/workspace/representation/tiinex.workspace.representation.v1.schema.md +550 -0
- package/src/schemas/core/signal/tiinex.signal.v1.schema.md +239 -0
- package/src/schemas/core/task/.transitions/create-task-transition-definition.trace.md +93 -0
- package/src/schemas/core/task/.transitions/topic-to-task-transition-definition.trace.md +102 -0
- package/src/schemas/core/task/task-semantic-package.trace.md +53 -0
- package/src/schemas/core/task/tiinex.task.v1-transitions.trace.md +33 -0
- package/src/schemas/core/task/tiinex.task.v1.authoring.js +65 -0
- package/src/schemas/core/task/tiinex.task.v1.schema.md +229 -0
- package/src/schemas/core/topic/.transitions/create-topic-transition-definition.trace.md +93 -0
- package/src/schemas/core/topic/tiinex.topic.v1-transitions.trace.md +33 -0
- package/src/schemas/core/topic/tiinex.topic.v1.schema.md +197 -0
- package/src/schemas/core/topic/topic-semantic-package.trace.md +53 -0
- package/src/schemas/discovery/finding/tiinex.discovery.finding.v1.schema.md +240 -0
- package/src/schemas/discovery/tiinex.discovery.v1.schema.md +294 -0
- package/src/schemas/manifest.json +367 -0
- package/src/schemas/origins.js +46 -0
- package/src/schemas/party/organization/tiinex.party.organization.v1.schema.md +290 -0
- package/src/schemas/party/role/tiinex.party.role.v1.schema.md +298 -0
- package/src/schemas/party/tiinex.party.v1.schema.js +14 -0
- package/src/schemas/party/tiinex.party.v1.schema.json +23 -0
- package/src/schemas/party/tiinex.party.v1.schema.md +311 -0
- package/src/schemas/party/tiinex.party.v1.schema.runtime.json +2033 -0
- package/src/schemas/party/tiinex.party.v1.schema.source.js +9 -0
- package/src/schemas/presentation/surface/tiinex.presentation.surface.v1.schema.md +476 -0
- package/src/schemas/reduction/tiinex.reduction.v1.schema.md +248 -0
- package/src/schemas/schema/module/tiinex.schema.module.v1.schema.md +465 -0
- package/src/schemas/schema.companionContract.case.mjs +259 -0
- package/src/schemas/schema.factory.case.mjs +256 -0
- package/src/schemas/schema.factory.viewerProjection.js +183 -0
- package/src/schemas/schemaMarkdownCatalog.js +19 -0
- package/src/schemas/tiinex.root.v1.schema.md +865 -0
- package/src/schemas/validation/finding/tiinex.validation.finding.v1.schema.md +309 -0
- package/src/schemas/validation/method/tiinex.validation.method.v1.schema.md +280 -0
- package/src/schemas/validation/report/tiinex.validation.report.v1.schema.md +300 -0
- package/src/schemas/workspace/tiinex.workspace.v1.schema.md +486 -0
- package/src/schemas/workspace/workspace.add.views.jsx +327 -0
- package/src/schemas/workspace/workspace.audit.views.jsx +160 -0
- package/src/schemas/workspace/workspace.auditBadge.views.jsx +98 -0
- package/src/schemas/workspace/workspace.canonicalReferenceDialog.views.jsx +63 -0
- package/src/schemas/workspace/workspace.canonicalTaskDialog.views.jsx +169 -0
- package/src/schemas/workspace/workspace.cardActions.js +9 -0
- package/src/schemas/workspace/workspace.cards.views.jsx +146 -0
- package/src/schemas/workspace/workspace.chrome.views.jsx +299 -0
- package/src/schemas/workspace/workspace.continuationDialog.views.jsx +80 -0
- package/src/schemas/workspace/workspace.discovery.views.jsx +63 -0
- package/src/schemas/workspace/workspace.discoveryRenderWindow.js +56 -0
- package/src/schemas/workspace/workspace.displayOptions.views.jsx +114 -0
- package/src/schemas/workspace/workspace.entrypointChoice.views.jsx +30 -0
- package/src/schemas/workspace/workspace.exportDialog.controller.jsx +40 -0
- package/src/schemas/workspace/workspace.exportDialog.views.jsx +142 -0
- package/src/schemas/workspace/workspace.governance.views.jsx +45 -0
- package/src/schemas/workspace/workspace.graph.views.jsx +134 -0
- package/src/schemas/workspace/workspace.lineage.views.jsx +307 -0
- package/src/schemas/workspace/workspace.read.views.jsx +105 -0
- package/src/schemas/workspace/workspace.recordActionButton.views.jsx +35 -0
- package/src/schemas/workspace/workspace.recordDialogs.views.jsx +257 -0
- package/src/schemas/workspace/workspace.selection.views.jsx +14 -0
- package/src/schemas/workspace/workspace.sourceReceipt.views.jsx +40 -0
- package/src/schemas/workspace/workspace.timePortal.views.jsx +56 -0
- package/src/schemas/workspace/workspace.tree.views.jsx +107 -0
- package/src/schemas/workspace/workspace.viewFormatting.js +172 -0
- package/src/schemas/workspace/workspace.views.jsx +210 -0
- package/src/source-settings/sourceSettings.model.js +7 -0
- package/src/sources/draft/draft.source.js +1 -0
- package/src/sources/github/github.loader.js +204 -0
- package/src/sources/github/github.permalink.js +1 -0
- package/src/sources/github/github.source.js +1 -0
- package/src/sources/github/github.transport.case.mjs +237 -0
- package/src/sources/github/github.transport.js +472 -0
- package/src/sources/local/local.source.js +1 -0
- package/src/sources/source.assetReferences.js +109 -0
- package/src/sources/source.boundaries.js +24 -0
- package/src/sources/source.identity.js +56 -0
- package/src/sources/source.model.case.mjs +26 -0
- package/src/sources/source.model.js +56 -0
- package/src/sources/source.presenter.js +66 -0
- package/src/sources/source.resolve.js +11 -0
- package/src/sources/static/static.source.js +1 -0
- package/src/sources/transport.policy.case.mjs +39 -0
- package/src/sources/transport.policy.js +81 -0
- package/src/storage/storage.policy.js +79 -0
- package/src/styles/app.css +8699 -0
- package/src/styles/responsive.css +10 -0
- package/src/styles/theme.css +4 -0
- package/src/styles/tokens.css +30 -0
- package/src/surfaces/audit/AuditSurface.js +1 -0
- package/src/surfaces/audit/audit.model.js +1 -0
- package/src/surfaces/audit/audit.render.js +1 -0
- package/src/surfaces/contracts.js +45 -0
- package/src/surfaces/create/CreateSurface.js +1 -0
- package/src/surfaces/create/create.model.js +1 -0
- package/src/surfaces/create/create.render.js +1 -0
- package/src/surfaces/detail/DetailSurface.js +1 -0
- package/src/surfaces/detail/detail.model.js +1 -0
- package/src/surfaces/detail/detail.render.js +1 -0
- package/src/surfaces/display-options/DisplayOptionsSurface.js +1 -0
- package/src/surfaces/display-options/displayOptions.model.js +1 -0
- package/src/surfaces/display-options/displayOptions.render.js +1 -0
- package/src/surfaces/edit/EditSurface.js +1 -0
- package/src/surfaces/edit/edit.model.js +1 -0
- package/src/surfaces/edit/edit.render.js +1 -0
- package/src/surfaces/feed/FeedSurface.js +1 -0
- package/src/surfaces/feed/feed.model.js +1 -0
- package/src/surfaces/feed/feed.render.js +1 -0
- package/src/surfaces/lineage/LineageSurface.js +1 -0
- package/src/surfaces/lineage/lineage.model.js +1 -0
- package/src/surfaces/lineage/lineage.render.js +1 -0
- package/src/surfaces/preview/PreviewSurface.js +1 -0
- package/src/surfaces/preview/preview.model.js +1 -0
- package/src/surfaces/preview/preview.render.js +1 -0
- package/src/surfaces/registry.js +58 -0
- package/src/surfaces/share/ShareSurface.js +1 -0
- package/src/surfaces/share/share.model.js +1 -0
- package/src/surfaces/share/share.render.js +1 -0
- package/src/surfaces/source-settings/SourceSettingsSurface.js +1 -0
- package/src/surfaces/source-settings/sourceSettings.model.js +1 -0
- package/src/surfaces/source-settings/sourceSettings.render.js +1 -0
- package/src/surfaces/surfaceContext.js +1 -0
- package/src/surfaces/tree/TreeSurface.js +1 -0
- package/src/surfaces/tree/tree.model.js +1 -0
- package/src/surfaces/tree/tree.render.js +1 -0
- package/src/tooling/portable/adapters/cli/cli.common-output.case.mjs +153 -0
- package/src/tooling/portable/adapters/cli/cli.handoff-sibling-allocation.case.mjs +52 -0
- package/src/tooling/portable/adapters/cli/cli.run.case.mjs +372 -0
- package/src/tooling/portable/adapters/cli/cli.summaryProjection.case.mjs +46 -0
- package/src/tooling/portable/audit/audit.capability.case.mjs +45 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/collaboration/dogfood/001-tiinex-development-dogfood-collaboration-model.trace.fixture.txt +37 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/handoff/tooling/001-v480-tooling-workflow-schema-enablement-handoff.trace.fixture.txt +108 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/handoff/tooling/002-v481-tooling-recipient-relative-handoff-material-closure-planner-foundation-handoff.trace.fixture.txt +130 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/loom/role/001-loom-role.trace.fixture.txt +61 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/sigma/role/001-1-sigma-role.trace.fixture.txt +61 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-1-1-1-1-1-site-tooling-v474-loaded-parent-identity-evidence-closure-result.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-1-1-1-1-site-tooling-v474-loaded-parent-identity-evidence-closure.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-1-1-1-site-tooling-v473-portable-parent-authority-coherence-metadata-fidelity-closure-result.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-1-1-site-tooling-v473-portable-parent-authority-coherence-metadata-fidelity-closure.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-1-site-tooling-v472-portable-exact-authoring-fidelity-closure-result.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-site-tooling-v472-portable-exact-authoring-fidelity-closure.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-site-tooling-v471-portable-lineage-authoring-closure-result.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-site-tooling-v471-portable-lineage-authoring-closure.trace.fixture.txt +39 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/002-1-v475-canonical-artifact-envelope-reference-integrity-validation.trace.fixture.txt +43 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/003-1-v476-canonical-authority-binding-integrity-method-lineage-alloca.trace.fixture.txt +42 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/027-handoff-package-workspace-archive-and-control-plane-minimality-audit.trace.fixture.txt +56 -0
- package/src/tooling/portable/fixtures/legacyArtifactFixtures.mjs +34 -0
- package/src/tooling/portable/grounding/grounding.capsule.case.mjs +65 -0
- package/src/tooling/portable/grounding/grounding.readiness.case.mjs +543 -0
- package/src/tooling/portable/grounding/grounding.semanticIntegration.case.mjs +131 -0
- package/src/tooling/portable/handoff/boundedWorkspaceRepresentation.case.mjs +103 -0
- package/src/tooling/portable/handoff/carrierLineage.fixedWidth.case.mjs +97 -0
- package/src/tooling/portable/handoff/coldStartQualification.case.mjs +293 -0
- package/src/tooling/portable/handoff/fixtures/027-invalid-audit-result.fixture.txt +192 -0
- package/src/tooling/portable/handoff/fixtures/027-invalid-return-handoff.fixture.txt +92 -0
- package/src/tooling/portable/handoff/fixtures/cold-start-qualification.v1.examples.json +60 -0
- package/src/tooling/portable/handoff/handoff.manufacture.case.mjs +293 -0
- package/src/tooling/portable/handoff/materialClosure.case.mjs +727 -0
- package/src/tooling/portable/handoff/multiRootManufacture.case.mjs +615 -0
- package/src/tooling/portable/handoff/operatorBridgeCore.case.mjs +205 -0
- package/src/tooling/portable/handoff/workspaceLandingPlan.case.mjs +143 -0
- package/src/tooling/portable/input/node.input.case.mjs +293 -0
- package/src/tooling/portable/lifecycle/lifecycle.readiness.case.mjs +179 -0
- package/src/tooling/portable/lineage/fixtures/lineage.integrity.repair-projection.v1.examples.json +605 -0
- package/src/tooling/portable/lineage/lineage.integrity.shared-capability.case.mjs +37 -0
- package/src/tooling/portable/lineage/publicationProviderReceipt.fixture.mjs +50 -0
- package/src/tooling/portable/package/fixtures/reference-relation-generation-authority.trace.md +84 -0
- package/src/tooling/portable/package/fixtures/tiinex.root.v1.package-authority.contract-fixture.md +64 -0
- package/src/tooling/portable/package/fixtures/tiinex.schema.generation.v1.generation-binding.contract-fixture.md +140 -0
- package/src/tooling/portable/package/fixtures/tiinex.schema.transition.companion.v1.contract-fixture.md +73 -0
- package/src/tooling/portable/package/fixtures/tiinex.semantic.package.v1.contract-fixture.md +147 -0
- package/src/tooling/portable/package/fixtures/tiinex.transition.definition.v1.package-contract-fixture.md +77 -0
- package/src/tooling/portable/providers/schema.providers.genericity.case.mjs +31 -0
- package/src/tooling/portable/reduction/reduction.preflight.case.mjs +298 -0
- package/src/tooling/portable/schema/fixtures/evidence-preservation-body.inheritance-record-fixture.md +65 -0
- package/src/tooling/portable/schema/fixtures/tiinex.portal.time.v1.contract-fixture.md +294 -0
- package/src/tooling/portable/schema/fixtures/tiinex.root.v1.contract-fixture.md +584 -0
- package/src/tooling/portable/schema/fixtures/tiinex.root.v1.machine-shape.contract-fixture.md +44 -0
- package/src/tooling/portable/schema/fixtures/tiinex.root.v1.ordinary-target.contract-fixture.md +630 -0
- package/src/tooling/portable/schema/fixtures/tiinex.schema.contract.v1.repeated-record.contract-fixture.md +69 -0
- package/src/tooling/portable/schema/fixtures/tiinex.schema.generation.v1.repeated-record.contract-fixture.md +98 -0
- package/src/tooling/portable/schema/fixtures/tiinex.schema.inheritance.v1.repeated-record.contract-fixture.md +58 -0
- package/src/tooling/portable/schema/fixtures/tiinex.task.v1.contract-fixture.md +165 -0
- package/src/tooling/portable/schema/fixtures/tiinex.transition.definition.v1.contract-fixture.md +744 -0
- package/src/tooling/portable/schema/fixtures/tiinex.transition.definition.v1.field-domain.contract-fixture.md +319 -0
- package/src/transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.relation.v1.schema.md +240 -0
- package/src/transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.schema.contract.v1.schema.md +391 -0
- package/src/transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.schema.generation.v1.schema.md +379 -0
- package/src/transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.schema.transition.companion.v1.schema.md +265 -0
- package/src/transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.semantic.package.v1.schema.md +402 -0
- package/src/transitions/canonical-schema-cache/52ecdea0a75893882ce282214d155f70e1309c2a/tiinex.topic.v1.schema.md +197 -0
- package/src/transitions/canonical-schema-cache/d69b8ff55a56b8cb9282b8684db6a938a4435b94/tiinex.root.v1.schema.md +797 -0
- package/src/transitions/canonical-schema-cache/d69b8ff55a56b8cb9282b8684db6a938a4435b94/tiinex.transition.definition.v1.schema.md +1062 -0
- package/src/transitions/canonicalTransition.packageContracts.js +22 -0
- package/src/transitions/canonicalTransition.productDefaults.js +105 -0
- package/src/transitions/canonicalTransition.schemaCache.case.mjs +45 -0
- package/src/transitions/canonicalTransition.schemaCache.js +81 -0
- package/src/transitions/canonicalTransition.semanticPackage.js +146 -0
- package/src/transitions/record.transitions.case.mjs +88 -0
- package/src/transitions/transition.authorityRepresentation.js +55 -0
- package/src/transitions/transition.availabilityPlanner.js +376 -0
- package/src/transitions/transition.availabilitySemantics.js +150 -0
- package/src/transitions/transition.definitionReadProjection.js +107 -0
- package/src/transitions/transition.definitionRegistry.js +355 -0
- package/src/transitions/transition.definitionRegistryAggregation.js +21 -0
- package/src/transitions/transition.definitions.js +46 -0
- package/src/transitions/transition.explicitGenerationPlanner.js +58 -0
- package/src/transitions/transition.generationInputPlanner.js +19 -0
- package/src/transitions/transition.interpretationMaterializer.js +2 -0
- package/src/transitions/transition.invocationBindingPacket.js +52 -0
- package/src/transitions/transition.invocationBindingPlanner.js +467 -0
- package/src/transitions/transition.legacyShorthand.js +133 -0
- package/src/transitions/transition.localArtifactMaterializers.js +16 -0
- package/src/transitions/transition.localCreateCapability.js +111 -0
- package/src/transitions/transition.outputMaterializationPlanner.js +336 -0
- package/src/transitions/transition.parentReference.js +90 -0
- package/src/transitions/transition.presentation.js +43 -0
- package/src/transitions/transition.productPreparation.js +377 -0
- package/src/transitions/transition.productPresentation.browser.js +28 -0
- package/src/transitions/transition.productPresentation.js +55 -0
- package/src/transitions/transition.referenceCreateCapability.js +119 -0
- package/src/transitions/transition.referenceTarget.js +50 -0
- package/src/transitions/transition.relationMaterializer.js +2 -0
- package/src/transitions/transition.resultSemantics.js +453 -0
- package/src/transitions/transition.taskMaterializer.js +2 -0
- package/src/transitions/transition.topicMaterializer.js +2 -0
- package/src/transitions/transition.validate.case.mjs +58 -0
- package/src/ui/cards/ArtifactCard.js +1 -0
- package/src/ui/cards/SchemaCard.js +1 -0
- package/src/ui/cards/SourceCard.js +1 -0
- package/src/ui/dialog.presenter.js +24 -0
- package/src/ui/feedback/DegradedNotice.js +1 -0
- package/src/ui/feedback/EmptyState.js +1 -0
- package/src/ui/feedback/ValidationNotice.js +1 -0
- package/src/ui/forms/FormRenderer.js +1 -0
- package/src/ui/forms/fields/MarkdownField.js +1 -0
- package/src/ui/forms/fields/SelectField.js +1 -0
- package/src/ui/forms/fields/SourcePickerField.js +1 -0
- package/src/ui/forms/fields/TextField.js +1 -0
- package/src/ui/icon.paths.js +37 -0
- package/src/ui/layout/Panel.js +1 -0
- package/src/ui/layout/ResponsiveFrame.js +1 -0
- package/src/ui/layout/SplitView.js +1 -0
- package/src/ui/layout/Stack.js +1 -0
- package/src/ui/panels/DetailPanel.js +1 -0
- package/src/ui/panels/LineagePanel.js +1 -0
- package/src/ui/panels/SourcePanel.js +1 -0
- package/src/ui/primitives/Badge.js +1 -0
- package/src/ui/primitives/Badge.jsx +5 -0
- package/src/ui/primitives/Button.js +1 -0
- package/src/ui/primitives/Button.jsx +12 -0
- package/src/ui/primitives/Card.js +1 -0
- package/src/ui/primitives/Field.js +1 -0
- package/src/ui/primitives/Field.jsx +21 -0
- package/src/ui/primitives/Icon.jsx +90 -0
- package/src/ui/primitives/Modal.jsx +27 -0
- package/src/validation/i18n.js +37 -0
- package/src/validation/validateArtifact.case.mjs +41 -0
- package/src/verses/column/ColumnVerse.js +1 -0
- package/src/verses/column/column.model.js +6 -0
- package/src/verses/column/column.project.js +7 -0
- package/src/verses/context.js +26 -0
- package/src/verses/contracts.js +1 -0
- package/src/verses/hostRegistry.js +47 -0
- package/src/verses/registry.js +22 -0
- package/src/verses/universe.model.js +8 -0
- package/src/verses/universe.presenter.js +8 -0
- package/src/verses/universe.project.js +12 -0
- package/src/viewer.js +34 -0
- package/src/workspace.model.js +5 -0
- package/src/workspaces/workspace.actions.js +1 -0
- package/src/workspaces/workspace.artifactActions.js +56 -0
- package/src/workspaces/workspace.auditView.js +153 -0
- package/src/workspaces/workspace.candidates.js +149 -0
- package/src/workspaces/workspace.config.js +153 -0
- package/src/workspaces/workspace.configuredSource.js +68 -0
- package/src/workspaces/workspace.discoveryProgress.js +34 -0
- package/src/workspaces/workspace.discoveryView.js +386 -0
- package/src/workspaces/workspace.displayFilters.js +63 -0
- package/src/workspaces/workspace.displayOptions.js +58 -0
- package/src/workspaces/workspace.emptyStateCopy.js +24 -0
- package/src/workspaces/workspace.entrypointLifecycle.js +131 -0
- package/src/workspaces/workspace.entrypointViewIntent.js +83 -0
- package/src/workspaces/workspace.entrypoints.js +157 -0
- package/src/workspaces/workspace.feedSort.js +152 -0
- package/src/workspaces/workspace.import.js +212 -0
- package/src/workspaces/workspace.importConflicts.js +200 -0
- package/src/workspaces/workspace.importLifecycle.case.mjs +103 -0
- package/src/workspaces/workspace.lifecycle.case.mjs +353 -0
- package/src/workspaces/workspace.lifecycle.js +357 -0
- package/src/workspaces/workspace.lineageView.js +244 -0
- package/src/workspaces/workspace.localSourceLifecycle.js +91 -0
- package/src/workspaces/workspace.materialLedger.js +110 -0
- package/src/workspaces/workspace.materialReconciliation.case.mjs +164 -0
- package/src/workspaces/workspace.materialReconciliation.js +390 -0
- package/src/workspaces/workspace.memberIdentity.js +140 -0
- package/src/workspaces/workspace.model.js +2 -0
- package/src/workspaces/workspace.navigation.js +20 -0
- package/src/workspaces/workspace.openSemantics.js +33 -0
- package/src/workspaces/workspace.pathTree.js +132 -0
- package/src/workspaces/workspace.persistence.case.mjs +271 -0
- package/src/workspaces/workspace.persistence.js +469 -0
- package/src/workspaces/workspace.persistenceCache.js +41 -0
- package/src/workspaces/workspace.persistenceClear.js +45 -0
- package/src/workspaces/workspace.persistencePresentation.js +93 -0
- package/src/workspaces/workspace.persistenceRecovery.js +147 -0
- package/src/workspaces/workspace.persistenceRouteCache.js +82 -0
- package/src/workspaces/workspace.publicationReceipts.case.mjs +36 -0
- package/src/workspaces/workspace.publicationReceipts.js +56 -0
- package/src/workspaces/workspace.recoverabilityView.js +264 -0
- package/src/workspaces/workspace.route.js +432 -0
- package/src/workspaces/workspace.runtimeCanonical.js +18 -0
- package/src/workspaces/workspace.sourceMaterial.js +71 -0
- package/src/workspaces/workspace.sourceRecordPath.js +5 -0
- package/src/workspaces/workspace.sourceRecords.js +75 -0
- package/src/workspaces/workspace.store.js +3 -0
- package/src/workspaces/workspace.summary.js +116 -0
- package/src/workspaces/workspace.timePortal.js +137 -0
|
@@ -0,0 +1,843 @@
|
|
|
1
|
+
import assert from 'assert';
|
|
2
|
+
import { discoverGithubMarkdownRefs, materializeGithubSource, resolveGithubSourceRef } from './github.adapter.js';
|
|
3
|
+
|
|
4
|
+
function makeFetch(map, called = []) {
|
|
5
|
+
return async function fetchImpl(url) {
|
|
6
|
+
called.push(url);
|
|
7
|
+
const hit = map[url];
|
|
8
|
+
if (!hit) return { ok: false, status: 404, statusText: 'Not Found', json: async () => ({}), text: async () => '' };
|
|
9
|
+
if (hit.ok === false) {
|
|
10
|
+
return {
|
|
11
|
+
ok: false,
|
|
12
|
+
status: hit.status || 500,
|
|
13
|
+
statusText: hit.statusText || 'Error',
|
|
14
|
+
json: async () => hit.json || {},
|
|
15
|
+
text: async () => hit.text || ''
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (hit.buffer != null) {
|
|
19
|
+
return {
|
|
20
|
+
ok: true,
|
|
21
|
+
status: 200,
|
|
22
|
+
statusText: 'OK',
|
|
23
|
+
arrayBuffer: async () => toArrayBuffer(hit.buffer),
|
|
24
|
+
json: async () => hit.json,
|
|
25
|
+
text: async () => hit.text || ''
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
ok: true,
|
|
30
|
+
status: 200,
|
|
31
|
+
statusText: 'OK',
|
|
32
|
+
json: async () => hit.json,
|
|
33
|
+
text: async () => hit.text
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const repo = 'owner/repo';
|
|
39
|
+
const repoApi = 'https://api.github.com/repos/owner/repo';
|
|
40
|
+
const treeApi = 'https://api.github.com/repos/owner/repo/git/trees/main?recursive=1';
|
|
41
|
+
const rawTopic = 'https://raw.githubusercontent.com/owner/repo/main/.topics/a.md';
|
|
42
|
+
const rawNested = 'https://raw.githubusercontent.com/owner/repo/main/.topics/nested/b.trace.md';
|
|
43
|
+
const issueApi = 'https://api.github.com/repos/owner/repo/issues/1';
|
|
44
|
+
const issueCommentsApi = 'https://api.github.com/repos/owner/repo/issues/1/comments?per_page=24';
|
|
45
|
+
const issueListApi = 'https://api.github.com/repos/owner/repo/issues?state=all&sort=updated&direction=desc&per_page=12';
|
|
46
|
+
|
|
47
|
+
const map = {
|
|
48
|
+
[repoApi]: { json: { default_branch: 'main' } },
|
|
49
|
+
[treeApi]: { json: { truncated: false, tree: [
|
|
50
|
+
{ type: 'blob', path: '.topics/a.md' },
|
|
51
|
+
{ type: 'blob', path: '.topics/nested/b.trace.md' },
|
|
52
|
+
{ type: 'blob', path: '.topics/image.png' },
|
|
53
|
+
{ type: 'blob', path: 'README.md' }
|
|
54
|
+
] } },
|
|
55
|
+
[rawTopic]: { text: '# A\n\n' },
|
|
56
|
+
[rawNested]: { text: '# B\n\nBody' },
|
|
57
|
+
[issueApi]: { json: { html_url: 'https://github.com/owner/repo/issues/1', number: 1, title: 'API issue', state: 'open', body: 'Issue body', user: { login: 'q' }, created_at: '2026-07-21T00:00:00.000Z', comments: 1 } },
|
|
58
|
+
[issueCommentsApi]: { json: [{ user: { login: 'reviewer' }, body: 'Comment body' }] },
|
|
59
|
+
[issueListApi]: { json: [{ html_url: 'https://github.com/owner/repo/issues/1', number: 1, title: 'API issue', state: 'open', body: 'Issue body', user: { login: 'q' }, created_at: '2026-07-21T00:00:00.000Z', comments: 0 }] }
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const called = [];
|
|
63
|
+
const fetchImpl = makeFetch(map, called);
|
|
64
|
+
const source = { id: 'github:owner/repo:.topics', repo, ref: '', rootPath: '.topics' };
|
|
65
|
+
|
|
66
|
+
const resolved = await resolveGithubSourceRef(source, { fetchImpl });
|
|
67
|
+
assert.equal(resolved.ref, 'main', 'blank ref should resolve public default branch');
|
|
68
|
+
|
|
69
|
+
const discovered = await discoverGithubMarkdownRefs(source, { fetchImpl });
|
|
70
|
+
assert.deepEqual(discovered.refs, ['.topics/a.md', '.topics/nested/b.trace.md'], 'repo discovery should return only markdown under roots');
|
|
71
|
+
assert.equal(discovered.ref, 'main', 'discovery should return resolved ref');
|
|
72
|
+
|
|
73
|
+
const progressEvents = [];
|
|
74
|
+
const materialized = await materializeGithubSource(source, { repoDiscovery: true, fileRefs: [] }, { fetchImpl, onProgress: (event) => progressEvents.push(event) });
|
|
75
|
+
assert.equal(materialized.okCount, 2, 'repo discovery should load discovered markdown files');
|
|
76
|
+
assert.equal(materialized.failCount, 0, 'repo discovery should not fail for mapped files');
|
|
77
|
+
assert.equal(materialized.records.length, 2, 'records should be materialized');
|
|
78
|
+
assert(materialized.records.every((record) => !record.source), 'adapter must not assign lifecycle source provenance');
|
|
79
|
+
assert.equal(materialized.diagnostics.resolvedRef, 'main', 'adapter result should expose resolved ref');
|
|
80
|
+
assert.equal(materialized.diagnostics.requests, 2, 'raw materialization should count fetch requests');
|
|
81
|
+
assert.equal(materialized.diagnostics.surfaces.repoFiles.discovered, 2, 'repo surface should report discovered markdown count');
|
|
82
|
+
assert.equal(materialized.diagnostics.surfaces.repoFiles.loaded, 2, 'repo surface should report loaded markdown count');
|
|
83
|
+
assert(progressEvents.some((event) => event.phase === 'repo-discovery' && /Found 2 Markdown/.test(event.label || '')), 'repo discovery should emit a visible found-count progress event');
|
|
84
|
+
assert(progressEvents.some((event) => event.phase === 'raw-file-load' && event.total === 2), 'raw loading should emit concrete N/M progress');
|
|
85
|
+
assert.equal(materialized.diagnostics.assetReferences.counts['referenced-unloaded'], 1, 'adapter should report referenced-but-unloaded source assets without fetching binaries');
|
|
86
|
+
assert(materialized.warnings.some((warning) => warning.code === 'github.asset.referenced-unloaded'), 'referenced source assets should be surfaced as honest non-fetch warnings');
|
|
87
|
+
|
|
88
|
+
const workspaceTreeApi = 'https://api.github.com/repos/owner/workspaces/git/trees/main?recursive=1';
|
|
89
|
+
const workspaceRepoApi = 'https://api.github.com/repos/owner/workspaces';
|
|
90
|
+
const rawWorkspace = 'https://raw.githubusercontent.com/owner/workspaces/main/.topics/news.workspace.md';
|
|
91
|
+
const rawOrdinary = 'https://raw.githubusercontent.com/owner/workspaces/main/.topics/ordinary.md';
|
|
92
|
+
const workspaceFetch = makeFetch({
|
|
93
|
+
[workspaceRepoApi]: { json: { default_branch: 'main' } },
|
|
94
|
+
[workspaceTreeApi]: { json: { truncated: false, tree: [
|
|
95
|
+
{ type: 'blob', path: '.topics/news.workspace.md' },
|
|
96
|
+
{ type: 'blob', path: '.topics/ordinary.md' }
|
|
97
|
+
] } },
|
|
98
|
+
[rawWorkspace]: { text: `# News
|
|
99
|
+
|
|
100
|
+
## Workspace Entrypoints
|
|
101
|
+
|
|
102
|
+
### News
|
|
103
|
+
|
|
104
|
+
- Repository: owner/news
|
|
105
|
+
` },
|
|
106
|
+
[rawOrdinary]: { text: '# Ordinary' }
|
|
107
|
+
});
|
|
108
|
+
const workspaceDiscoveryOnly = await materializeGithubSource(
|
|
109
|
+
{ id: 'github:owner/workspaces:.topics', repo: 'owner/workspaces', ref: '', rootPath: '.topics', workspaceMatch: '*.workspace.md' },
|
|
110
|
+
{ repoDiscovery: true, fileRefs: [], workspaceMatch: '*.workspace.md' },
|
|
111
|
+
{ fetchImpl: workspaceFetch, allowCache: false }
|
|
112
|
+
);
|
|
113
|
+
assert.equal(workspaceDiscoveryOnly.records.length, 1, 'workspace discovery should materialize only matched workspace config files');
|
|
114
|
+
assert.equal(workspaceDiscoveryOnly.records[0].sourceTarget.inputTarget, '.topics/news.workspace.md');
|
|
115
|
+
assert.equal(workspaceDiscoveryOnly.diagnostics.surfaces.repoFiles.discovered, 1, 'workspace discovery count should reflect matched workspace configs, not all markdown');
|
|
116
|
+
|
|
117
|
+
const limitedFetch = makeFetch({
|
|
118
|
+
[repoApi]: { json: { default_branch: 'main' } },
|
|
119
|
+
[treeApi]: { ok: false, status: 403, statusText: 'Forbidden', json: { message: 'API rate limit exceeded' } }
|
|
120
|
+
});
|
|
121
|
+
const limited = await materializeGithubSource(source, { repoDiscovery: true, fileRefs: [] }, { fetchImpl: limitedFetch, allowCache: false });
|
|
122
|
+
assert.equal(limited.okCount, 0, 'rate-limited repo discovery should not materialize records');
|
|
123
|
+
assert.equal(limited.failCount, 0, 'rate-limited repo discovery should be a warning, not a file failure');
|
|
124
|
+
assert.equal(limited.errors.length, 0, 'rate-limited repo discovery should not be a fatal adapter error');
|
|
125
|
+
assert(limited.warnings.some((warning) => warning.code === 'github.repo.discovery.rate-limited-or-forbidden'), '403 discovery should produce a precise warning');
|
|
126
|
+
assert.equal(limited.diagnostics.discoveryUnavailable, true, 'diagnostics should preserve discovery-unavailable state');
|
|
127
|
+
assert(limited.diagnostics.transportEvents.some((event) => event.code === 'github.repo.discovery.rate-limited-or-forbidden'), '403 discovery should be captured as a transport event');
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
const budgetCalled = [];
|
|
131
|
+
const budgetLimited = await materializeGithubSource(
|
|
132
|
+
{ id: 'github:owner/repo', repo, ref: 'main', rootPath: '.topics' },
|
|
133
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md', '.topics/nested/b.trace.md'] },
|
|
134
|
+
{ fetchImpl: makeFetch(map, budgetCalled), maxRequestsPerOperation: 1, allowCache: false }
|
|
135
|
+
);
|
|
136
|
+
assert.equal(budgetLimited.records.length, 0, 'transport budget should block raw materialization before fetching');
|
|
137
|
+
assert.equal(budgetLimited.okCount, 0, 'budget-blocked materialization should not claim loaded records');
|
|
138
|
+
assert.equal(budgetLimited.failCount, 0, 'budget-blocked transport policy is degraded warning, not file failure');
|
|
139
|
+
assert.equal(budgetLimited.diagnostics.requests, 0, 'budget block must happen before raw fetch requests');
|
|
140
|
+
assert.equal(budgetCalled.length, 0, 'budget-blocked raw load must not call fetch');
|
|
141
|
+
assert(budgetLimited.warnings.some((warning) => warning.code === 'transport.policy.request-budget-exceeded'), 'budget warning must be surfaced');
|
|
142
|
+
assert(budgetLimited.diagnostics.transportEvents.some((event) => event.code === 'transport.policy.request-budget-exceeded'), 'budget warning must be a transport event');
|
|
143
|
+
|
|
144
|
+
const issueLoaded = await materializeGithubSource(source, { issueDiscovery: true, issueUrls: 'https://github.com/owner/repo/issues/1' }, { fetchImpl });
|
|
145
|
+
assert.equal(issueLoaded.records.length, 1, 'explicit issue target should materialize through browser issue reader');
|
|
146
|
+
assert.equal(issueLoaded.records[0].kind, 'tiinex.evidence.v1', 'issue reader should create an Evidence snapshot record');
|
|
147
|
+
assert.equal(issueLoaded.records[0].summary, 'Issue body', 'issue reader should project issue body into the visible card summary');
|
|
148
|
+
assert(issueLoaded.records[0].markdown.includes('## Interpretation Limits'), 'issue reader should create schema-valid Evidence sections');
|
|
149
|
+
assert.equal(issueLoaded.diagnostics.issueSnapshotTargets, 1, 'issue targets should be parsed into diagnostics');
|
|
150
|
+
assert.equal(issueLoaded.diagnostics.surfaces.issueSnapshots.loaded, 1, 'issue surface should report loaded browser issue reader state');
|
|
151
|
+
assert.equal(issueLoaded.diagnostics.surfaces.repoFiles.loaded, 0, 'issue targets must not be attributed to repo files');
|
|
152
|
+
assert.equal(issueLoaded.diagnostics.recordAttribution[0].surface, 'issueSnapshots', 'issue targets must claim issueSnapshots attribution');
|
|
153
|
+
assert.equal(issueLoaded.diagnostics.sourcePlan.surfaces.issueSnapshots.requested, true, 'normalized source plan should preserve requested issue surface');
|
|
154
|
+
assert.equal(issueLoaded.diagnostics.sourcePlan.surfaces.issueSnapshots.attempted, true, 'normalized source plan should preserve attempted issue surface');
|
|
155
|
+
assert(issueLoaded.diagnostics.transportEvents.some((event) => event.url === issueListApi && event.code === 'github.transport.proxy.ok'), 'broad issue discovery should expose its GitHub API list read as the proxy tier even when an explicit target is also included');
|
|
156
|
+
assert(!issueLoaded.diagnostics.transportEvents.some((event) => event.url === issueListApi && event.code === 'github.transport.direct.ok'), 'broad discovery must not label GitHub API reads as direct');
|
|
157
|
+
assert.equal(issueLoaded.diagnostics.issueSnapshotDiscovery.unionTargets, 1, 'broad + explicit duplicate target should dedupe to one issue');
|
|
158
|
+
|
|
159
|
+
const issueUrlAsExplicitFile = await materializeGithubSource(source, { repoDiscovery: false, fileRefs: ['https://github.com/owner/repo/issues/1'], issueDiscovery: false, issueUrls: '' }, { fetchImpl });
|
|
160
|
+
assert.equal(issueUrlAsExplicitFile.okCount, 0, 'issue URL in explicit file surface must not become a repo file');
|
|
161
|
+
assert.equal(issueUrlAsExplicitFile.diagnostics.surfaces.repoFiles.loaded, 0, 'invalid explicit issue URL must not increment repo file surface');
|
|
162
|
+
assert.equal(issueUrlAsExplicitFile.diagnostics.surfaces.explicitFiles.failed, 1, 'invalid explicit issue URL should be owned by explicit files surface');
|
|
163
|
+
assert(issueUrlAsExplicitFile.errors.some((error) => error.surface === 'explicitFiles'), 'invalid explicit target should preserve owning surface');
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
const issueBudgetCalled = [];
|
|
167
|
+
const issueBudgetBlocked = await materializeGithubSource(
|
|
168
|
+
source,
|
|
169
|
+
{ issueDiscovery: true, issueUrls: 'https://github.com/owner/repo/issues/1' },
|
|
170
|
+
{ fetchImpl: makeFetch(map, issueBudgetCalled), maxRequestsPerOperation: 1, allowCache: false }
|
|
171
|
+
);
|
|
172
|
+
assert.equal(issueBudgetBlocked.records.length, 0, 'transport budget should block explicit issue snapshot loading before fetching');
|
|
173
|
+
assert.equal(issueBudgetCalled.length, 0, 'budget-blocked issue snapshot load must not call GitHub API');
|
|
174
|
+
assert.equal(issueBudgetBlocked.diagnostics.surfaces.issueSnapshots.skipped, true, 'issue surface should report skipped when budget-blocked');
|
|
175
|
+
assert(issueBudgetBlocked.warnings.some((warning) => warning.code === 'transport.policy.request-budget-exceeded' && warning.surface === 'issueSnapshots'), 'issue budget warning must stay owned by issueSnapshots');
|
|
176
|
+
|
|
177
|
+
const repoWideIssueLoaded = await materializeGithubSource(source, { issueDiscovery: true, issueUrls: '' }, { fetchImpl });
|
|
178
|
+
assert.equal(repoWideIssueLoaded.records.length, 1, 'repo-wide issue discovery should materialize bounded public issue snapshots');
|
|
179
|
+
assert.equal(repoWideIssueLoaded.diagnostics.issueSnapshotDiscovery.discovered, 1, 'repo-wide issue discovery should report discovered targets');
|
|
180
|
+
assert.equal(repoWideIssueLoaded.diagnostics.surfaces.issueSnapshots.loaded, 1, 'issue surface should report loaded repo-wide issue snapshots');
|
|
181
|
+
assert.equal(repoWideIssueLoaded.diagnostics.transportOutcome.sequenceTier, 'proxy', 'repo-wide issue discovery via GitHub API should display proxy, not cache or direct');
|
|
182
|
+
|
|
183
|
+
const issueSnapshot = await materializeGithubSource(source, { issueDiscovery: true, issueUrls: 'https://github.com/owner/repo/issues/1' }, { fetchImpl, issueSnapshotFixtures: { 'https://github.com/owner/repo/issues/1': { title: 'Fixture issue', state: 'open', body: 'Issue body', user: { login: 'q' }, created_at: '2026-07-21T00:00:00.000Z' } } });
|
|
184
|
+
assert.equal(issueSnapshot.records.length, 1, 'fixture-backed issue snapshot should materialize one evidence record');
|
|
185
|
+
assert.equal(issueSnapshot.records[0].kind, 'tiinex.evidence.v1', 'issue snapshot should become an evidence record');
|
|
186
|
+
assert.equal(issueSnapshot.records[0].source, undefined, 'adapter must not assign lifecycle source provenance to issue snapshots');
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
const progressSinkFailure = await materializeGithubSource(
|
|
191
|
+
source,
|
|
192
|
+
{ issueDiscovery: true, issueUrls: '' },
|
|
193
|
+
{ fetchImpl, onProgress: () => { throw new Error('progress sink failed'); } }
|
|
194
|
+
);
|
|
195
|
+
assert.equal(progressSinkFailure.records.length, 0, 'issue surface exceptions should not promote invalid records');
|
|
196
|
+
assert.equal(progressSinkFailure.errors.length, 0, 'issue surface exceptions should remain degraded warnings, not fatal adapter errors');
|
|
197
|
+
assert(progressSinkFailure.warnings.some((warning) => warning.code === 'github.issue.surface.exception'), 'issue surface exceptions should surface a retryable issue warning');
|
|
198
|
+
assert.equal(progressSinkFailure.diagnostics.sourcePlan.surfaces.issueSnapshots.requested, true, 'issue surface request must remain in diagnostics after degraded failure');
|
|
199
|
+
assert.equal(progressSinkFailure.diagnostics.sourcePlan.surfaces.issueSnapshots.unavailable, true, 'issue surface exception should be marked unavailable, not fatal');
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
console.log('✓ github.adapter tests passed');
|
|
203
|
+
|
|
204
|
+
const discoveryBudgetCalled = [];
|
|
205
|
+
const discoveryBudgetBlocked = await materializeGithubSource(
|
|
206
|
+
{ id: 'github:owner/repo', repo, ref: '', rootPath: '.topics' },
|
|
207
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
208
|
+
{ fetchImpl: makeFetch(map, discoveryBudgetCalled), maxRequestsPerOperation: 1, allowCache: false }
|
|
209
|
+
);
|
|
210
|
+
assert.equal(discoveryBudgetBlocked.records.length, 0, 'transport budget should block repo discovery before fetching');
|
|
211
|
+
assert.equal(discoveryBudgetBlocked.diagnostics.discoveryBlockedByPolicy, true, 'repo discovery policy block must be diagnosed');
|
|
212
|
+
assert.equal(discoveryBudgetCalled.length, 0, 'budget-blocked repo discovery must not call default-branch or tree fetch');
|
|
213
|
+
assert(discoveryBudgetBlocked.warnings.some((warning) => warning.code === 'transport.policy.request-budget-exceeded'), 'repo discovery budget warning must be surfaced');
|
|
214
|
+
|
|
215
|
+
const proxyIssueCalls = [];
|
|
216
|
+
const proxyIssueLoaded = await materializeGithubSource(
|
|
217
|
+
source,
|
|
218
|
+
{ issueDiscovery: false, issueUrls: 'https://github.com/owner/repo/issues/1' },
|
|
219
|
+
{ fetchImpl: async () => { throw new Error('direct fetch must not run for configured proxy issue test'); }, proxyFetchImpl: makeFetch(map, proxyIssueCalls), preferredTransports: ['proxy'], transportOrderExact: true }
|
|
220
|
+
);
|
|
221
|
+
assert.equal(proxyIssueLoaded.records.length, 1, 'explicit proxy transport should read issue snapshots through the GitHub issue API tier');
|
|
222
|
+
assert(proxyIssueLoaded.diagnostics.transportEvents.some((event) => event.code === 'github.transport.proxy.ok' && event.url === issueApi), 'proxy issue API success should be exposed as proxy transport evidence');
|
|
223
|
+
assert(!proxyIssueLoaded.diagnostics.transportEvents.some((event) => String(event.code || '').includes('direct')), 'explicit proxy issue refresh must not silently fall through to direct');
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
function makeNativeResponseFetch(map, called = []) {
|
|
227
|
+
return async function fetchImpl(url) {
|
|
228
|
+
called.push(url);
|
|
229
|
+
const hit = map[url];
|
|
230
|
+
if (!hit) return new Response(JSON.stringify({ message: 'Not Found' }), { status: 404, statusText: 'Not Found', headers: { 'content-type': 'application/json' } });
|
|
231
|
+
if (hit.ok === false) return new Response(JSON.stringify(hit.json || { message: hit.statusText || 'Error' }), { status: hit.status || 500, statusText: hit.statusText || 'Error', headers: { 'content-type': 'application/json' } });
|
|
232
|
+
if (hit.text != null) return new Response(String(hit.text), { status: 200, statusText: 'OK', headers: { 'content-type': 'text/markdown; charset=utf-8' } });
|
|
233
|
+
return new Response(JSON.stringify(hit.json || {}), { status: 200, statusText: 'OK', headers: { 'content-type': 'application/json' } });
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const nativeProxyIssueCalls = [];
|
|
238
|
+
const nativeProxyIssueLoaded = await materializeGithubSource(
|
|
239
|
+
source,
|
|
240
|
+
{ issueDiscovery: false, issueUrls: 'https://github.com/owner/repo/issues/1' },
|
|
241
|
+
{ fetchImpl: async () => { throw new Error('direct fetch must not run for configured native proxy issue test'); }, proxyFetchImpl: makeNativeResponseFetch(map, nativeProxyIssueCalls), preferredTransports: ['proxy'], transportOrderExact: true }
|
|
242
|
+
);
|
|
243
|
+
assert.equal(nativeProxyIssueLoaded.records.length, 1, 'explicit proxy transport should work with native Fetch Response objects');
|
|
244
|
+
assert(nativeProxyIssueLoaded.diagnostics.transportEvents.some((event) => event.code === 'github.transport.proxy.ok' && event.url === issueApi), 'native proxy issue API success should remain observable as proxy transport evidence');
|
|
245
|
+
|
|
246
|
+
const mirrorBase = 'https://viewer.example/';
|
|
247
|
+
const mirrorMetaUrl = 'https://viewer.example/issues/github.com/owner/repo.json';
|
|
248
|
+
const mirrorManifestUrl = 'https://viewer.example/issues/github.com/owner/repo/manifest.json';
|
|
249
|
+
const mirrorIssueJsonUrl = 'https://viewer.example/issues/github.com/owner/repo/issues/2/issue.json';
|
|
250
|
+
const mirrorIssueBodyUrl = 'https://viewer.example/issues/github.com/owner/repo/issues/2/issue.md';
|
|
251
|
+
const mirrorCommentJsonUrl = 'https://viewer.example/issues/github.com/owner/repo/issues/2/comments/200.json';
|
|
252
|
+
const mirrorCommentBodyUrl = 'https://viewer.example/issues/github.com/owner/repo/issues/2/comments/200.md';
|
|
253
|
+
const mirrorMap = {
|
|
254
|
+
[mirrorMetaUrl]: { json: { type: 'tiinex.github.issues.snapshot', repo, directory: 'repo/', manifest: 'repo/manifest.json', generatedAt: '2026-07-24T00:00:00.000Z', sourceUpdatedAt: '2026-07-24T00:00:00.000Z' } },
|
|
255
|
+
[mirrorManifestUrl]: { json: { type: 'tiinex.github.issues.snapshot.manifest.v1', repository: repo, issues: [{ number: 2, title: 'Mirror issue', state: 'open', updated_at: '2026-07-24T00:00:00.000Z', issue: 'issues/2/issue.json', body: 'issues/2/issue.md' }] } },
|
|
256
|
+
[mirrorIssueJsonUrl]: { json: { html_url: 'https://github.com/owner/repo/issues/2', number: 2, title: 'Mirror issue', state: 'open', user: { login: 'mirror' }, created_at: '2026-07-24T00:00:00.000Z', comments: [{ id: 200, json: 'comments/200.json', path: 'comments/200.md', html_url: 'https://github.com/owner/repo/issues/2#issuecomment-200' }] } },
|
|
257
|
+
[mirrorIssueBodyUrl]: { text: 'Mirror body from hosted snapshot' },
|
|
258
|
+
[mirrorCommentJsonUrl]: { json: { id: 200, user: { login: 'commenter' }, html_url: 'https://github.com/owner/repo/issues/2#issuecomment-200' } },
|
|
259
|
+
[mirrorCommentBodyUrl]: { text: 'Mirror comment from hosted snapshot' }
|
|
260
|
+
};
|
|
261
|
+
const mirrorIssueCalls = [];
|
|
262
|
+
const mirrorIssueLoaded = await materializeGithubSource(
|
|
263
|
+
source,
|
|
264
|
+
{ issueDiscovery: true, issueUrls: '' },
|
|
265
|
+
{ fetchImpl: makeFetch(mirrorMap, mirrorIssueCalls), preferredTransports: ['mirror'], transportOrderExact: true, hostedIssueSnapshotBaseUrls: [mirrorBase] }
|
|
266
|
+
);
|
|
267
|
+
assert.equal(mirrorIssueLoaded.records.length, 1, 'explicit mirror transport should read hosted issue snapshots');
|
|
268
|
+
assert.equal(mirrorIssueLoaded.records[0].summary, 'Mirror body from hosted snapshot', 'mirror issue snapshot body should become card summary');
|
|
269
|
+
assert(mirrorIssueLoaded.diagnostics.transportEvents.some((event) => event.code === 'github.transport.mirror.ok' && event.url === mirrorMetaUrl), 'mirror metadata fetch should be exposed as mirror transport evidence');
|
|
270
|
+
assert(!mirrorIssueCalls.some((url) => url.includes('api.github.com')), 'explicit mirror issue refresh must not call live GitHub API');
|
|
271
|
+
|
|
272
|
+
const mirrorPartialCommentCalls = [];
|
|
273
|
+
const mirrorPartialCommentLoaded = await materializeGithubSource(
|
|
274
|
+
source,
|
|
275
|
+
{ issueDiscovery: true, issueUrls: '' },
|
|
276
|
+
{
|
|
277
|
+
fetchImpl: makeFetch(Object.assign({}, mirrorMap, { [mirrorCommentBodyUrl]: { ok: false, status: 404, statusText: 'Not Found', json: { message: 'missing comment body' } } }), mirrorPartialCommentCalls),
|
|
278
|
+
preferredTransports: ['mirror'],
|
|
279
|
+
transportOrderExact: true,
|
|
280
|
+
hostedIssueSnapshotBaseUrls: [mirrorBase]
|
|
281
|
+
}
|
|
282
|
+
);
|
|
283
|
+
assert.equal(mirrorPartialCommentLoaded.records.length, 1, 'mirror issue body should still materialize when one hosted comment payload is unavailable');
|
|
284
|
+
assert(mirrorPartialCommentLoaded.warnings.some((warning) => warning.code === 'github.issue.mirror.comment-fetch-failed'), 'mirror comment fetch degradation should be explicit');
|
|
285
|
+
assert.equal(mirrorPartialCommentLoaded.diagnostics.issueSnapshotMaterialization.loadedTargets, 1, 'mirror target diagnostics should count the issue target loaded despite partial comments');
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
const nativeMirrorIssueCalls = [];
|
|
289
|
+
const nativeMirrorIssueLoaded = await materializeGithubSource(
|
|
290
|
+
source,
|
|
291
|
+
{ issueDiscovery: true, issueUrls: '' },
|
|
292
|
+
{ fetchImpl: makeNativeResponseFetch(mirrorMap, nativeMirrorIssueCalls), preferredTransports: ['mirror'], transportOrderExact: true, hostedIssueSnapshotBaseUrls: [mirrorBase] }
|
|
293
|
+
);
|
|
294
|
+
assert.equal(nativeMirrorIssueLoaded.records.length, 1, 'explicit mirror transport should work with native Fetch Response objects');
|
|
295
|
+
assert.equal(nativeMirrorIssueLoaded.records[0].summary, 'Mirror body from hosted snapshot', 'native mirror issue snapshot body should become card summary');
|
|
296
|
+
assert(nativeMirrorIssueLoaded.diagnostics.transportEvents.some((event) => event.code === 'github.transport.mirror.ok' && event.url === mirrorMetaUrl), 'native mirror metadata success should remain observable as mirror transport evidence');
|
|
297
|
+
|
|
298
|
+
const defaultMirrorIssueCalls = [];
|
|
299
|
+
const defaultMirrorIssueLoaded = await materializeGithubSource(
|
|
300
|
+
source,
|
|
301
|
+
{ issueDiscovery: true, issueUrls: '' },
|
|
302
|
+
{ fetchImpl: makeFetch(mirrorMap, defaultMirrorIssueCalls), hostedIssueSnapshotBaseUrls: [mirrorBase] }
|
|
303
|
+
);
|
|
304
|
+
assert.equal(defaultMirrorIssueLoaded.records.length, 1, 'default issue materialization should try hosted mirror before live API');
|
|
305
|
+
assert(defaultMirrorIssueLoaded.diagnostics.transportEvents.some((event) => event.code === 'github.transport.cache.hit' && event.url === mirrorMetaUrl), 'default issue materialization should restore from Tiinex source cache after a hosted mirror load');
|
|
306
|
+
assert(!defaultMirrorIssueCalls.some((url) => url.includes('api.github.com')), 'source-cache issue restore should not call live GitHub API during default issue loading');
|
|
307
|
+
assert.equal(defaultMirrorIssueLoaded.diagnostics.transportOutcome.sequenceTier, 'cache', 'source-cache issue restore should make cache the visible transport tier after F5/route restore');
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
const directDiscoveryCalls = [];
|
|
311
|
+
const directDiscoveryOnly = await materializeGithubSource(
|
|
312
|
+
source,
|
|
313
|
+
{ issueDiscovery: true, issueUrls: '' },
|
|
314
|
+
{ fetchImpl: makeFetch(map, directDiscoveryCalls), preferredTransports: ['direct'], transportOrderExact: true }
|
|
315
|
+
);
|
|
316
|
+
assert.equal(directDiscoveryOnly.records.length, 0, 'direct issue transport must not perform repository-wide issue discovery');
|
|
317
|
+
assert.equal(directDiscoveryCalls.length, 0, 'direct issue discovery must not call GitHub API or raw URLs without explicit targets');
|
|
318
|
+
assert(directDiscoveryOnly.warnings.some((warning) => warning.code === 'github.issue.direct.discovery-unavailable'), 'direct issue discovery should explain that explicit issue URLs are required');
|
|
319
|
+
|
|
320
|
+
const directIssuePage = 'https://github.com/owner/repo/issues/1';
|
|
321
|
+
const directExplicitCalls = [];
|
|
322
|
+
const directExplicitLoaded = await materializeGithubSource(
|
|
323
|
+
source,
|
|
324
|
+
{ issueDiscovery: true, issueUrls: directIssuePage },
|
|
325
|
+
{ fetchImpl: makeFetch({ [directIssuePage]: { text: 'Direct raw issue body' } }, directExplicitCalls), preferredTransports: ['direct'], transportOrderExact: true }
|
|
326
|
+
);
|
|
327
|
+
assert.equal(directExplicitLoaded.records.length, 1, 'direct issue transport may read explicitly provided issue URLs as raw/browser-readable material');
|
|
328
|
+
assert(directExplicitCalls.includes(directIssuePage), 'direct explicit issue load should fetch the canonical issue URL');
|
|
329
|
+
assert(!directExplicitCalls.some((url) => url.includes('api.github.com')), 'direct explicit issue load must not call GitHub API');
|
|
330
|
+
assert.equal(directExplicitLoaded.diagnostics.transportOutcome.sequenceTier, 'direct', 'direct explicit issue load should leave the visible transport sequence at direct');
|
|
331
|
+
|
|
332
|
+
const directStaticIssueUrl = 'https://viewer.example/issues/github.com/owner/repo/issues/4/issue.md';
|
|
333
|
+
const directStaticCalls = [];
|
|
334
|
+
const directStaticLoaded = await materializeGithubSource(
|
|
335
|
+
source,
|
|
336
|
+
{ issueDiscovery: true, issueUrls: directStaticIssueUrl },
|
|
337
|
+
{ fetchImpl: makeFetch({ [directStaticIssueUrl]: { text: 'Direct static issue body' } }, directStaticCalls), preferredTransports: ['direct'], transportOrderExact: true }
|
|
338
|
+
);
|
|
339
|
+
assert.equal(directStaticLoaded.records.length, 1, 'direct issue transport may read browser-readable static issue snapshot URLs');
|
|
340
|
+
assert(directStaticCalls.includes(directStaticIssueUrl), 'direct static issue load should fetch the literal static URL');
|
|
341
|
+
assert(!directStaticCalls.some((url) => url.includes('api.github.com')), 'direct static issue load must not call GitHub API');
|
|
342
|
+
|
|
343
|
+
const mirrorIssueFallbackCalls = [];
|
|
344
|
+
const mirrorIssueFallbackLoaded = await materializeGithubSource(
|
|
345
|
+
source,
|
|
346
|
+
{ issueDiscovery: false, issueUrls: 'https://github.com/owner/repo/issues/1' },
|
|
347
|
+
{ fetchImpl: makeFetch(map, mirrorIssueFallbackCalls), preferredTransports: ['mirror'], transportOrderExact: true, hostedIssueSnapshotBaseUrls: ['https://viewer.example/missing/'] }
|
|
348
|
+
);
|
|
349
|
+
assert.equal(mirrorIssueFallbackLoaded.records.length, 1, 'issue snapshots should fall back from hosted mirror to the next issue transport');
|
|
350
|
+
assert(mirrorIssueFallbackLoaded.diagnostics.transportEvents.some((event) => event.code === 'github.issue.transport.surface-fallback'), 'issue surface fallback must be diagnosable');
|
|
351
|
+
assert(mirrorIssueFallbackLoaded.diagnostics.transportEvents.some((event) => event.code === 'github.transport.proxy.ok' && event.url === issueApi), 'fallback issue load should expose GitHub issue API as proxy after hosted mirror is unavailable');
|
|
352
|
+
assert(!mirrorIssueFallbackLoaded.diagnostics.transportEvents.some((event) => event.code === 'github.transport.direct.ok' && String(event.url || '').includes('api.github.com')), 'issue direct tier must not call GitHub API during mirror fallback');
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
const repoMirrorRepo = 'mirror-owner/mirror-repo';
|
|
356
|
+
const repoMirrorSource = { id: 'github:mirror-owner/mirror-repo:.topics', repo: repoMirrorRepo, ref: '', rootPath: '.topics' };
|
|
357
|
+
const repoMirrorMetaUrl = 'https://viewer.example/mirrors/github.com/mirror-owner/mirror-repo.json';
|
|
358
|
+
const repoMirrorArchiveUrl = 'https://viewer.example/mirrors/github.com/mirror-owner/mirror/repo.zip';
|
|
359
|
+
const repoMirrorZip = storedZip([
|
|
360
|
+
{ name: 'mirror-repo-main/.topics/mirror.md', data: '# Repo Mirror\n\nMirror file body' },
|
|
361
|
+
{ name: 'mirror-repo-main/README.md', data: '# Ignored' }
|
|
362
|
+
]);
|
|
363
|
+
const repoMirrorCalls = [];
|
|
364
|
+
const repoMirrorLoaded = await materializeGithubSource(
|
|
365
|
+
repoMirrorSource,
|
|
366
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
367
|
+
{
|
|
368
|
+
fetchImpl: makeFetch({
|
|
369
|
+
[repoMirrorMetaUrl]: { json: { type: 'tiinex.repository.snapshot', repository: repoMirrorRepo, commit: 'abc123', archive: 'mirror/repo.zip' } },
|
|
370
|
+
[repoMirrorArchiveUrl]: { buffer: repoMirrorZip }
|
|
371
|
+
}, repoMirrorCalls),
|
|
372
|
+
preferredTransports: ['mirror'],
|
|
373
|
+
transportOrderExact: true,
|
|
374
|
+
hostedRepoMirrorBaseUrls: [mirrorBase]
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
assert.equal(repoMirrorLoaded.records.length, 1, 'explicit repo-file mirror transport should read hosted repository snapshot archives');
|
|
378
|
+
assert.equal(repoMirrorLoaded.records[0].path, 'https://raw.githubusercontent.com/mirror-owner/mirror-repo/abc123/.topics/mirror.md', 'repo mirror records should preserve source raw URL path identity');
|
|
379
|
+
assert.equal(repoMirrorLoaded.records[0].sourceTarget.transportTier, 'mirror', 'repo mirror records should disclose mirror transport');
|
|
380
|
+
assert(!repoMirrorCalls.some((url) => url.includes('api.github.com')), 'repo mirror transport must not call GitHub API tree discovery');
|
|
381
|
+
assert.equal(repoMirrorLoaded.diagnostics.surfaces.repoFiles.transportTier, 'mirror', 'repo mirror success should set repoFiles surface transport to mirror');
|
|
382
|
+
|
|
383
|
+
const repoCacheCalls = [];
|
|
384
|
+
const repoCacheLoaded = await materializeGithubSource(
|
|
385
|
+
Object.assign({}, repoMirrorSource, { ref: 'abc123' }),
|
|
386
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
387
|
+
{ fetchImpl: makeFetch({}, repoCacheCalls), hostedRepoMirrorBaseUrls: [mirrorBase] }
|
|
388
|
+
);
|
|
389
|
+
assert.equal(repoCacheLoaded.records.length, 1, 'repo-file source cache should restore after a mirror load without network materialization');
|
|
390
|
+
assert.equal(repoCacheLoaded.records[0].sourceTarget.transportTier, 'cache', 'repo-file source-cache restore should disclose cache transport');
|
|
391
|
+
assert.equal(repoCacheCalls.length, 0, 'repo-file cache restore should not call mirror, proxy, direct, or GitHub API');
|
|
392
|
+
assert.equal(repoCacheLoaded.diagnostics.surfaces.repoFiles.transportTier, 'cache', 'repo-file cache restore should set repoFiles surface transport to cache');
|
|
393
|
+
|
|
394
|
+
const repoNoCacheCalls = [];
|
|
395
|
+
const repoNoCacheLoaded = await materializeGithubSource(
|
|
396
|
+
Object.assign({}, repoMirrorSource, { ref: 'abc123' }),
|
|
397
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
398
|
+
{
|
|
399
|
+
fetchImpl: makeFetch({
|
|
400
|
+
[repoMirrorMetaUrl]: { json: { type: 'tiinex.repository.snapshot', repository: repoMirrorRepo, commit: 'abc123', archive: 'mirror/repo.zip' } },
|
|
401
|
+
[repoMirrorArchiveUrl]: { buffer: repoMirrorZip }
|
|
402
|
+
}, repoNoCacheCalls),
|
|
403
|
+
hostedRepoMirrorBaseUrls: [mirrorBase],
|
|
404
|
+
allowCache: false
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
assert.equal(repoNoCacheLoaded.records.length, 1, 'user/material reload should bypass source-cache when allowCache is false');
|
|
408
|
+
assert(repoNoCacheCalls.some((url) => url === repoMirrorMetaUrl), 'allowCache=false should try mirror instead of serving repo files from cache');
|
|
409
|
+
|
|
410
|
+
const repoCaseMirrorCalls = [];
|
|
411
|
+
const repoCaseMirrorLoaded = await materializeGithubSource(
|
|
412
|
+
{ id: 'github:Tiinex/docs:.topics', repo: 'Tiinex/docs', ref: '', rootPath: '.topics' },
|
|
413
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
414
|
+
{
|
|
415
|
+
fetchImpl: makeFetch({
|
|
416
|
+
['https://viewer.example/mirrors/github.com/Tiinex/docs.json']: { json: { type: 'tiinex.repository.snapshot', repository: 'Tiinex/docs', commit: 'case123', archive: 'case.zip' } },
|
|
417
|
+
['https://viewer.example/mirrors/github.com/Tiinex/case.zip']: { buffer: repoMirrorZip }
|
|
418
|
+
}, repoCaseMirrorCalls),
|
|
419
|
+
preferredTransports: ['mirror'],
|
|
420
|
+
transportOrderExact: true,
|
|
421
|
+
hostedRepoMirrorBaseUrls: [mirrorBase]
|
|
422
|
+
}
|
|
423
|
+
);
|
|
424
|
+
assert.equal(repoCaseMirrorLoaded.records.length, 1, 'repo-file mirror should try case-preserving Tiinex/docs metadata before lowercase fallback');
|
|
425
|
+
assert(repoCaseMirrorCalls.some((url) => url === 'https://viewer.example/mirrors/github.com/Tiinex/docs.json'), 'case-preserving repository mirror URL should be attempted');
|
|
426
|
+
|
|
427
|
+
const repoProxyCalls = [];
|
|
428
|
+
const repoProxyExact = await materializeGithubSource(
|
|
429
|
+
repoMirrorSource,
|
|
430
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
431
|
+
{ fetchImpl: makeFetch(map, repoProxyCalls), preferredTransports: ['proxy'], transportOrderExact: true }
|
|
432
|
+
);
|
|
433
|
+
assert.equal(repoProxyExact.records.length, 0, 'repo-file proxy should not silently fall through to direct when the browser Git runtime is unavailable');
|
|
434
|
+
assert.equal(repoProxyCalls.length, 0, 'repo-file proxy unavailable should not call GitHub API through direct fetch');
|
|
435
|
+
assert(repoProxyExact.warnings.some((warning) => warning.code === 'github.repo.proxy.unavailable'), 'repo-file proxy unavailability should be explicit');
|
|
436
|
+
|
|
437
|
+
const fakeGitProxyRuntime = {
|
|
438
|
+
acquireSnapshot: async (options = {}) => ({
|
|
439
|
+
ok: true,
|
|
440
|
+
repo: options.repo,
|
|
441
|
+
ref: 'main',
|
|
442
|
+
commit: 'def456',
|
|
443
|
+
networkOperation: 'git-proxy-snapshot',
|
|
444
|
+
networkOperationSucceeded: true,
|
|
445
|
+
candidates: ['.topics/proxy.md', 'README.md']
|
|
446
|
+
}),
|
|
447
|
+
ensureRuntime: async () => ({ ok: true }),
|
|
448
|
+
readGitText: async (_runtime, path) => (path === '.topics/proxy.md' ? '# Repo Proxy\n\nProxy file body' : '')
|
|
449
|
+
};
|
|
450
|
+
const repoProxyLoaded = await materializeGithubSource(
|
|
451
|
+
repoMirrorSource,
|
|
452
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
453
|
+
{
|
|
454
|
+
fetchImpl: async () => { throw new Error('direct fetch must not run for repo-file proxy'); },
|
|
455
|
+
gitNativeRuntime: fakeGitProxyRuntime,
|
|
456
|
+
workspaceConfig: { repositoryTransports: [{ kind: 'git-proxy', match: 'github.com/*', proxy: 'https://cors.isomorphic-git.org' }] },
|
|
457
|
+
preferredTransports: ['proxy'],
|
|
458
|
+
transportOrderExact: true
|
|
459
|
+
}
|
|
460
|
+
);
|
|
461
|
+
assert.equal(repoProxyLoaded.records.length, 1, 'repo-file proxy should materialize Markdown through the browser Git runtime when configured');
|
|
462
|
+
assert.equal(repoProxyLoaded.records[0].sourceTarget.transportTier, 'proxy', 'repo-file proxy records should disclose proxy transport');
|
|
463
|
+
assert.equal(repoProxyLoaded.diagnostics.surfaces.repoFiles.transportTier, 'proxy', 'repo-file proxy success should set repoFiles surface transport to proxy');
|
|
464
|
+
assert(repoProxyLoaded.diagnostics.transportEvents.some((event) => event.code === 'github.repo.proxy.ok'), 'repo-file proxy success should be diagnosable');
|
|
465
|
+
|
|
466
|
+
const abortController = new AbortController();
|
|
467
|
+
abortController.abort(new DOMException('user advanced transport', 'AbortError'));
|
|
468
|
+
let abortedRuntimeSawSignal = false;
|
|
469
|
+
const abortAwareGitProxyRuntime = {
|
|
470
|
+
acquireSnapshot: async (options = {}) => {
|
|
471
|
+
abortedRuntimeSawSignal = Boolean(options.transportSignal?.aborted);
|
|
472
|
+
throw options.transportSignal?.reason || new DOMException('transport aborted', 'AbortError');
|
|
473
|
+
},
|
|
474
|
+
ensureRuntime: async () => { throw new Error('aborted proxy must not continue to ensureRuntime'); },
|
|
475
|
+
readGitText: async () => ''
|
|
476
|
+
};
|
|
477
|
+
const repoProxyAborted = await materializeGithubSource(
|
|
478
|
+
repoMirrorSource,
|
|
479
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
480
|
+
{
|
|
481
|
+
gitNativeRuntime: abortAwareGitProxyRuntime,
|
|
482
|
+
workspaceConfig: { repositoryTransports: [{ kind: 'git-proxy', match: 'github.com/*', proxy: 'https://cors.isomorphic-git.org' }] },
|
|
483
|
+
preferredTransports: ['proxy'],
|
|
484
|
+
transportOrderExact: true,
|
|
485
|
+
abortSignal: abortController.signal
|
|
486
|
+
}
|
|
487
|
+
);
|
|
488
|
+
assert.equal(abortedRuntimeSawSignal, true, 'repo proxy runtime should receive the active operation abort signal');
|
|
489
|
+
assert(repoProxyAborted.warnings.some((warning) => warning.code === 'github.repo.proxy.aborted'), 'aborted repo proxy transport should be diagnosable and non-committing');
|
|
490
|
+
|
|
491
|
+
let slowRuntimeOptions = null;
|
|
492
|
+
const slowPolicyRuntime = {
|
|
493
|
+
acquireSnapshot: async (options = {}) => { slowRuntimeOptions = options; return { ok: false, error: 'stop after capture' }; },
|
|
494
|
+
ensureRuntime: async () => ({ ok: true }),
|
|
495
|
+
readGitText: async () => ''
|
|
496
|
+
};
|
|
497
|
+
await materializeGithubSource(
|
|
498
|
+
repoMirrorSource,
|
|
499
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
500
|
+
{
|
|
501
|
+
gitNativeRuntime: slowPolicyRuntime,
|
|
502
|
+
workspaceConfig: { repositoryTransports: [{ kind: 'git-proxy', match: 'github.com/*', proxy: 'https://cors.isomorphic-git.org' }] },
|
|
503
|
+
preferredTransports: ['proxy'],
|
|
504
|
+
transportOrderExact: true,
|
|
505
|
+
repoProxyTimeoutMs: 9000
|
|
506
|
+
}
|
|
507
|
+
);
|
|
508
|
+
assert.equal(slowRuntimeOptions.maxNetworkDurationMs, 9000, 'repo proxy should pass a hard network budget into the browser Git runtime');
|
|
509
|
+
assert.equal(slowRuntimeOptions.reloadRuntime, true, 'repo proxy should refresh the runtime wrapper so abort signals do not get trapped in a stale cached http client');
|
|
510
|
+
assert(slowRuntimeOptions.minBytesPerSecond >= 8192, 'repo proxy should pass a low-throughput floor into the browser Git runtime');
|
|
511
|
+
|
|
512
|
+
const repoDirectCalls = [];
|
|
513
|
+
const repoDirectLoaded = await materializeGithubSource(
|
|
514
|
+
source,
|
|
515
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
516
|
+
{ fetchImpl: makeFetch(map, repoDirectCalls), preferredTransports: ['direct'], transportOrderExact: true }
|
|
517
|
+
);
|
|
518
|
+
assert.equal(repoDirectLoaded.records.length, 2, 'repo-file direct transport should use GitHub tree/raw fallback when direct is explicitly selected');
|
|
519
|
+
assert(repoDirectCalls.some((url) => url === treeApi), 'repo-file direct transport should perform tree discovery only at direct tier');
|
|
520
|
+
assert(repoDirectLoaded.records.every((record) => record.sourceTarget?.transportTier === 'direct'), 'repo-file direct records should disclose direct raw/API transport');
|
|
521
|
+
|
|
522
|
+
function toArrayBuffer(value) {
|
|
523
|
+
const buffer = value instanceof Uint8Array ? value : Buffer.from(value);
|
|
524
|
+
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function storedZip(entries) {
|
|
528
|
+
const localParts = [];
|
|
529
|
+
const centralParts = [];
|
|
530
|
+
let offset = 0;
|
|
531
|
+
for (const entry of entries) {
|
|
532
|
+
const name = Buffer.from(entry.name, 'utf8');
|
|
533
|
+
const data = Buffer.from(entry.data);
|
|
534
|
+
const crc = crc32(data);
|
|
535
|
+
const local = Buffer.alloc(30);
|
|
536
|
+
local.writeUInt32LE(0x04034b50, 0);
|
|
537
|
+
local.writeUInt16LE(20, 4);
|
|
538
|
+
local.writeUInt16LE(0x0800, 6);
|
|
539
|
+
local.writeUInt16LE(0, 8);
|
|
540
|
+
local.writeUInt16LE(0, 10);
|
|
541
|
+
local.writeUInt16LE(0, 12);
|
|
542
|
+
local.writeUInt32LE(crc, 14);
|
|
543
|
+
local.writeUInt32LE(data.length, 18);
|
|
544
|
+
local.writeUInt32LE(data.length, 22);
|
|
545
|
+
local.writeUInt16LE(name.length, 26);
|
|
546
|
+
local.writeUInt16LE(0, 28);
|
|
547
|
+
localParts.push(local, name, data);
|
|
548
|
+
const central = Buffer.alloc(46);
|
|
549
|
+
central.writeUInt32LE(0x02014b50, 0);
|
|
550
|
+
central.writeUInt16LE(20, 4);
|
|
551
|
+
central.writeUInt16LE(20, 6);
|
|
552
|
+
central.writeUInt16LE(0x0800, 8);
|
|
553
|
+
central.writeUInt16LE(0, 10);
|
|
554
|
+
central.writeUInt16LE(0, 12);
|
|
555
|
+
central.writeUInt16LE(0, 14);
|
|
556
|
+
central.writeUInt32LE(crc, 16);
|
|
557
|
+
central.writeUInt32LE(data.length, 20);
|
|
558
|
+
central.writeUInt32LE(data.length, 24);
|
|
559
|
+
central.writeUInt16LE(name.length, 28);
|
|
560
|
+
central.writeUInt16LE(0, 30);
|
|
561
|
+
central.writeUInt16LE(0, 32);
|
|
562
|
+
central.writeUInt16LE(0, 34);
|
|
563
|
+
central.writeUInt16LE(0, 36);
|
|
564
|
+
central.writeUInt32LE(0, 38);
|
|
565
|
+
central.writeUInt32LE(offset, 42);
|
|
566
|
+
centralParts.push(central, name);
|
|
567
|
+
offset += local.length + name.length + data.length;
|
|
568
|
+
}
|
|
569
|
+
const centralDirectory = Buffer.concat(centralParts);
|
|
570
|
+
const end = Buffer.alloc(22);
|
|
571
|
+
end.writeUInt32LE(0x06054b50, 0);
|
|
572
|
+
end.writeUInt16LE(0, 4);
|
|
573
|
+
end.writeUInt16LE(0, 6);
|
|
574
|
+
end.writeUInt16LE(entries.length, 8);
|
|
575
|
+
end.writeUInt16LE(entries.length, 10);
|
|
576
|
+
end.writeUInt32LE(centralDirectory.length, 12);
|
|
577
|
+
end.writeUInt32LE(offset, 16);
|
|
578
|
+
end.writeUInt16LE(0, 20);
|
|
579
|
+
return Buffer.concat([...localParts, centralDirectory, end]);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function crc32(buffer) {
|
|
583
|
+
let crc = 0xffffffff;
|
|
584
|
+
for (const byte of buffer) {
|
|
585
|
+
crc ^= byte;
|
|
586
|
+
for (let i = 0; i < 8; i += 1) crc = (crc >>> 1) ^ (0xedb88320 & -(crc & 1));
|
|
587
|
+
}
|
|
588
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// v426 Q-path closure: requested ref and immutable materialized commit are separate truths.
|
|
592
|
+
const immutableCommit = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
|
|
593
|
+
const exactCommitApi = `https://api.github.com/repos/owner/repo/commits/main`;
|
|
594
|
+
const exactTreeApi = `https://api.github.com/repos/owner/repo/git/trees/${immutableCommit}?recursive=1`;
|
|
595
|
+
const exactRawTopic = `https://raw.githubusercontent.com/owner/repo/${immutableCommit}/.topics/a.md`;
|
|
596
|
+
const immutableFetch = makeFetch({
|
|
597
|
+
[repoApi]: { json: { default_branch: 'main' } },
|
|
598
|
+
[exactCommitApi]: { json: { sha: immutableCommit } },
|
|
599
|
+
[exactTreeApi]: { json: { truncated: false, tree: [{ type: 'blob', path: '.topics/a.md' }] } },
|
|
600
|
+
[exactRawTopic]: { text: '# Immutable A' }
|
|
601
|
+
});
|
|
602
|
+
const immutableDefault = await materializeGithubSource(source, { repoDiscovery: true, fileRefs: [] }, { fetchImpl: immutableFetch, allowCache: false });
|
|
603
|
+
assert.equal(immutableDefault.diagnostics.resolvedRef, 'main', 'default-branch selection remains ref truth');
|
|
604
|
+
assert.equal(immutableDefault.diagnostics.materializedCommit, immutableCommit, 'default branch resolves immutable material commit');
|
|
605
|
+
assert.equal(immutableDefault.records[0]?.sourceTarget?.materializedCommit, immutableCommit, 'loaded representation carries immutable commit provenance');
|
|
606
|
+
assert.equal(immutableDefault.records[0]?.sourceTarget?.rawUrl, exactRawTopic, 'raw representation is loaded from exact commit, not moving branch');
|
|
607
|
+
|
|
608
|
+
const namedCommit = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
|
|
609
|
+
const namedCommitApi = 'https://api.github.com/repos/owner/repo/commits/release';
|
|
610
|
+
const namedRaw = `https://raw.githubusercontent.com/owner/repo/${namedCommit}/.topics/a.md`;
|
|
611
|
+
const namedBranch = await materializeGithubSource(
|
|
612
|
+
{ ...source, ref: 'release' },
|
|
613
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md'] },
|
|
614
|
+
{ fetchImpl: makeFetch({ [namedCommitApi]: { json: { sha: namedCommit } }, [namedRaw]: { text: '# Named branch' } }), allowCache: false }
|
|
615
|
+
);
|
|
616
|
+
assert.equal(namedBranch.diagnostics.resolvedRef, 'release');
|
|
617
|
+
assert.equal(namedBranch.diagnostics.materializedCommit, namedCommit);
|
|
618
|
+
assert.equal(namedBranch.records[0]?.sourceTarget?.materializedCommit, namedCommit);
|
|
619
|
+
|
|
620
|
+
const pinnedCommit = 'cccccccccccccccccccccccccccccccccccccccc';
|
|
621
|
+
const pinnedRaw = `https://raw.githubusercontent.com/owner/repo/${pinnedCommit}/.topics/a.md`;
|
|
622
|
+
const pinnedCalls = [];
|
|
623
|
+
const pinned = await materializeGithubSource(
|
|
624
|
+
{ ...source, ref: pinnedCommit },
|
|
625
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md'] },
|
|
626
|
+
{ fetchImpl: makeFetch({ [pinnedRaw]: { text: '# Already pinned' } }, pinnedCalls), allowCache: false }
|
|
627
|
+
);
|
|
628
|
+
assert.equal(pinned.diagnostics.materializedCommit, pinnedCommit);
|
|
629
|
+
assert.equal(pinned.records[0]?.sourceTarget?.materializedCommit, pinnedCommit);
|
|
630
|
+
assert.equal(pinnedCalls.some((url) => String(url).includes('/commits/')), false, 'exact commit input must not require another commit-resolution request');
|
|
631
|
+
|
|
632
|
+
const movingRaw = 'https://raw.githubusercontent.com/owner/repo/release/.topics/a.md';
|
|
633
|
+
const branchWithoutCommit = await materializeGithubSource(
|
|
634
|
+
{ ...source, ref: 'release' },
|
|
635
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md'] },
|
|
636
|
+
{ fetchImpl: makeFetch({ [movingRaw]: { text: '# Moving branch only' } }), allowCache: false }
|
|
637
|
+
);
|
|
638
|
+
assert.equal(branchWithoutCommit.diagnostics.resolvedRef, 'release');
|
|
639
|
+
assert.equal(branchWithoutCommit.diagnostics.materializedCommit, '', 'branch load without exact commit remains immutable-provenance unavailable');
|
|
640
|
+
assert.equal(branchWithoutCommit.records[0]?.sourceTarget?.materializedCommit || '', '');
|
|
641
|
+
|
|
642
|
+
console.log('github immutable material provenance closure: PASS');
|
|
643
|
+
|
|
644
|
+
// v426 final request-budget closure: policy accounts for immutable-commit discovery before direct fetches.
|
|
645
|
+
const budgetDefaultCommit = 'dddddddddddddddddddddddddddddddddddddddd';
|
|
646
|
+
const budgetDefaultCommitApi = 'https://api.github.com/repos/owner/repo/commits/main';
|
|
647
|
+
const budgetDefaultTreeApi = `https://api.github.com/repos/owner/repo/git/trees/${budgetDefaultCommit}?recursive=1`;
|
|
648
|
+
const budgetDefaultRaw = `https://raw.githubusercontent.com/owner/repo/${budgetDefaultCommit}/.topics/a.md`;
|
|
649
|
+
const budgetDefaultMap = {
|
|
650
|
+
[repoApi]: { json: { default_branch: 'main' } },
|
|
651
|
+
[budgetDefaultCommitApi]: { json: { sha: budgetDefaultCommit } },
|
|
652
|
+
[budgetDefaultTreeApi]: { json: { truncated: false, tree: [{ type: 'blob', path: '.topics/a.md' }] } },
|
|
653
|
+
[budgetDefaultRaw]: { text: '# Budget default' }
|
|
654
|
+
};
|
|
655
|
+
const budgetDefaultBlockedCalls = [];
|
|
656
|
+
const budgetDefaultBlocked = await materializeGithubSource(
|
|
657
|
+
source,
|
|
658
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
659
|
+
{ fetchImpl: makeFetch(budgetDefaultMap, budgetDefaultBlockedCalls), maxRequestsPerOperation: 2, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
660
|
+
);
|
|
661
|
+
assert.equal(budgetDefaultBlocked.diagnostics.discoveryBlockedByPolicy, true, 'blank/default discovery needs budget 3');
|
|
662
|
+
assert.equal(budgetDefaultBlockedCalls.length, 0, 'blank/default budget block happens before direct GitHub discovery fetches');
|
|
663
|
+
assert.equal(budgetDefaultBlocked.diagnostics.transportPolicy.operation.requestedRequests, 3);
|
|
664
|
+
const budgetDefaultAllowedCalls = [];
|
|
665
|
+
const budgetDefaultAllowed = await materializeGithubSource(
|
|
666
|
+
source,
|
|
667
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
668
|
+
{ fetchImpl: makeFetch(budgetDefaultMap, budgetDefaultAllowedCalls), maxRequestsPerOperation: 3, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
669
|
+
);
|
|
670
|
+
assert.equal(budgetDefaultAllowed.records.length, 1, 'blank/default discovery is allowed at exact budget 3');
|
|
671
|
+
assert.deepEqual(budgetDefaultAllowedCalls.slice(0, 3), [repoApi, budgetDefaultCommitApi, budgetDefaultTreeApi]);
|
|
672
|
+
|
|
673
|
+
const budgetNamedCommit = 'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
|
674
|
+
const budgetNamedCommitApi = 'https://api.github.com/repos/owner/repo/commits/release';
|
|
675
|
+
const budgetNamedTreeApi = `https://api.github.com/repos/owner/repo/git/trees/${budgetNamedCommit}?recursive=1`;
|
|
676
|
+
const budgetNamedRaw = `https://raw.githubusercontent.com/owner/repo/${budgetNamedCommit}/.topics/a.md`;
|
|
677
|
+
const budgetNamedMap = {
|
|
678
|
+
[budgetNamedCommitApi]: { json: { sha: budgetNamedCommit } },
|
|
679
|
+
[budgetNamedTreeApi]: { json: { truncated: false, tree: [{ type: 'blob', path: '.topics/a.md' }] } },
|
|
680
|
+
[budgetNamedRaw]: { text: '# Budget named' }
|
|
681
|
+
};
|
|
682
|
+
const budgetNamedBlockedCalls = [];
|
|
683
|
+
const budgetNamedBlocked = await materializeGithubSource(
|
|
684
|
+
{ ...source, ref: 'release' },
|
|
685
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
686
|
+
{ fetchImpl: makeFetch(budgetNamedMap, budgetNamedBlockedCalls), maxRequestsPerOperation: 1, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
687
|
+
);
|
|
688
|
+
assert.equal(budgetNamedBlocked.diagnostics.discoveryBlockedByPolicy, true, 'named branch/tag discovery needs budget 2');
|
|
689
|
+
assert.equal(budgetNamedBlockedCalls.length, 0, 'named ref budget block happens before direct GitHub discovery fetches');
|
|
690
|
+
assert.equal(budgetNamedBlocked.diagnostics.transportPolicy.operation.requestedRequests, 2);
|
|
691
|
+
const budgetNamedAllowed = await materializeGithubSource(
|
|
692
|
+
{ ...source, ref: 'release' },
|
|
693
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
694
|
+
{ fetchImpl: makeFetch(budgetNamedMap), maxRequestsPerOperation: 2, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
695
|
+
);
|
|
696
|
+
assert.equal(budgetNamedAllowed.records.length, 1, 'named branch/tag discovery is allowed at exact budget 2');
|
|
697
|
+
|
|
698
|
+
const budgetPinnedCommit = 'ffffffffffffffffffffffffffffffffffffffff';
|
|
699
|
+
const budgetPinnedTreeApi = `https://api.github.com/repos/owner/repo/git/trees/${budgetPinnedCommit}?recursive=1`;
|
|
700
|
+
const budgetPinnedRaw = `https://raw.githubusercontent.com/owner/repo/${budgetPinnedCommit}/.topics/a.md`;
|
|
701
|
+
const budgetPinnedCalls = [];
|
|
702
|
+
const budgetPinned = await materializeGithubSource(
|
|
703
|
+
{ ...source, ref: budgetPinnedCommit },
|
|
704
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
705
|
+
{ fetchImpl: makeFetch({
|
|
706
|
+
[budgetPinnedTreeApi]: { json: { truncated: false, tree: [{ type: 'blob', path: '.topics/a.md' }] } },
|
|
707
|
+
[budgetPinnedRaw]: { text: '# Budget pinned' }
|
|
708
|
+
}, budgetPinnedCalls), maxRequestsPerOperation: 1, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
709
|
+
);
|
|
710
|
+
assert.equal(budgetPinned.records.length, 1, 'exact commit discovery is allowed at budget 1');
|
|
711
|
+
assert.equal(budgetPinnedCalls[0], budgetPinnedTreeApi, 'exact commit discovery begins directly at tree fetch');
|
|
712
|
+
assert.equal(budgetPinnedCalls.some((url) => String(url).includes('/commits/')), false, 'exact commit discovery does not spend commit-resolution request');
|
|
713
|
+
|
|
714
|
+
// v426 prequalified materializedCommit consistency closure: discovery consumes the already-qualified immutable representation.
|
|
715
|
+
const prequalifiedDiscoveryCommit = 'abababababababababababababababababababab';
|
|
716
|
+
const prequalifiedDiscoveryTreeApi = `https://api.github.com/repos/owner/repo/git/trees/${prequalifiedDiscoveryCommit}?recursive=1`;
|
|
717
|
+
const prequalifiedDiscoveryRaw = `https://raw.githubusercontent.com/owner/repo/${prequalifiedDiscoveryCommit}/.topics/a.md`;
|
|
718
|
+
const prequalifiedDiscoveryMap = {
|
|
719
|
+
[prequalifiedDiscoveryTreeApi]: { json: { truncated: false, tree: [{ type: 'blob', path: '.topics/a.md' }] } },
|
|
720
|
+
[prequalifiedDiscoveryRaw]: { text: '# Prequalified discovery' }
|
|
721
|
+
};
|
|
722
|
+
const prequalifiedNamedCalls = [];
|
|
723
|
+
const prequalifiedNamed = await materializeGithubSource(
|
|
724
|
+
{ ...source, ref: 'main', materializedCommit: prequalifiedDiscoveryCommit },
|
|
725
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
726
|
+
{ fetchImpl: makeFetch(prequalifiedDiscoveryMap, prequalifiedNamedCalls), maxRequestsPerOperation: 1, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
727
|
+
);
|
|
728
|
+
assert.equal(prequalifiedNamed.records.length, 1, 'prequalified named-ref discovery is allowed at tree-only budget 1');
|
|
729
|
+
assert.deepEqual(prequalifiedNamedCalls, [prequalifiedDiscoveryTreeApi, prequalifiedDiscoveryRaw], 'prequalified named-ref discovery scans and loads one coherent immutable representation');
|
|
730
|
+
assert.equal(prequalifiedNamedCalls.some((url) => String(url).includes('/commits/')), false, 'prequalified named-ref discovery spends no commit-resolution request');
|
|
731
|
+
assert.equal(prequalifiedNamed.records[0]?.sourceTarget?.materializedCommit, prequalifiedDiscoveryCommit, 'prequalified named-ref raw record retains the same immutable representation');
|
|
732
|
+
assert.equal(prequalifiedNamed.diagnostics.resolvedRef, 'main', 'configured named ref remains configured-source truth');
|
|
733
|
+
|
|
734
|
+
const prequalifiedBlankCalls = [];
|
|
735
|
+
const prequalifiedBlank = await materializeGithubSource(
|
|
736
|
+
{ ...source, ref: '', materializedCommit: prequalifiedDiscoveryCommit },
|
|
737
|
+
{ repoDiscovery: true, fileRefs: [] },
|
|
738
|
+
{ fetchImpl: makeFetch(prequalifiedDiscoveryMap, prequalifiedBlankCalls), maxRequestsPerOperation: 1, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
739
|
+
);
|
|
740
|
+
assert.equal(prequalifiedBlank.records.length, 1, 'prequalified blank-ref discovery is allowed at tree-only budget 1');
|
|
741
|
+
assert.deepEqual(prequalifiedBlankCalls, [prequalifiedDiscoveryTreeApi, prequalifiedDiscoveryRaw], 'prequalified blank-ref discovery performs no default-branch or commit-resolution fetch');
|
|
742
|
+
assert.equal(prequalifiedBlank.diagnostics.resolvedRef, '', 'prequalified blank configured ref is not rewritten to pretend the commit was configured ref');
|
|
743
|
+
assert.equal(prequalifiedBlank.records[0]?.sourceTarget?.materializedCommit, prequalifiedDiscoveryCommit, 'prequalified blank-ref raw record retains immutable representation');
|
|
744
|
+
|
|
745
|
+
console.log('github discovery request-budget closure: PASS');
|
|
746
|
+
console.log('github prequalified repo-discovery consistency closure: PASS');
|
|
747
|
+
|
|
748
|
+
// v426 final raw-file request-budget closure: explicit loads account for ref/commit resolution before fetch.
|
|
749
|
+
const rawBudgetDefaultCommit = '1212121212121212121212121212121212121212';
|
|
750
|
+
const rawBudgetDefaultCommitApi = 'https://api.github.com/repos/owner/repo/commits/main';
|
|
751
|
+
const rawBudgetDefaultUrl = `https://raw.githubusercontent.com/owner/repo/${rawBudgetDefaultCommit}/.topics/a.md`;
|
|
752
|
+
const rawBudgetDefaultMap = {
|
|
753
|
+
[repoApi]: { json: { default_branch: 'main' } },
|
|
754
|
+
[rawBudgetDefaultCommitApi]: { json: { sha: rawBudgetDefaultCommit } },
|
|
755
|
+
[rawBudgetDefaultUrl]: { text: '# Raw budget default' }
|
|
756
|
+
};
|
|
757
|
+
const rawBudgetDefaultBlockedCalls = [];
|
|
758
|
+
const rawBudgetDefaultBlocked = await materializeGithubSource(
|
|
759
|
+
source,
|
|
760
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md'] },
|
|
761
|
+
{ fetchImpl: makeFetch(rawBudgetDefaultMap, rawBudgetDefaultBlockedCalls), maxRequestsPerOperation: 2, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
762
|
+
);
|
|
763
|
+
assert.equal(rawBudgetDefaultBlocked.records.length, 0, 'blank/default raw load needs budget 3');
|
|
764
|
+
assert.equal(rawBudgetDefaultBlockedCalls.length, 0, 'blank/default raw budget block happens before direct fetches');
|
|
765
|
+
assert.equal(rawBudgetDefaultBlocked.diagnostics.transportPolicy.operation.requestedRequests, 3);
|
|
766
|
+
const rawBudgetDefaultAllowedCalls = [];
|
|
767
|
+
const rawBudgetDefaultAllowed = await materializeGithubSource(
|
|
768
|
+
source,
|
|
769
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md'] },
|
|
770
|
+
{ fetchImpl: makeFetch(rawBudgetDefaultMap, rawBudgetDefaultAllowedCalls), maxRequestsPerOperation: 3, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
771
|
+
);
|
|
772
|
+
assert.equal(rawBudgetDefaultAllowed.records.length, 1, 'blank/default raw load is allowed at exact budget 3');
|
|
773
|
+
assert.deepEqual(rawBudgetDefaultAllowedCalls, [repoApi, rawBudgetDefaultCommitApi, rawBudgetDefaultUrl]);
|
|
774
|
+
assert.equal(rawBudgetDefaultAllowed.records[0]?.sourceTarget?.materializedCommit, rawBudgetDefaultCommit);
|
|
775
|
+
|
|
776
|
+
const rawBudgetNamedCommit = '3434343434343434343434343434343434343434';
|
|
777
|
+
const rawBudgetNamedCommitApi = 'https://api.github.com/repos/owner/repo/commits/release';
|
|
778
|
+
const rawBudgetNamedUrl = `https://raw.githubusercontent.com/owner/repo/${rawBudgetNamedCommit}/.topics/a.md`;
|
|
779
|
+
const rawBudgetNamedMap = {
|
|
780
|
+
[rawBudgetNamedCommitApi]: { json: { sha: rawBudgetNamedCommit } },
|
|
781
|
+
[rawBudgetNamedUrl]: { text: '# Raw budget named' }
|
|
782
|
+
};
|
|
783
|
+
const rawBudgetNamedBlockedCalls = [];
|
|
784
|
+
const rawBudgetNamedBlocked = await materializeGithubSource(
|
|
785
|
+
{ ...source, ref: 'release' },
|
|
786
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md'] },
|
|
787
|
+
{ fetchImpl: makeFetch(rawBudgetNamedMap, rawBudgetNamedBlockedCalls), maxRequestsPerOperation: 1, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
788
|
+
);
|
|
789
|
+
assert.equal(rawBudgetNamedBlocked.records.length, 0, 'named raw load needs budget 2');
|
|
790
|
+
assert.equal(rawBudgetNamedBlockedCalls.length, 0, 'named raw budget block happens before direct fetches');
|
|
791
|
+
assert.equal(rawBudgetNamedBlocked.diagnostics.transportPolicy.operation.requestedRequests, 2);
|
|
792
|
+
const rawBudgetNamedAllowedCalls = [];
|
|
793
|
+
const rawBudgetNamedAllowed = await materializeGithubSource(
|
|
794
|
+
{ ...source, ref: 'release' },
|
|
795
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md'] },
|
|
796
|
+
{ fetchImpl: makeFetch(rawBudgetNamedMap, rawBudgetNamedAllowedCalls), maxRequestsPerOperation: 2, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
797
|
+
);
|
|
798
|
+
assert.equal(rawBudgetNamedAllowed.records.length, 1, 'named raw load is allowed at exact budget 2');
|
|
799
|
+
assert.deepEqual(rawBudgetNamedAllowedCalls, [rawBudgetNamedCommitApi, rawBudgetNamedUrl]);
|
|
800
|
+
|
|
801
|
+
const rawBudgetPinnedCommit = '5656565656565656565656565656565656565656';
|
|
802
|
+
const rawBudgetPinnedUrl = `https://raw.githubusercontent.com/owner/repo/${rawBudgetPinnedCommit}/.topics/a.md`;
|
|
803
|
+
const rawBudgetPinnedCalls = [];
|
|
804
|
+
const rawBudgetPinned = await materializeGithubSource(
|
|
805
|
+
{ ...source, ref: rawBudgetPinnedCommit },
|
|
806
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md'] },
|
|
807
|
+
{ fetchImpl: makeFetch({ [rawBudgetPinnedUrl]: { text: '# Raw budget pinned' } }, rawBudgetPinnedCalls), maxRequestsPerOperation: 1, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
808
|
+
);
|
|
809
|
+
assert.equal(rawBudgetPinned.records.length, 1, 'exact-commit raw load is allowed at budget 1');
|
|
810
|
+
assert.deepEqual(rawBudgetPinnedCalls, [rawBudgetPinnedUrl]);
|
|
811
|
+
|
|
812
|
+
const rawBudgetMultiSecond = `https://raw.githubusercontent.com/owner/repo/${rawBudgetDefaultCommit}/.topics/nested/b.trace.md`;
|
|
813
|
+
const rawBudgetMultiMap = { ...rawBudgetDefaultMap, [rawBudgetMultiSecond]: { text: '# Raw budget second' } };
|
|
814
|
+
const rawBudgetMultiBlockedCalls = [];
|
|
815
|
+
const rawBudgetMultiBlocked = await materializeGithubSource(
|
|
816
|
+
source,
|
|
817
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md', '.topics/nested/b.trace.md'] },
|
|
818
|
+
{ fetchImpl: makeFetch(rawBudgetMultiMap, rawBudgetMultiBlockedCalls), maxRequestsPerOperation: 3, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
819
|
+
);
|
|
820
|
+
assert.equal(rawBudgetMultiBlocked.records.length, 0, 'two raw targets plus default-ref overhead need budget 4');
|
|
821
|
+
assert.equal(rawBudgetMultiBlockedCalls.length, 0, 'multi-target budget block happens before direct fetches');
|
|
822
|
+
assert.equal(rawBudgetMultiBlocked.diagnostics.transportPolicy.operation.requestedRequests, 4);
|
|
823
|
+
const rawBudgetMultiAllowedCalls = [];
|
|
824
|
+
const rawBudgetMultiAllowed = await materializeGithubSource(
|
|
825
|
+
source,
|
|
826
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md', '.topics/nested/b.trace.md'] },
|
|
827
|
+
{ fetchImpl: makeFetch(rawBudgetMultiMap, rawBudgetMultiAllowedCalls), maxRequestsPerOperation: 4, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
828
|
+
);
|
|
829
|
+
assert.equal(rawBudgetMultiAllowed.records.length, 2, 'two raw targets are allowed at overhead + N budget');
|
|
830
|
+
assert.equal(rawBudgetMultiAllowedCalls.length, 4);
|
|
831
|
+
|
|
832
|
+
const rawBudgetPrequalifiedCommit = '7878787878787878787878787878787878787878';
|
|
833
|
+
const rawBudgetPrequalifiedUrl = `https://raw.githubusercontent.com/owner/repo/${rawBudgetPrequalifiedCommit}/.topics/a.md`;
|
|
834
|
+
const rawBudgetPrequalifiedCalls = [];
|
|
835
|
+
const rawBudgetPrequalified = await materializeGithubSource(
|
|
836
|
+
{ ...source, ref: 'main', materializedCommit: rawBudgetPrequalifiedCommit },
|
|
837
|
+
{ repoDiscovery: false, fileRefs: ['.topics/a.md'] },
|
|
838
|
+
{ fetchImpl: makeFetch({ [rawBudgetPrequalifiedUrl]: { text: '# Raw budget prequalified' } }, rawBudgetPrequalifiedCalls), maxRequestsPerOperation: 1, allowCache: false, preferredTransports: ['direct'], transportOrderExact: true }
|
|
839
|
+
);
|
|
840
|
+
assert.equal(rawBudgetPrequalified.records.length, 1, 'prequalified materialized commit pays only raw target cost');
|
|
841
|
+
assert.deepEqual(rawBudgetPrequalifiedCalls, [rawBudgetPrequalifiedUrl]);
|
|
842
|
+
|
|
843
|
+
console.log('github raw-file request-budget closure: PASS');
|